From 123639fc43937569e8c921f9c517adcc7817a548 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 4 Jul 2026 15:32:58 +0800 Subject: [PATCH 01/85] feat: add GameInstanceID and GameInstanceManifest records, and ImmutableSequencedMap class --- .../jackhuang/hmcl/game/GameInstanceID.java | 29 +++ .../hmcl/game/GameInstanceManifest.java | 27 +++ .../hmcl/util/ImmutableSequencedMap.java | 187 ++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java new file mode 100644 index 0000000000..05159e1df1 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java @@ -0,0 +1,29 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import org.jetbrains.annotations.NotNullByDefault; + +@NotNullByDefault +public record GameInstanceID(String id) { + public GameInstanceID { + if (id.isEmpty()) { + throw new IllegalArgumentException("Game instance id cannot be empty"); + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java new file mode 100644 index 0000000000..3238d4d626 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -0,0 +1,27 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import org.jetbrains.annotations.NotNullByDefault; + +@NotNullByDefault +public record GameInstanceManifest( + +) { + +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java new file mode 100644 index 0000000000..2463ec7ae5 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java @@ -0,0 +1,187 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.util; + +import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.*; +import java.util.function.BiConsumer; + +/// An unmodifiable [Map] that preserves insertion order. +/// +/// This class wrap a [LinkedHashMap] and exposes only read operations; +/// any attempt to modify the map through the standard [Map] mutator methods +/// throws [UnsupportedOperationException]. +/// The iteration order of keys, values, and entries is always the order in which +/// they were originally inserted. +/// +/// Use the static factory methods [`of()`](#of()) and [`copyOf(Map)`](#copyOf(java.util.Map)) +/// to obtain instances. +/// +/// @param the type of keys +/// @param the type of values +@NotNullByDefault +public final class ImmutableSequencedMap + implements Map { + + /// Shared empty instance. + private static final ImmutableSequencedMap EMPTY = new ImmutableSequencedMap<>(new LinkedHashMap<>()); + + /// Returns the empty immutable sequenced map. + /// + /// @param the type of keys + /// @param the type of values + /// @return an empty [ImmutableSequencedMap] + @SuppressWarnings("unchecked") + public static ImmutableSequencedMap of() { + return (ImmutableSequencedMap) EMPTY; + } + + /// Returns an immutable sequenced map containing the entries of the given map + /// in their original iteration order. + /// + /// The entries are copied into an internal [LinkedHashMap], + /// so subsequent changes to the source map do not affect the returned map. + /// + /// @param the type of keys + /// @param the type of values + /// @param map the source map whose entries are copied + /// @return an [ImmutableSequencedMap] with the same entries and order as {@code map} + public static ImmutableSequencedMap copyOf(Map map) { + if (map.isEmpty()) { + return of(); + } else { + return new ImmutableSequencedMap<>(new LinkedHashMap<>(map)); + } + } + + /// Wraps the given [LinkedHashMap] directly without copying. + /// + /// The caller **must not** modify the map after passing it to this method; + /// the resulting [ImmutableSequencedMap] does not + /// make a defensive copy. + /// + /// @param the type of keys + /// @param the type of values + /// @param map the linked hash map to wrap + /// @return an [ImmutableSequencedMap] backed by {@code map} + public static ImmutableSequencedMap wrap(@Unmodifiable LinkedHashMap map) { + return new ImmutableSequencedMap<>(map); + } + + /// The backing map that stores the entries in insertion order. + private final LinkedHashMap map; + + private ImmutableSequencedMap(LinkedHashMap map) { + this.map = map; + } + + /// {@inheritDoc} + @Override + public int size() { + return map.size(); + } + + /// {@inheritDoc} + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + /// {@inheritDoc} + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + /// {@inheritDoc} + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + /// {@inheritDoc} + @Override + public V get(Object key) { + return map.get(key); + } + + /// Always throws [UnsupportedOperationException] because this map is unmodifiable. + /// + /// @throws UnsupportedOperationException always + @Override + public @Nullable V put(K key, V value) { + throw new UnsupportedOperationException(); + } + + /// Always throws [UnsupportedOperationException] because this map is unmodifiable. + /// + /// @throws UnsupportedOperationException always + @Override + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + /// Always throws [UnsupportedOperationException] because this map is unmodifiable. + /// + /// @throws UnsupportedOperationException always + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException(); + } + + /// Always throws [UnsupportedOperationException] because this map is unmodifiable. + /// + /// @throws UnsupportedOperationException always + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + /// Returns an unmodifiable view of the keys in insertion order. + /// + /// @return an unmodifiable set of keys + @Override + public Set keySet() { + return Collections.unmodifiableSet(map.keySet()); + } + + /// Returns an unmodifiable view of the values in insertion order. + /// + /// @return an unmodifiable collection of values + @Override + public Collection values() { + return Collections.unmodifiableCollection(map.values()); + } + + /// Returns an unmodifiable view of the entries in insertion order. + /// + /// @return an unmodifiable set of entries + @Override + public Set> entrySet() { + return Collections.unmodifiableSet(map.entrySet()); + } + + @Override + public void forEach(BiConsumer action) { + map.forEach(action); + } +} From 1ac9ec3ca96e57ae5959bdddf105e312d834664b Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 4 Jul 2026 15:39:32 +0800 Subject: [PATCH 02/85] feat: enhance GameInstanceManifest with additional nullable fields for improved configuration --- .../hmcl/game/GameInstanceManifest.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 3238d4d626..9fa7a31a98 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -18,10 +18,35 @@ package org.jackhuang.hmcl.game; import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.Map; @NotNullByDefault public record GameInstanceManifest( - + String id, + @Nullable String minecraftArguments, + @Nullable Arguments arguments, + @Nullable String mainClass, + @Nullable String inheritsFrom, + @Nullable String jar, + @Nullable AssetIndexInfo assetIndex, + @Nullable String assets, + @Nullable Integer complianceLevel, + @Nullable GameJavaVersion javaVersion, + @Nullable List libraries, + @Nullable List compatibilityRules, + @Nullable @Unmodifiable Map downloads, + @Nullable @Unmodifiable Map logging, + @Nullable ReleaseType type, + @Nullable String time, + @Nullable String releaseTime, + @Nullable Integer minimumLauncherVersion, + @Nullable Boolean root, + @Nullable Boolean hidden, + @Nullable @Unmodifiable List patches ) { } From d0da5a7863808190d0a041a4c81559995f93083b Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 4 Jul 2026 15:40:28 +0800 Subject: [PATCH 03/85] feat: mark libraries and compatibilityRules as unmodifiable for enhanced safety --- .../java/org/jackhuang/hmcl/game/GameInstanceManifest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 9fa7a31a98..be8bb9889c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -36,8 +36,8 @@ public record GameInstanceManifest( @Nullable String assets, @Nullable Integer complianceLevel, @Nullable GameJavaVersion javaVersion, - @Nullable List libraries, - @Nullable List compatibilityRules, + @Nullable @Unmodifiable List libraries, + @Nullable @Unmodifiable List compatibilityRules, @Nullable @Unmodifiable Map downloads, @Nullable @Unmodifiable Map logging, @Nullable ReleaseType type, From 9787165b44106fa78a8cac4ea8268db9166d30bf Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 4 Jul 2026 15:47:08 +0800 Subject: [PATCH 04/85] feat: introduce GameInstancePatch record and update GameInstanceManifest for enhanced structure --- .../hmcl/game/GameInstanceManifest.java | 6 ++- .../hmcl/game/GameInstancePatch.java | 53 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index be8bb9889c..bb393cb719 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.game; +import com.google.gson.JsonElement; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; @@ -26,7 +27,7 @@ @NotNullByDefault public record GameInstanceManifest( - String id, + GameInstanceID id, @Nullable String minecraftArguments, @Nullable Arguments arguments, @Nullable String mainClass, @@ -46,7 +47,8 @@ public record GameInstanceManifest( @Nullable Integer minimumLauncherVersion, @Nullable Boolean root, @Nullable Boolean hidden, - @Nullable @Unmodifiable List patches + @Nullable @Unmodifiable List patches, + @Unmodifiable Map unknownFields ) { } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java new file mode 100644 index 0000000000..8110668cc5 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -0,0 +1,53 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import com.google.gson.JsonElement; +import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.Map; + +@NotNullByDefault +public record GameInstancePatch( + @Nullable GameInstanceID id, + @Nullable String minecraftArguments, + @Nullable Arguments arguments, + @Nullable String mainClass, + @Nullable String inheritsFrom, + @Nullable String jar, + @Nullable AssetIndexInfo assetIndex, + @Nullable String assets, + @Nullable Integer complianceLevel, + @Nullable GameJavaVersion javaVersion, + @Nullable @Unmodifiable List libraries, + @Nullable @Unmodifiable List compatibilityRules, + @Nullable @Unmodifiable Map downloads, + @Nullable @Unmodifiable Map logging, + @Nullable ReleaseType type, + @Nullable String time, + @Nullable String releaseTime, + @Nullable Integer minimumLauncherVersion, + @Nullable Boolean root, + @Nullable Boolean hidden, + @Nullable @Unmodifiable List patches, + @Unmodifiable Map unknownFields +) { +} From 78667c277941e3581c635541db64fcab50cccf64 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 4 Jul 2026 17:51:15 +0800 Subject: [PATCH 05/85] feat: implement GameRepository2 interface and DefaultGameRepository2 class for game instance management --- .../hmcl/game/DefaultGameRepository2.java | 201 ++++++++++++++ .../jackhuang/hmcl/game/GameInstanceID.java | 12 +- .../hmcl/game/GameInstanceManifest.java | 250 +++++++++++++++++- .../jackhuang/hmcl/game/GameRepository2.java | 37 +++ .../game/NoSuchGameInstanceException.java | 34 +++ .../game/ResolvedGameInstanceManifest.java | 51 ++++ 6 files changed, 581 insertions(+), 4 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/NoSuchGameInstanceException.java create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/ResolvedGameInstanceManifest.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java new file mode 100644 index 0000000000..4d716f8177 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -0,0 +1,201 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.versioning.GameVersionNumber; +import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Stream; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +@NotNullByDefault +public class DefaultGameRepository2 implements GameRepository2 { + + private static final GameInstanceManifest CLASSIC_MANIFEST = new GameInstanceManifest( + new GameInstanceID("Classic"), + "${auth_player_name} ${auth_session} --workDir ${game_directory}", + null, + "net.minecraft.client.Minecraft", + null, + null, + null, + null, + null, + null, + List.of( + classicLibrary("lwjgl"), + classicLibrary("jinput"), + classicLibrary("lwjgl_util")), + null, + null, + null, + ReleaseType.UNKNOWN, + null, + null, + 0, + null, + null, + null, + null + ); + + private static Library classicLibrary(String name) { + return new Library(new Artifact("", "", ""), null, + new LibrariesDownloadInfo(new LibraryDownloadInfo("bin/" + name + ".jar"), null), + null, null, null, null, null, null); + } + + private Path baseDirectory; + private volatile @Unmodifiable Snapshot snapshot; + + public DefaultGameRepository2(Path baseDirectory) { + this.baseDirectory = baseDirectory; + this.snapshot = new Snapshot(baseDirectory); + } + + + public Path getBaseDirectory() { + return baseDirectory; + } + + private static boolean hasClassicVersion(Path baseDirectory) { + Path bin = baseDirectory.resolve("bin"); + return Files.isDirectory(bin) + && Files.exists(bin.resolve("lwjgl.jar")) + && Files.exists(bin.resolve("jinput.jar")) + && Files.exists(bin.resolve("lwjgl_util.jar")); + } + + public GameInstanceManifest readInstanceManifest(Path file) throws IOException, JsonParseException { + JsonObject json = JsonUtils.fromJsonFile(file, JsonObject.class); + try { + // Try TLauncher version json format + // return JsonUtils.GSON.fromJson(json, TLauncherVersion.class).toVersion(); + throw new UnsupportedOperationException("TODO"); + } catch (JsonParseException ignored) { + } + + try { + // Try official version json format + return JsonUtils.GSON.fromJson(json, GameInstanceManifest.class); // TODO + } catch (JsonParseException ignored) { + } + + LOG.warning("Cannot parse game instance manifest json: " + file + "\n" + json); + throw new JsonParseException("Game instance manifest incorrect"); + } + + @Override + public void refresh() { + Snapshot newSnapshot = new Snapshot(baseDirectory); + + if (hasClassicVersion(newSnapshot.baseDirectory)) { + GameInstanceID id = CLASSIC_MANIFEST.id(); + newSnapshot.instances.put(id, new InstanceHolder(newSnapshot, id, CLASSIC_MANIFEST)); + } + + Path versionsDir = newSnapshot.baseDirectory.resolve("versions"); + if (Files.isDirectory(versionsDir)) { + try (Stream stream = Files.list(versionsDir)) { + stream.parallel().filter(Files::isDirectory).flatMap(dir -> { + GameInstanceID id = new GameInstanceID(FileUtils.getName(dir)); + Path json = dir.resolve(id + ".json"); + + if (Files.notExists(json)) { + return Stream.empty(); + } + + GameInstanceManifest manifest; + try { + manifest = readInstanceManifest(json); + } catch (Exception e) { + LOG.warning("Malformed version json " + id, e); + return Stream.empty(); + } + + if (!id.equals(manifest.id())) { + // TODO + return Stream.empty(); + } + + return Stream.of(manifest); + }).forEachOrdered(it -> newSnapshot.instances.put( + it.id(), + new InstanceHolder(newSnapshot, it.id(), it))); + } catch (IOException e) { + LOG.warning("Failed to load versions from " + versionsDir, e); + } + } + } + + @Override + public GameVersionNumber getGameVersion(GameInstanceID instanceId) throws NoSuchGameInstanceException { + InstanceHolder instance = snapshot.instances.get(instanceId); + if (instance == null) { + throw new NoSuchGameInstanceException(instanceId); + } + + throw new UnsupportedOperationException("TODO"); + } + + @Override + public Path getInstanceRoot(GameInstanceID instanceId) { + return getBaseDirectory().resolve("versions").resolve(instanceId.id()); + } + + @Override + public ResolvedGameInstanceManifest resolve(GameInstanceManifest manifest) { + throw new UnsupportedOperationException("TODO"); + } + + protected static class Snapshot { + private final Path baseDirectory; + private final Map instances = new TreeMap<>(); + + protected Snapshot(Path baseDirectory) { + this.baseDirectory = baseDirectory; + } + } + + protected static class InstanceHolder { + protected final Snapshot snapshot; + protected final GameInstanceID id; + protected final GameInstanceManifest manifest; + + protected @Nullable GameVersionNumber version; + + protected InstanceHolder(Snapshot snapshot, GameInstanceID id, GameInstanceManifest manifest) { + this.snapshot = snapshot; + this.id = id; + this.manifest = manifest; + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java index 05159e1df1..c4dc6b5ff5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java @@ -20,10 +20,20 @@ import org.jetbrains.annotations.NotNullByDefault; @NotNullByDefault -public record GameInstanceID(String id) { +public record GameInstanceID(String id) implements Comparable { public GameInstanceID { if (id.isEmpty()) { throw new IllegalArgumentException("Game instance id cannot be empty"); } } + + @Override + public int compareTo(GameInstanceID that) { + return this.id.compareTo(that.id); + } + + @Override + public String toString() { + return id; + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index bb393cb719..163d148528 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -17,13 +17,17 @@ */ package org.jackhuang.hmcl.game; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -import java.util.List; -import java.util.Map; +import java.util.*; @NotNullByDefault public record GameInstanceManifest( @@ -48,7 +52,247 @@ public record GameInstanceManifest( @Nullable Boolean root, @Nullable Boolean hidden, @Nullable @Unmodifiable List patches, - @Unmodifiable Map unknownFields + @Nullable @Unmodifiable JsonObject rawJson ) { + public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) throws JsonParseException { + if (copyJson) { + json = json.deepCopy(); + } + + Editor editor = new Editor(); + + for (Map.Entry entry : json.entrySet()) { + String memberName = entry.getKey(); + JsonElement value = entry.getValue(); + + switch (memberName) { + case "id" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + try { + editor.id = new GameInstanceID(primitive.getAsString()); + } catch (IllegalArgumentException e) { + throw new JsonParseException(e); + } + } + } + case "minecraftArguments" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + editor.minecraftArguments = primitive.getAsString(); + } + } + case "arguments" -> { + editor.arguments = JsonUtils.GSON.fromJson(value, Arguments.class); + } + case "mainClass" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + editor.mainClass = primitive.getAsString(); + } + } + case "inheritsFrom" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + editor.inheritsFrom = primitive.getAsString(); + } + } + case "jar" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + editor.jar = primitive.getAsString(); + } + } + case "assetIndex" -> { + editor.assetIndex = JsonUtils.GSON.fromJson(value, AssetIndexInfo.class); + } + case "assets" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + editor.assets = primitive.getAsString(); + } + } + case "complianceLevel" -> { + if (value instanceof JsonPrimitive primitive && primitive.isNumber()) { + editor.complianceLevel = primitive.getAsInt(); + } + } + case "javaVersion" -> { + editor.javaVersion = JsonUtils.GSON.fromJson(value, GameJavaVersion.class); + } + case "libraries" -> { + if (value instanceof JsonArray array) { + List list = new ArrayList<>(array.size()); + for (JsonElement element : array) { + list.add(JsonUtils.GSON.fromJson(element, Library.class)); + } + editor.libraries = Collections.unmodifiableList(list); + } + } + case "compatibilityRules" -> { + if (value instanceof JsonArray array) { + List list = new ArrayList<>(array.size()); + for (JsonElement element : array) { + list.add(JsonUtils.GSON.fromJson(element, CompatibilityRule.class)); + } + editor.compatibilityRules = Collections.unmodifiableList(list); + } + } + case "downloads" -> { + if (value instanceof JsonObject obj) { + Map map = new EnumMap<>(DownloadType.class); + for (Map.Entry downloadEntry : obj.entrySet()) { + try { + DownloadType type = DownloadType.valueOf(downloadEntry.getKey()); + map.put(type, JsonUtils.GSON.fromJson(downloadEntry.getValue(), DownloadInfo.class)); + } catch (IllegalArgumentException ignored) { + } + } + editor.downloads = Collections.unmodifiableMap(map); + } + } + case "logging" -> { + if (value instanceof JsonObject obj) { + Map map = new EnumMap<>(DownloadType.class); + for (Map.Entry loggingEntry : obj.entrySet()) { + try { + DownloadType type = DownloadType.valueOf(loggingEntry.getKey()); + map.put(type, JsonUtils.GSON.fromJson(loggingEntry.getValue(), LoggingInfo.class)); + } catch (IllegalArgumentException ignored) { + } + } + editor.logging = Map.copyOf(map); + } + } + case "type" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + try { + editor.type = ReleaseType.valueOf(primitive.getAsString()); + } catch (IllegalArgumentException ignored) { + } + } + } + case "time" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + editor.time = primitive.getAsString(); + } + } + case "releaseTime" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + editor.releaseTime = primitive.getAsString(); + } + } + case "minimumLauncherVersion" -> { + if (value instanceof JsonPrimitive primitive && primitive.isNumber()) { + editor.minimumLauncherVersion = primitive.getAsInt(); + } + } + case "root" -> { + if (value instanceof JsonPrimitive primitive && primitive.isBoolean()) { + editor.root = primitive.getAsBoolean(); + } + } + case "hidden" -> { + if (value instanceof JsonPrimitive primitive && primitive.isBoolean()) { + editor.hidden = primitive.getAsBoolean(); + } + } + case "patches" -> { + if (value instanceof JsonArray array) { + List list = new ArrayList<>(array.size()); + for (JsonElement element : array) { + // TODO + } + editor.patches = List.copyOf(list); + } + } + } + } + + editor.rawJson = json; + return editor.toManifest(); + } + + + private static final class Editor { + // @formatter:off + private @Nullable GameInstanceID id; + private @Nullable String minecraftArguments; + private @Nullable Arguments arguments; + private @Nullable String mainClass; + private @Nullable String inheritsFrom; + private @Nullable String jar; + private @Nullable AssetIndexInfo assetIndex; + private @Nullable String assets; + private @Nullable Integer complianceLevel; + private @Nullable GameJavaVersion javaVersion; + private @Nullable @Unmodifiable List libraries; + private @Nullable @Unmodifiable List compatibilityRules; + private @Nullable @Unmodifiable Map downloads; + private @Nullable @Unmodifiable Map logging; + private @Nullable ReleaseType type; + private @Nullable String time; + private @Nullable String releaseTime; + private @Nullable Integer minimumLauncherVersion; + private @Nullable Boolean root; + private @Nullable Boolean hidden; + private @Nullable @Unmodifiable List patches; + private @Nullable @Unmodifiable JsonObject rawJson; + // @formatter:on + + + Editor() { + } + + Editor(GameInstanceManifest manifest) { + this.id = manifest.id; + this.minecraftArguments = manifest.minecraftArguments; + this.arguments = manifest.arguments; + this.mainClass = manifest.mainClass; + this.inheritsFrom = manifest.inheritsFrom; + this.jar = manifest.jar; + this.assetIndex = manifest.assetIndex; + this.assets = manifest.assets; + this.complianceLevel = manifest.complianceLevel; + this.javaVersion = manifest.javaVersion; + this.libraries = manifest.libraries; + this.compatibilityRules = manifest.compatibilityRules; + this.downloads = manifest.downloads; + this.logging = manifest.logging; + this.type = manifest.type; + this.time = manifest.time; + this.releaseTime = manifest.releaseTime; + this.minimumLauncherVersion = manifest.minimumLauncherVersion; + this.root = manifest.root; + this.hidden = manifest.hidden; + this.patches = manifest.patches; + this.rawJson = manifest.rawJson != null ? manifest.rawJson.deepCopy() : null; + } + + GameInstanceManifest toManifest() { + if (id == null) { + throw new IllegalStateException("id is null"); + } + + return new GameInstanceManifest( + id, + minecraftArguments, + arguments, + mainClass, + inheritsFrom, + jar, + assetIndex, + assets, + complianceLevel, + javaVersion, + libraries, + compatibilityRules, + downloads, + logging, + type, + time, + releaseTime, + minimumLauncherVersion, + root, + hidden, + patches, + rawJson + ); + } + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java new file mode 100644 index 0000000000..f3dadb60c2 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java @@ -0,0 +1,37 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import org.jackhuang.hmcl.util.versioning.GameVersionNumber; +import org.jetbrains.annotations.NotNullByDefault; + +import java.nio.file.Path; + +@NotNullByDefault +public interface GameRepository2 { + ResolvedGameInstanceManifest resolve(GameInstanceManifest manifest); + + void refresh(); + + GameVersionNumber getGameVersion(GameInstanceID instanceId) throws NoSuchGameInstanceException; + + Path getInstanceRoot(GameInstanceID instanceId); + + + +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/NoSuchGameInstanceException.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/NoSuchGameInstanceException.java new file mode 100644 index 0000000000..090ed8c957 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/NoSuchGameInstanceException.java @@ -0,0 +1,34 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import org.jetbrains.annotations.NotNullByDefault; + +@NotNullByDefault +public class NoSuchGameInstanceException extends Exception { + private final GameInstanceID id; + + public NoSuchGameInstanceException(GameInstanceID id) { + super(id.id()); + this.id = id; + } + + public GameInstanceID getId() { + return id; + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ResolvedGameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ResolvedGameInstanceManifest.java new file mode 100644 index 0000000000..fe622c66a5 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ResolvedGameInstanceManifest.java @@ -0,0 +1,51 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import com.google.gson.JsonElement; +import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.List; +import java.util.Map; + +@NotNullByDefault +public record ResolvedGameInstanceManifest( + GameInstanceID id, + @Nullable String minecraftArguments, + @Nullable Arguments arguments, + @Nullable String mainClass, + @Nullable String jar, + @Nullable AssetIndexInfo assetIndex, + @Nullable String assets, + @Nullable Integer complianceLevel, + @Nullable GameJavaVersion javaVersion, + @Nullable @Unmodifiable List libraries, + @Nullable @Unmodifiable List compatibilityRules, + @Nullable @Unmodifiable Map downloads, + @Nullable @Unmodifiable Map logging, + @Nullable ReleaseType type, + @Nullable String time, + @Nullable String releaseTime, + @Nullable Integer minimumLauncherVersion, + @Nullable Boolean root, + @Nullable Boolean hidden +) { + +} From a063e4a07423bc2322f27076e429931e2c53fa11 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 4 Jul 2026 17:53:31 +0800 Subject: [PATCH 06/85] feat: refactor GameInstanceManifest to use Builder pattern for improved instantiation --- .../hmcl/game/GameInstanceManifest.java | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 163d148528..cd6e018a24 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -60,7 +60,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t json = json.deepCopy(); } - Editor editor = new Editor(); + Builder builder = new Builder(); for (Map.Entry entry : json.entrySet()) { String memberName = entry.getKey(); @@ -70,7 +70,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t case "id" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { try { - editor.id = new GameInstanceID(primitive.getAsString()); + builder.id = new GameInstanceID(primitive.getAsString()); } catch (IllegalArgumentException e) { throw new JsonParseException(e); } @@ -78,42 +78,42 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } case "minecraftArguments" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - editor.minecraftArguments = primitive.getAsString(); + builder.minecraftArguments = primitive.getAsString(); } } case "arguments" -> { - editor.arguments = JsonUtils.GSON.fromJson(value, Arguments.class); + builder.arguments = JsonUtils.GSON.fromJson(value, Arguments.class); } case "mainClass" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - editor.mainClass = primitive.getAsString(); + builder.mainClass = primitive.getAsString(); } } case "inheritsFrom" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - editor.inheritsFrom = primitive.getAsString(); + builder.inheritsFrom = primitive.getAsString(); } } case "jar" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - editor.jar = primitive.getAsString(); + builder.jar = primitive.getAsString(); } } case "assetIndex" -> { - editor.assetIndex = JsonUtils.GSON.fromJson(value, AssetIndexInfo.class); + builder.assetIndex = JsonUtils.GSON.fromJson(value, AssetIndexInfo.class); } case "assets" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - editor.assets = primitive.getAsString(); + builder.assets = primitive.getAsString(); } } case "complianceLevel" -> { if (value instanceof JsonPrimitive primitive && primitive.isNumber()) { - editor.complianceLevel = primitive.getAsInt(); + builder.complianceLevel = primitive.getAsInt(); } } case "javaVersion" -> { - editor.javaVersion = JsonUtils.GSON.fromJson(value, GameJavaVersion.class); + builder.javaVersion = JsonUtils.GSON.fromJson(value, GameJavaVersion.class); } case "libraries" -> { if (value instanceof JsonArray array) { @@ -121,7 +121,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t for (JsonElement element : array) { list.add(JsonUtils.GSON.fromJson(element, Library.class)); } - editor.libraries = Collections.unmodifiableList(list); + builder.libraries = Collections.unmodifiableList(list); } } case "compatibilityRules" -> { @@ -130,7 +130,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t for (JsonElement element : array) { list.add(JsonUtils.GSON.fromJson(element, CompatibilityRule.class)); } - editor.compatibilityRules = Collections.unmodifiableList(list); + builder.compatibilityRules = Collections.unmodifiableList(list); } } case "downloads" -> { @@ -143,7 +143,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } catch (IllegalArgumentException ignored) { } } - editor.downloads = Collections.unmodifiableMap(map); + builder.downloads = Collections.unmodifiableMap(map); } } case "logging" -> { @@ -156,40 +156,40 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } catch (IllegalArgumentException ignored) { } } - editor.logging = Map.copyOf(map); + builder.logging = Map.copyOf(map); } } case "type" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { try { - editor.type = ReleaseType.valueOf(primitive.getAsString()); + builder.type = ReleaseType.valueOf(primitive.getAsString()); } catch (IllegalArgumentException ignored) { } } } case "time" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - editor.time = primitive.getAsString(); + builder.time = primitive.getAsString(); } } case "releaseTime" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - editor.releaseTime = primitive.getAsString(); + builder.releaseTime = primitive.getAsString(); } } case "minimumLauncherVersion" -> { if (value instanceof JsonPrimitive primitive && primitive.isNumber()) { - editor.minimumLauncherVersion = primitive.getAsInt(); + builder.minimumLauncherVersion = primitive.getAsInt(); } } case "root" -> { if (value instanceof JsonPrimitive primitive && primitive.isBoolean()) { - editor.root = primitive.getAsBoolean(); + builder.root = primitive.getAsBoolean(); } } case "hidden" -> { if (value instanceof JsonPrimitive primitive && primitive.isBoolean()) { - editor.hidden = primitive.getAsBoolean(); + builder.hidden = primitive.getAsBoolean(); } } case "patches" -> { @@ -198,18 +198,22 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t for (JsonElement element : array) { // TODO } - editor.patches = List.copyOf(list); + builder.patches = List.copyOf(list); } } } } - editor.rawJson = json; - return editor.toManifest(); + builder.rawJson = json; + if (builder.id == null) { + throw new JsonParseException("Missing instance id"); + } + + return builder.toManifest(); } - private static final class Editor { + private static final class Builder { // @formatter:off private @Nullable GameInstanceID id; private @Nullable String minecraftArguments; @@ -236,10 +240,10 @@ private static final class Editor { // @formatter:on - Editor() { + Builder() { } - Editor(GameInstanceManifest manifest) { + Builder(GameInstanceManifest manifest) { this.id = manifest.id; this.minecraftArguments = manifest.minecraftArguments; this.arguments = manifest.arguments; From 8ee17342a5b7b0cac71816ae04567e4bcaad25ab Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 4 Jul 2026 18:19:36 +0800 Subject: [PATCH 07/85] feat: enhance Library JSON parsing with custom deserializer and improve list handling in GameInstanceManifest --- .../hmcl/game/GameInstanceManifest.java | 10 ++- .../java/org/jackhuang/hmcl/game/Library.java | 84 +++++++++++++++++-- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index cd6e018a24..4913a37bea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -119,9 +119,13 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t if (value instanceof JsonArray array) { List list = new ArrayList<>(array.size()); for (JsonElement element : array) { - list.add(JsonUtils.GSON.fromJson(element, Library.class)); + if (element instanceof JsonObject object) { + list.add(Library.fromJson(object)); + } else { + // TODO + } } - builder.libraries = Collections.unmodifiableList(list); + builder.libraries = List.copyOf(list); } } case "compatibilityRules" -> { @@ -130,7 +134,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t for (JsonElement element : array) { list.add(JsonUtils.GSON.fromJson(element, CompatibilityRule.class)); } - builder.compatibilityRules = Collections.unmodifiableList(list); + builder.compatibilityRules = List.copyOf(list); } } case "downloads" -> { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java index dc253ca8e3..1a18496979 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java @@ -17,18 +17,25 @@ */ package org.jackhuang.hmcl.game; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.util.Constants; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.ToStringBuilder; +import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.TolerableValidationException; import org.jackhuang.hmcl.util.gson.Validation; import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Type; import java.util.*; /** @@ -36,16 +43,13 @@ * * @author huangyuhui */ +@JsonAdapter(Library.Adapter.class) @Immutable public class Library implements Comparable, Validation { - /** - *

A possible native descriptors can be: [variant-]os[-key]

- * - *

- * Variant can be empty string, 'native', or 'natives'. - * Key can be empty string, system arch, or system arch bit count. - *

- */ + /// A possible native descriptors can be: [variant-]os[-key] + /// + /// Variant can be an empty string, 'native', or 'natives'. + /// Key can be an empty string, system arch, or system arch bit count. private static final String[] POSSIBLE_NATIVE_DESCRIPTORS; static { @@ -74,6 +78,56 @@ public class Library implements Comparable, Validation { } } + + /// Parses a library from either the standard version JSON format or TLauncher library format. + public static Library fromJson(JsonObject json) { + if (json == null) { + throw new JsonParseException("Library must be a JSON object"); + } + + Artifact artifact = fromJsonMember(json, "name", Artifact.class); + String url = JsonUtils.getString(json, "url"); + LibrariesDownloadInfo downloads = fromJsonMember(json, "downloads", LibrariesDownloadInfo.class); + + if (downloads == null && (json.has("artifact") || json.has("classifies"))) { + LibraryDownloadInfo downloadArtifact = fromJsonMember(json, "artifact", LibraryDownloadInfo.class); + Map classifiers = fromJsonMember( + json, + "classifies", + JsonUtils.mapTypeOf(String.class, LibraryDownloadInfo.class).getType() + ); + downloads = new LibrariesDownloadInfo(downloadArtifact, classifiers); + } + + List checksums = fromJsonMember(json, "checksums", JsonUtils.listTypeOf(String.class).getType()); + ExtractRules extract = fromJsonMember(json, "extract", ExtractRules.class); + Map natives = fromJsonMember(json, "natives", JsonUtils.mapTypeOf(String.class, String.class).getType()); + List rules = fromJsonMember(json, "rules", JsonUtils.listTypeOf(CompatibilityRule.class).getType()); + + String hint = JsonUtils.getString(json, "hint"); + if (hint == null) { + hint = JsonUtils.getString(json, "MMC-hint"); + } + + String fileName = JsonUtils.getString(json, "filename"); + if (fileName == null) { + fileName = JsonUtils.getString(json, "MMC-filename"); + } + + return new Library(artifact, url, downloads, checksums, extract, natives, rules, hint, fileName); + } + + /// Reads a JSON member using HMCL's configured Gson instance. + private static @Nullable T fromJsonMember(JsonObject json, String name, Class type) { + return fromJsonMember(json, name, (Type) type); + } + + /// Reads a JSON member using HMCL's configured Gson instance. + private static @Nullable T fromJsonMember(JsonObject json, String name, Type type) { + JsonElement element = json.get(name); + return element == null || element.isJsonNull() ? null : JsonUtils.GSON.fromJson(element, type); + } + @SerializedName("name") private final Artifact artifact; private final String url; @@ -295,4 +349,18 @@ public void validate() throws JsonParseException, TolerableValidationException { if (artifact == null) throw new JsonParseException("Library.name cannot be null"); } + + /// Gson deserializer for library JSON variants. + public static final class Adapter implements JsonDeserializer { + @Override + public Library deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json == null || json.isJsonNull()) + return null; + + if (json instanceof JsonObject jsonObject) + return fromJson(jsonObject); + else + throw new JsonParseException("Library must be a JSON object"); + } + } } From 4461f01cc13ad8e212fbf03584132655a82d1e1b Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 4 Jul 2026 18:29:09 +0800 Subject: [PATCH 08/85] feat: add GameInstance interface and implement read-write lock in DefaultGameRepository2 for thread safety --- .../java/org/jackhuang/hmcl/GameInstance.java | 31 +++++++++++++++++++ .../hmcl/game/DefaultGameRepository2.java | 4 ++- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/GameInstance.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/GameInstance.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/GameInstance.java new file mode 100644 index 0000000000..378d982fdf --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/GameInstance.java @@ -0,0 +1,31 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl; + +import org.jackhuang.hmcl.game.GameInstanceID; +import org.jackhuang.hmcl.game.GameRepository; +import org.jetbrains.annotations.NotNullByDefault; + +@NotNullByDefault +public interface GameInstance { + GameRepository repository(); + + GameInstanceID id(); + + +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index 4d716f8177..56654fa64f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Stream; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -73,6 +74,8 @@ private static Library classicLibrary(String name) { null, null, null, null, null, null); } + protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private Path baseDirectory; private volatile @Unmodifiable Snapshot snapshot; @@ -81,7 +84,6 @@ public DefaultGameRepository2(Path baseDirectory) { this.snapshot = new Snapshot(baseDirectory); } - public Path getBaseDirectory() { return baseDirectory; } From a9d1070512c57e48dfa44ecfdcb636c94fb195d6 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 01:33:52 +0800 Subject: [PATCH 09/85] update --- .../hmcl/game/DefaultGameInstance.java | 46 ++++++ .../hmcl/game/DefaultGameRepository2.java | 123 +++++++++------- .../hmcl/{ => game}/GameInstance.java | 8 +- .../hmcl/game/GameInstanceManifest.java | 139 +++++++++++++++++- .../hmcl/game/GameInstancePatch.java | 11 +- .../jackhuang/hmcl/game/GameRepository2.java | 5 - 6 files changed, 266 insertions(+), 66 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameInstance.java rename HMCLCore/src/main/java/org/jackhuang/hmcl/{ => game}/GameInstance.java (85%) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameInstance.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameInstance.java new file mode 100644 index 0000000000..a7213701d3 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameInstance.java @@ -0,0 +1,46 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import org.jackhuang.hmcl.util.versioning.GameVersionNumber; +import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; + +@NotNullByDefault +public class DefaultGameInstance implements GameInstance { + + private final DefaultGameRepository2 repository; + private final GameInstanceID id; + + protected @Nullable GameVersionNumber version; + + protected DefaultGameInstance(DefaultGameRepository2 repository, GameInstanceID id) { + this.repository = repository; + this.id = id; + } + + @Override + public DefaultGameRepository2 repository() { + return repository; + } + + @Override + public GameInstanceID id() { + return id; + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index 56654fa64f..e5fae34c07 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -17,21 +17,18 @@ */ package org.jackhuang.hmcl.game; -import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; +import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Stream; @@ -76,16 +73,22 @@ private static Library classicLibrary(String name) { protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - private Path baseDirectory; - private volatile @Unmodifiable Snapshot snapshot; + private volatile Status status; public DefaultGameRepository2(Path baseDirectory) { - this.baseDirectory = baseDirectory; - this.snapshot = new Snapshot(baseDirectory); + this.status = new Status(baseDirectory); + } + + public Lock readLock() { + return lock.readLock(); + } + + public Lock writeLock() { + return lock.writeLock(); } public Path getBaseDirectory() { - return baseDirectory; + return status.baseDirectory; } private static boolean hasClassicVersion(Path baseDirectory) { @@ -96,35 +99,17 @@ private static boolean hasClassicVersion(Path baseDirectory) { && Files.exists(bin.resolve("lwjgl_util.jar")); } - public GameInstanceManifest readInstanceManifest(Path file) throws IOException, JsonParseException { - JsonObject json = JsonUtils.fromJsonFile(file, JsonObject.class); - try { - // Try TLauncher version json format - // return JsonUtils.GSON.fromJson(json, TLauncherVersion.class).toVersion(); - throw new UnsupportedOperationException("TODO"); - } catch (JsonParseException ignored) { - } - - try { - // Try official version json format - return JsonUtils.GSON.fromJson(json, GameInstanceManifest.class); // TODO - } catch (JsonParseException ignored) { - } - - LOG.warning("Cannot parse game instance manifest json: " + file + "\n" + json); - throw new JsonParseException("Game instance manifest incorrect"); - } @Override public void refresh() { - Snapshot newSnapshot = new Snapshot(baseDirectory); + Status newStatus = new Status(status.baseDirectory); - if (hasClassicVersion(newSnapshot.baseDirectory)) { + if (hasClassicVersion(newStatus.baseDirectory)) { GameInstanceID id = CLASSIC_MANIFEST.id(); - newSnapshot.instances.put(id, new InstanceHolder(newSnapshot, id, CLASSIC_MANIFEST)); + newStatus.instances.put(id, new InstanceHolder(newStatus, id, CLASSIC_MANIFEST)); } - Path versionsDir = newSnapshot.baseDirectory.resolve("versions"); + Path versionsDir = newStatus.baseDirectory.resolve("versions"); if (Files.isDirectory(versionsDir)) { try (Stream stream = Files.list(versionsDir)) { stream.parallel().filter(Files::isDirectory).flatMap(dir -> { @@ -137,7 +122,9 @@ public void refresh() { GameInstanceManifest manifest; try { - manifest = readInstanceManifest(json); + manifest = JsonUtils.fromJsonFile(json, GameInstanceManifest.class); + if (manifest == null) + throw new JsonParseException("Manifest is null"); } catch (Exception e) { LOG.warning("Malformed version json " + id, e); return Stream.empty(); @@ -149,53 +136,89 @@ public void refresh() { } return Stream.of(manifest); - }).forEachOrdered(it -> newSnapshot.instances.put( + }).forEachOrdered(it -> newStatus.instances.put( it.id(), - new InstanceHolder(newSnapshot, it.id(), it))); + new InstanceHolder(newStatus, it.id(), it))); } catch (IOException e) { LOG.warning("Failed to load versions from " + versionsDir, e); } } } - @Override - public GameVersionNumber getGameVersion(GameInstanceID instanceId) throws NoSuchGameInstanceException { - InstanceHolder instance = snapshot.instances.get(instanceId); - if (instance == null) { - throw new NoSuchGameInstanceException(instanceId); - } - - throw new UnsupportedOperationException("TODO"); - } - @Override public Path getInstanceRoot(GameInstanceID instanceId) { return getBaseDirectory().resolve("versions").resolve(instanceId.id()); } + @Override public ResolvedGameInstanceManifest resolve(GameInstanceManifest manifest) { throw new UnsupportedOperationException("TODO"); } - protected static class Snapshot { + protected static class Status { private final Path baseDirectory; private final Map instances = new TreeMap<>(); - protected Snapshot(Path baseDirectory) { + protected Status(Path baseDirectory) { this.baseDirectory = baseDirectory; } + + private GameInstanceManifest resolve(GameInstanceManifest manifest, + Set resolvedSoFar) throws NoSuchGameInstanceException { + GameInstanceManifest currentManifest; + + if (manifest.inheritsFrom() == null) { + if (manifest.isRoot()) { + // TODO: Breaking change, require much testing on versions installed with external installer, other launchers, and all kinds of versions. + currentManifest = manifest.patches() != null ? new GameInstanceManifest(manifest.id()).withPatches(manifest.patches()) : manifest; + } else { + currentManifest = manifest; + } + currentManifest = manifest.jar() == null ? manifest.withJar(manifest.id().id()) : currentManifest.withJar(manifest.jar()); + } else { + // To maximize the compatibility. + if (!resolvedSoFar.add(manifest.id())) { + LOG.warning("Found circular dependency versions: " + resolvedSoFar); + currentManifest = manifest.jar() == null ? manifest.withJar(manifest.id().id()) : manifest; + } else { + InstanceHolder parentInstance = instances.get(manifest.inheritsFrom()); + if (parentInstance == null) { + throw new NoSuchGameInstanceException(manifest.inheritsFrom()); + } + + // It is supposed to auto-install a version in getVersion. + currentManifest = manifest.merge(resolve(parentInstance.manifest, resolvedSoFar)); + } + } + + if (manifest.patches() == null) { + // This is a version from external launcher. NO need to resolve the patches. + return currentManifest; + } else if (!manifest.patches().isEmpty()) { + // Assume patches themselves do not have patches recursively. + List sortedPatches = manifest.patches().stream() + .sorted(Comparator.comparing(GameInstancePatch::priority)) + .toList(); + for (GameInstancePatch patch : sortedPatches) { + currentManifest = patch.withJar(null).merge(currentManifest); + } + } + + return currentManifest.setId(id); + } + } protected static class InstanceHolder { - protected final Snapshot snapshot; + protected final Status status; protected final GameInstanceID id; protected final GameInstanceManifest manifest; protected @Nullable GameVersionNumber version; - protected InstanceHolder(Snapshot snapshot, GameInstanceID id, GameInstanceManifest manifest) { - this.snapshot = snapshot; + protected InstanceHolder(Status status, GameInstanceID id, GameInstanceManifest manifest) { + this.status = status; this.id = id; this.manifest = manifest; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/GameInstance.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstance.java similarity index 85% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/GameInstance.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstance.java index 378d982fdf..56b7afa547 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/GameInstance.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstance.java @@ -15,17 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl; +package org.jackhuang.hmcl.game; -import org.jackhuang.hmcl.game.GameInstanceID; -import org.jackhuang.hmcl.game.GameRepository; import org.jetbrains.annotations.NotNullByDefault; @NotNullByDefault public interface GameInstance { - GameRepository repository(); + GameRepository2 repository(); GameInstanceID id(); - - } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 4913a37bea..1f639b033e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -22,6 +22,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; +import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; @@ -35,7 +36,7 @@ public record GameInstanceManifest( @Nullable String minecraftArguments, @Nullable Arguments arguments, @Nullable String mainClass, - @Nullable String inheritsFrom, + @Nullable GameInstanceID inheritsFrom, @Nullable String jar, @Nullable AssetIndexInfo assetIndex, @Nullable String assets, @@ -55,6 +56,58 @@ public record GameInstanceManifest( @Nullable @Unmodifiable JsonObject rawJson ) { + GameInstanceManifest merge(GameInstanceManifest parent) { + return new GameInstanceManifest( + id, + minecraftArguments == null ? parent.minecraftArguments : minecraftArguments, + Arguments.merge(parent.arguments, arguments), + mainClass == null ? parent.mainClass : mainClass, + null, // inheritsFrom + jar == null ? parent.jar : jar, + assetIndex == null ? parent.assetIndex : assetIndex, + assets == null ? parent.assets : assets, + complianceLevel, + javaVersion == null ? parent.javaVersion : javaVersion, + Lang.merge(this.libraries, parent.libraries), + Lang.merge(parent.compatibilityRules, this.compatibilityRules), + downloads == null ? parent.downloads : downloads, + logging == null ? parent.logging : logging, + type == null ? parent.type : type, + time == null ? parent.time : time, + releaseTime == null ? parent.releaseTime : releaseTime, + Lang.merge(minimumLauncherVersion, parent.minimumLauncherVersion, Math::max), + true, + hidden, + Lang.merge(Lang.merge(parent.patches, Collections.singleton(toPatch())), patches), + null + ); + } + + public GameInstanceManifest(GameInstanceID id) { + this(id, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + true, + null, + null, + null); + } + public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) throws JsonParseException { if (copyJson) { json = json.deepCopy(); @@ -216,6 +269,45 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t return builder.toManifest(); } + public boolean isRoot() { + return root != null && root; + } + + public GameInstanceManifest withId(GameInstanceID id) { + Objects.requireNonNull(id); + + if (Objects.equals(this.id, id)) { + return this; + } + + Builder builder = new Builder(this); + builder.setId(id); + return builder.toManifest(); + } + + public GameInstanceManifest withJar(String jar) { + if (Objects.equals(this.jar, jar)) { + return this; + } + + Builder builder = new Builder(this); + builder.setJar(jar); + return builder.toManifest(); + } + + public GameInstanceManifest withPatches(@Nullable List patches) { + if (patches == this.patches) { + return this; + } + + Builder builder = new Builder(this); + builder.setPatches(patches); + return builder.toManifest(); + } + + public GameInstancePatch toPatch() { + throw new UnsupportedOperationException("TODO"); + } private static final class Builder { // @formatter:off @@ -240,10 +332,9 @@ private static final class Builder { private @Nullable Boolean root; private @Nullable Boolean hidden; private @Nullable @Unmodifiable List patches; - private @Nullable @Unmodifiable JsonObject rawJson; + private @Nullable JsonObject rawJson; // @formatter:on - Builder() { } @@ -272,6 +363,48 @@ private static final class Builder { this.rawJson = manifest.rawJson != null ? manifest.rawJson.deepCopy() : null; } + public void setId(GameInstanceID id) { + if (Objects.equals(this.id, id)) + return; + + this.id = id; + if (rawJson != null) { + rawJson.addProperty("id", id.toString()); + } + } + + public void setJar(@Nullable String jar) { + if (Objects.equals(this.jar, jar)) + return; + + this.jar = jar; + if (rawJson != null) { + if (jar != null) { + rawJson.addProperty("jar", jar); + } else { + rawJson.remove("jar"); + } + } + } + + public void setPatches(@Nullable @Unmodifiable List patches) { + if (patches == this.patches) { + return; + } + + if (patches != null) { + this.patches = List.copyOf(patches); + if (rawJson != null) { + // TODO: set patches + } + } else { + this.patches = null; + if (rawJson != null) { + rawJson.remove("patches"); + } + } + } + GameInstanceManifest toManifest() { if (id == null) { throw new IllegalStateException("id is null"); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 8110668cc5..3c1029d8f7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -28,6 +28,7 @@ @NotNullByDefault public record GameInstancePatch( @Nullable GameInstanceID id, + Integer priority, @Nullable String minecraftArguments, @Nullable Arguments arguments, @Nullable String mainClass, @@ -45,9 +46,15 @@ public record GameInstancePatch( @Nullable String time, @Nullable String releaseTime, @Nullable Integer minimumLauncherVersion, - @Nullable Boolean root, @Nullable Boolean hidden, - @Nullable @Unmodifiable List patches, @Unmodifiable Map unknownFields ) { + + public GameInstancePatch withJar(@Nullable String jar) { + throw new UnsupportedOperationException("TODO"); + } + + GameInstancePatch merge(GameInstancePatch parent) { + + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java index f3dadb60c2..fe56394266 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java @@ -17,7 +17,6 @@ */ package org.jackhuang.hmcl.game; -import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import org.jetbrains.annotations.NotNullByDefault; import java.nio.file.Path; @@ -28,10 +27,6 @@ public interface GameRepository2 { void refresh(); - GameVersionNumber getGameVersion(GameInstanceID instanceId) throws NoSuchGameInstanceException; - Path getInstanceRoot(GameInstanceID instanceId); - - } From ec44baa5f5b601f5e922f28d759e5a40e00142f9 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 02:10:12 +0800 Subject: [PATCH 10/85] feat: update GameInstance resolution logic and refactor manifest handling for improved clarity --- .../hmcl/game/DefaultGameInstance.java | 46 ------------------- .../hmcl/game/DefaultGameRepository2.java | 8 ++-- .../org/jackhuang/hmcl/game/GameInstance.java | 27 ----------- .../hmcl/game/GameInstanceManifest.java | 19 +++----- .../hmcl/game/GameInstancePatch.java | 30 ++++++++++-- .../jackhuang/hmcl/game/GameRepository2.java | 2 +- 6 files changed, 38 insertions(+), 94 deletions(-) delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameInstance.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstance.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameInstance.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameInstance.java deleted file mode 100644 index a7213701d3..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameInstance.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2026 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -import org.jackhuang.hmcl.util.versioning.GameVersionNumber; -import org.jetbrains.annotations.NotNullByDefault; -import org.jetbrains.annotations.Nullable; - -@NotNullByDefault -public class DefaultGameInstance implements GameInstance { - - private final DefaultGameRepository2 repository; - private final GameInstanceID id; - - protected @Nullable GameVersionNumber version; - - protected DefaultGameInstance(DefaultGameRepository2 repository, GameInstanceID id) { - this.repository = repository; - this.id = id; - } - - @Override - public DefaultGameRepository2 repository() { - return repository; - } - - @Override - public GameInstanceID id() { - return id; - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index e5fae34c07..4e30edbf8a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -152,8 +152,8 @@ public Path getInstanceRoot(GameInstanceID instanceId) { @Override - public ResolvedGameInstanceManifest resolve(GameInstanceManifest manifest) { - throw new UnsupportedOperationException("TODO"); + public GameInstanceManifest resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException { + return status.resolve(manifest, new HashSet<>()); } protected static class Status { @@ -193,7 +193,7 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, } if (manifest.patches() == null) { - // This is a version from external launcher. NO need to resolve the patches. + // This is a version from an external launcher. NO need to resolve the patches. return currentManifest; } else if (!manifest.patches().isEmpty()) { // Assume patches themselves do not have patches recursively. @@ -205,7 +205,7 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, } } - return currentManifest.setId(id); + return currentManifest.withId(manifest.id()); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstance.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstance.java deleted file mode 100644 index 56b7afa547..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstance.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2026 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -import org.jetbrains.annotations.NotNullByDefault; - -@NotNullByDefault -public interface GameInstance { - GameRepository2 repository(); - - GameInstanceID id(); -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 1f639b033e..e671795478 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -144,7 +144,10 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } case "inheritsFrom" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - builder.inheritsFrom = primitive.getAsString(); + try { + builder.inheritsFrom = new GameInstanceID(primitive.getAsString()); + } catch (IllegalArgumentException ignored) { + } } } case "jar" -> { @@ -315,7 +318,7 @@ private static final class Builder { private @Nullable String minecraftArguments; private @Nullable Arguments arguments; private @Nullable String mainClass; - private @Nullable String inheritsFrom; + private @Nullable GameInstanceID inheritsFrom; private @Nullable String jar; private @Nullable AssetIndexInfo assetIndex; private @Nullable String assets; @@ -364,9 +367,6 @@ private static final class Builder { } public void setId(GameInstanceID id) { - if (Objects.equals(this.id, id)) - return; - this.id = id; if (rawJson != null) { rawJson.addProperty("id", id.toString()); @@ -374,9 +374,6 @@ public void setId(GameInstanceID id) { } public void setJar(@Nullable String jar) { - if (Objects.equals(this.jar, jar)) - return; - this.jar = jar; if (rawJson != null) { if (jar != null) { @@ -387,11 +384,7 @@ public void setJar(@Nullable String jar) { } } - public void setPatches(@Nullable @Unmodifiable List patches) { - if (patches == this.patches) { - return; - } - + public void setPatches(@Nullable List patches) { if (patches != null) { this.patches = List.copyOf(patches); if (rawJson != null) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 3c1029d8f7..28b5fb34b8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -18,16 +18,18 @@ package org.jackhuang.hmcl.game; import com.google.gson.JsonElement; +import org.jackhuang.hmcl.util.Lang; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; +import java.util.Collections; import java.util.List; import java.util.Map; @NotNullByDefault public record GameInstancePatch( - @Nullable GameInstanceID id, + @Nullable String id, Integer priority, @Nullable String minecraftArguments, @Nullable Arguments arguments, @@ -54,7 +56,29 @@ public GameInstancePatch withJar(@Nullable String jar) { throw new UnsupportedOperationException("TODO"); } - GameInstancePatch merge(GameInstancePatch parent) { - + GameInstanceManifest merge(GameInstanceManifest parent) { + return new GameInstanceManifest( + parent.id(), + minecraftArguments == null ? parent.minecraftArguments() : minecraftArguments, + Arguments.merge(parent.arguments(), arguments), + mainClass == null ? parent.mainClass() : mainClass, + null, // inheritsFrom + jar == null ? parent.jar() : jar, + assetIndex == null ? parent.assetIndex() : assetIndex, + assets == null ? parent.assets() : assets, + complianceLevel, + javaVersion == null ? parent.javaVersion() : javaVersion, + Lang.merge(this.libraries, parent.libraries()), + Lang.merge(parent.compatibilityRules(), this.compatibilityRules), + downloads == null ? parent.downloads() : downloads, + logging == null ? parent.logging() : logging, + type == null ? parent.type() : type, + time == null ? parent.time() : time, + releaseTime == null ? parent.releaseTime() : releaseTime, + Lang.merge(minimumLauncherVersion, parent.minimumLauncherVersion(), Math::max), + hidden, + true, + parent.patches(), + null); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java index fe56394266..9d8005975b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java @@ -23,7 +23,7 @@ @NotNullByDefault public interface GameRepository2 { - ResolvedGameInstanceManifest resolve(GameInstanceManifest manifest); + GameInstanceManifest resolve(GameInstanceManifest manifest)throws NoSuchGameInstanceException; void refresh(); From 4b0ecf8e0489e208f8262f70bdb853ca84d38a05 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 04:08:39 +0800 Subject: [PATCH 11/85] feat: enhance GameRepository2 with instance management methods and update GameInstanceManifest to use GameInstanceID for jar references --- .../hmcl/game/DefaultGameRepository2.java | 290 ++++++++++++++++-- .../hmcl/game/GameInstanceManifest.java | 15 +- .../hmcl/game/GameInstancePatch.java | 2 +- .../jackhuang/hmcl/game/GameRepository2.java | 83 ++++- 4 files changed, 359 insertions(+), 31 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index 4e30edbf8a..0ef5664906 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -18,8 +18,12 @@ package org.jackhuang.hmcl.game; import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.modpack.ModpackConfiguration; +import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.platform.Platform; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; @@ -28,8 +32,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Stream; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -71,7 +73,13 @@ private static Library classicLibrary(String name) { null, null, null, null, null, null); } - protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private static boolean hasClassicVersion(Path baseDirectory) { + Path bin = baseDirectory.resolve("bin"); + return Files.isDirectory(bin) + && Files.exists(bin.resolve("lwjgl.jar")) + && Files.exists(bin.resolve("jinput.jar")) + && Files.exists(bin.resolve("lwjgl_util.jar")); + } private volatile Status status; @@ -79,27 +87,10 @@ public DefaultGameRepository2(Path baseDirectory) { this.status = new Status(baseDirectory); } - public Lock readLock() { - return lock.readLock(); - } - - public Lock writeLock() { - return lock.writeLock(); - } - public Path getBaseDirectory() { return status.baseDirectory; } - private static boolean hasClassicVersion(Path baseDirectory) { - Path bin = baseDirectory.resolve("bin"); - return Files.isDirectory(bin) - && Files.exists(bin.resolve("lwjgl.jar")) - && Files.exists(bin.resolve("jinput.jar")) - && Files.exists(bin.resolve("lwjgl_util.jar")); - } - - @Override public void refresh() { Status newStatus = new Status(status.baseDirectory); @@ -145,11 +136,264 @@ public void refresh() { } } + @Override + public boolean hasInstance(GameInstanceID instanceId) { + return status.instances.containsKey(instanceId); + } + + @Override + public GameInstanceManifest getInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException { + InstanceHolder instanceHolder = status.instances.get(instanceId); + if (instanceHolder == null) { + throw new NoSuchGameInstanceException(instanceId); + } + return instanceHolder.manifest; + } + + @Override + public GameInstanceManifest getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException { + InstanceHolder instanceHolder = status.instances.get(instanceId); + if (instanceHolder == null) { + throw new NoSuchGameInstanceException(instanceId); + } + + GameInstanceManifest resolvedManifest = instanceHolder.resolvedManifest; + if (resolvedManifest == null) { + resolvedManifest = resolve(instanceHolder.manifest); + instanceHolder.resolvedManifest = resolvedManifest; + } + return resolvedManifest; + } + + @Override + public int getInstanceCount() { + return status.instances.size(); + } + @Override public Path getInstanceRoot(GameInstanceID instanceId) { return getBaseDirectory().resolve("versions").resolve(instanceId.id()); } + @Override + public Collection getInstanceManifests() { + return status.instances.values().stream().map(i -> i.manifest).toList(); + } + + @Override + public Path getLibrariesDirectory(GameInstanceManifest manifest) { + return getBaseDirectory().resolve("libraries"); + } + + @Override + public Path getLibraryFile(GameInstanceManifest manifest, Library lib) { + if ("local".equals(lib.getHint())) { + if (lib.getFileName() != null) { + return getInstanceRoot(manifest.id()).resolve("libraries/" + lib.getFileName()); + } + + return getInstanceRoot(manifest.id()).resolve("libraries/" + lib.getArtifact().getFileName()); + } + + return getLibrariesDirectory(manifest).resolve(lib.getPath()); + } + + public Path getArtifactFile(Version version, Artifact artifact) { + return artifact.getPath(getBaseDirectory().resolve("libraries")); + } + + @Override + public Path getRunDirectory(GameInstanceID instanceId) { + return getBaseDirectory(); + } + + @Override + public Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceException { + GameInstanceManifest manifest = getResolvedInstanceManifest(instanceId); + + GameInstanceID jarID = Objects.requireNonNullElse(manifest.jar(), manifest.id()); + return getInstanceRoot(instanceId).resolve(jarID + ".jar"); + } + + @Override + public Path getInstanceJar(GameInstanceManifest manifest) { + return null; + } + + @Override + public boolean renameInstance(GameInstanceID from, GameInstanceID to) { + return false; + } + + @Override + public Optional getGameVersion(GameInstanceManifest manifest) { + try { + return getGameVersion(manifest.id()); + } catch (NoSuchGameInstanceException e) { + return Optional.empty(); + } + } + + @Override + public Path getNativeDirectory(GameInstanceID instanceId, Platform platform) { + return getInstanceRoot(instanceId).resolve("natives-" + platform); + } + + @Override + public Path getModsDirectory(GameInstanceID instanceId) { + return getRunDirectory(instanceId).resolve("mods"); + } + + @Override + public Path getResourcePackDirectory(GameInstanceID instanceId) { + return getRunDirectory(instanceId).resolve("resourcepacks"); + } + + public Path getInstanceJson(GameInstanceID instanceId) { + return getInstanceRoot(instanceId).resolve(instanceId.id() + ".json"); + } + + @Override + public AssetIndex getAssetIndex(GameInstanceID instanceId, String assetId) throws IOException { + try { + return Objects.requireNonNull(JsonUtils.fromJsonFile(getIndexFile(instanceId, assetId), AssetIndex.class)); + } catch (JsonParseException | NullPointerException e) { + throw new IOException("Asset index file malformed", e); + } + } + + @Override + public Path getActualAssetDirectory(GameInstanceID instanceId, String assetId) { + try { + return reconstructAssets(instanceId, assetId); + } catch (IOException | JsonParseException e) { + LOG.error("Unable to reconstruct asset directory", e); + return getAssetDirectory(instanceId, assetId); + } + } + + @Override + public Path getAssetDirectory(GameInstanceID instanceId, String assetId) { + return getBaseDirectory().resolve("assets"); + } + + @Override + public Optional getAssetObject(GameInstanceID instanceId, String assetId, String name) throws IOException { + try { + AssetObject assetObject = getAssetIndex(instanceId, assetId).getObjects().get(name); + if (assetObject == null) return Optional.empty(); + return Optional.of(getAssetObject(instanceId, assetId, assetObject)); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException("Unrecognized asset object " + name + " in asset " + assetId + " of version " + instanceId, e); + } + } + + @Override + public Path getAssetObject(GameInstanceID instanceId, String assetId, AssetObject obj) { + return getAssetObject(instanceId, getAssetDirectory(instanceId, assetId), obj); + } + + public Path getAssetObject(GameInstanceID instanceId, Path assetDir, AssetObject obj) { + return assetDir.resolve("objects").resolve(obj.getLocation()); + } + + @Override + public Path getIndexFile(GameInstanceID instanceId, String assetId) { + return getAssetDirectory(instanceId, assetId).resolve("indexes").resolve(assetId + ".json"); + } + + @Override + public Path getLoggingObject(GameInstanceID instanceId, String assetId, LoggingInfo loggingInfo) { + return getAssetDirectory(instanceId, assetId).resolve("log_configs").resolve(loggingInfo.file().getId()); + } + + protected Path reconstructAssets(GameInstanceID instanceId, String assetId) throws IOException, JsonParseException { + Path assetsDir = getAssetDirectory(instanceId, assetId); + Path indexFile = getIndexFile(instanceId, assetId); + Path virtualRoot = assetsDir.resolve("virtual").resolve(assetId); + + if (!Files.isRegularFile(indexFile)) + return assetsDir; + + AssetIndex index = JsonUtils.fromJsonFile(indexFile, AssetIndex.class); + + if (index == null) + return assetsDir; + + if (index.isVirtual()) { + Path resourcesDir = getRunDirectory(instanceId).resolve("resources"); + + int cnt = 0; + int tot = index.getObjects().size(); + for (Map.Entry entry : index.getObjects().entrySet()) { + Path target = virtualRoot.resolve(entry.getKey()); + Path original = getAssetObject(instanceId, assetsDir, entry.getValue()); + if (Files.exists(original)) { + cnt++; + if (!Files.isRegularFile(target)) + FileUtils.copyFile(original, target); + + if (index.needMapToResources()) { + target = resourcesDir.resolve(entry.getKey()); + if (!Files.isRegularFile(target)) + FileUtils.copyFile(original, target); + } + } + } + + // If the scale new format existent file is lower than 0.1, use the old format. + if (cnt * 10 < tot) + return assetsDir; + else + return virtualRoot; + } + + return assetsDir; + } + + public Task saveAsync(GameInstanceManifest instanceManifest) { + throw new UnsupportedOperationException("TODO"); +// if (instanceManifest.isResolvedPreservingPatches()) { +// return new VersionJsonSaveTask(this, MaintainTask.maintainPreservingPatches(this, instanceManifest)); +// } else { +// return new VersionJsonSaveTask(this, instanceManifest); +// } + } + + public Path getModpackConfiguration(GameInstanceID instanceId) { + return getInstanceRoot(instanceId).resolve("modpack.json"); + } + + @Nullable + public ModpackConfiguration readModpackConfiguration(GameInstanceID instanceId) throws IOException, NoSuchGameInstanceException { + if (!hasInstance(instanceId)) throw new NoSuchGameInstanceException(instanceId); + Path file = getModpackConfiguration(instanceId); + if (Files.notExists(file)) return null; + return JsonUtils.fromJsonFile(file, ModpackConfiguration.class); + } + + public boolean isModpack(GameInstanceID instanceId) { + return Files.exists(getModpackConfiguration(instanceId)); + } + + public ModManager getModManager(GameInstanceID instanceId) { + throw new UnsupportedOperationException("TODO"); + // TODO: // return new ModManager(this, instanceId); + } + + public Path getSavesDirectory(GameInstanceID instanceId) { + return getRunDirectory(instanceId).resolve("saves"); + } + + public Path getBackupsDirectory(GameInstanceID instanceID) { + return getRunDirectory(instanceID).resolve("backups"); + } + + public Path getSchematicsDirectory(GameInstanceID instanceId) { + return getRunDirectory(instanceId).resolve("schematics"); + } @Override public GameInstanceManifest resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException { @@ -175,12 +419,12 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, } else { currentManifest = manifest; } - currentManifest = manifest.jar() == null ? manifest.withJar(manifest.id().id()) : currentManifest.withJar(manifest.jar()); + currentManifest = manifest.jar() == null ? manifest.withJar(manifest.id()) : currentManifest.withJar(manifest.jar()); } else { // To maximize the compatibility. if (!resolvedSoFar.add(manifest.id())) { LOG.warning("Found circular dependency versions: " + resolvedSoFar); - currentManifest = manifest.jar() == null ? manifest.withJar(manifest.id().id()) : manifest; + currentManifest = manifest.jar() == null ? manifest.withJar(manifest.id()) : manifest; } else { InstanceHolder parentInstance = instances.get(manifest.inheritsFrom()); if (parentInstance == null) { @@ -214,7 +458,7 @@ protected static class InstanceHolder { protected final Status status; protected final GameInstanceID id; protected final GameInstanceManifest manifest; - + protected @Nullable GameInstanceManifest resolvedManifest; protected @Nullable GameVersionNumber version; protected InstanceHolder(Status status, GameInstanceID id, GameInstanceManifest manifest) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index e671795478..d3a7e91553 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -37,7 +37,7 @@ public record GameInstanceManifest( @Nullable Arguments arguments, @Nullable String mainClass, @Nullable GameInstanceID inheritsFrom, - @Nullable String jar, + @Nullable GameInstanceID jar, @Nullable AssetIndexInfo assetIndex, @Nullable String assets, @Nullable Integer complianceLevel, @@ -152,7 +152,10 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } case "jar" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - builder.jar = primitive.getAsString(); + try { + builder.jar = new GameInstanceID(primitive.getAsString()); + } catch (IllegalArgumentException ignored) { + } } } case "assetIndex" -> { @@ -288,7 +291,7 @@ public GameInstanceManifest withId(GameInstanceID id) { return builder.toManifest(); } - public GameInstanceManifest withJar(String jar) { + public GameInstanceManifest withJar(@Nullable GameInstanceID jar) { if (Objects.equals(this.jar, jar)) { return this; } @@ -319,7 +322,7 @@ private static final class Builder { private @Nullable Arguments arguments; private @Nullable String mainClass; private @Nullable GameInstanceID inheritsFrom; - private @Nullable String jar; + private @Nullable GameInstanceID jar; private @Nullable AssetIndexInfo assetIndex; private @Nullable String assets; private @Nullable Integer complianceLevel; @@ -373,11 +376,11 @@ public void setId(GameInstanceID id) { } } - public void setJar(@Nullable String jar) { + public void setJar(@Nullable GameInstanceID jar) { this.jar = jar; if (rawJson != null) { if (jar != null) { - rawJson.addProperty("jar", jar); + rawJson.addProperty("jar", jar.toString()); } else { rawJson.remove("jar"); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 28b5fb34b8..d55a46009e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -35,7 +35,7 @@ public record GameInstancePatch( @Nullable Arguments arguments, @Nullable String mainClass, @Nullable String inheritsFrom, - @Nullable String jar, + @Nullable GameInstanceID jar, @Nullable AssetIndexInfo assetIndex, @Nullable String assets, @Nullable Integer complianceLevel, diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java index 9d8005975b..f6de232945 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java @@ -17,16 +17,97 @@ */ package org.jackhuang.hmcl.game; +import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.platform.Platform; import org.jetbrains.annotations.NotNullByDefault; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; @NotNullByDefault public interface GameRepository2 { - GameInstanceManifest resolve(GameInstanceManifest manifest)throws NoSuchGameInstanceException; + GameInstanceManifest resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException; + + boolean hasInstance(GameInstanceID instanceId); + + GameInstanceManifest getInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; + + GameInstanceManifest getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; + + default GameInstanceManifest getResolvedPreservingPatchesInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException { + throw new UnsupportedOperationException(); // TODO + } + + int getInstanceCount(); + + Collection getInstanceManifests(); void refresh(); + default Task refreshAsync() { + return Task.runAsync(this::refresh); + } + Path getInstanceRoot(GameInstanceID instanceId); + Path getRunDirectory(GameInstanceID instanceId); + + Path getLibrariesDirectory(GameInstanceManifest manifest); + + Path getLibraryFile(GameInstanceManifest manifest, Library lib); + + Path getNativeDirectory(GameInstanceID instanceId, Platform platform); + + Path getModsDirectory(GameInstanceID instanceId); + + Path getResourcePackDirectory(GameInstanceID instanceId); + + Path getInstanceJar(GameInstanceManifest manifest); + + Optional getGameVersion(GameInstanceManifest manifest); + + default Optional getGameVersion(GameInstanceID instanceId) throws NoSuchGameInstanceException { + return getGameVersion(getInstanceManifest(instanceId)); + } + + default Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceException { + return getInstanceJar(resolve(getInstanceManifest(instanceId))); + } + + boolean renameInstance(GameInstanceID from, GameInstanceID to); + + Path getActualAssetDirectory(GameInstanceID instanceId, String assetId); + + Path getAssetDirectory(GameInstanceID instanceId, String assetId); + + Optional getAssetObject(GameInstanceID instanceId, String assetId, String name) throws IOException; + + Path getAssetObject(GameInstanceID instanceId, String assetId, AssetObject obj); + + AssetIndex getAssetIndex(GameInstanceID instanceId, String assetId) throws IOException; + + Path getIndexFile(GameInstanceID instanceId, String assetId); + + Path getLoggingObject(GameInstanceID instanceId, String assetId, LoggingInfo loggingInfo); + + default Set getClasspath(GameInstanceManifest manifest) { + Set classpath = new LinkedHashSet<>(); + if (manifest.libraries() != null) { + for (Library library : manifest.libraries()) + if (library.appliesToCurrentEnvironment() && !library.isNative()) { + Path f = getLibraryFile(manifest, library); + if (Files.isRegularFile(f)) + classpath.add(FileUtils.getAbsolutePath(f)); + } + } + + return classpath; + } + } From aa2a2c4a8e593d345e529212f82390e1c7ae8ea7 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 04:11:25 +0800 Subject: [PATCH 12/85] feat: add version field to GameInstancePatch for enhanced instance management --- .../src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java | 1 + 1 file changed, 1 insertion(+) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index d55a46009e..974f10e8f1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -30,6 +30,7 @@ @NotNullByDefault public record GameInstancePatch( @Nullable String id, + @Nullable String version, Integer priority, @Nullable String minecraftArguments, @Nullable Arguments arguments, From 67b5a6a031ef046c7f4bbc9b8190cc9cf31c4d07 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 04:12:17 +0800 Subject: [PATCH 13/85] delete class --- .../game/ResolvedGameInstanceManifest.java | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/ResolvedGameInstanceManifest.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ResolvedGameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ResolvedGameInstanceManifest.java deleted file mode 100644 index fe622c66a5..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ResolvedGameInstanceManifest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2026 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -import com.google.gson.JsonElement; -import org.jetbrains.annotations.NotNullByDefault; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; - -import java.util.List; -import java.util.Map; - -@NotNullByDefault -public record ResolvedGameInstanceManifest( - GameInstanceID id, - @Nullable String minecraftArguments, - @Nullable Arguments arguments, - @Nullable String mainClass, - @Nullable String jar, - @Nullable AssetIndexInfo assetIndex, - @Nullable String assets, - @Nullable Integer complianceLevel, - @Nullable GameJavaVersion javaVersion, - @Nullable @Unmodifiable List libraries, - @Nullable @Unmodifiable List compatibilityRules, - @Nullable @Unmodifiable Map downloads, - @Nullable @Unmodifiable Map logging, - @Nullable ReleaseType type, - @Nullable String time, - @Nullable String releaseTime, - @Nullable Integer minimumLauncherVersion, - @Nullable Boolean root, - @Nullable Boolean hidden -) { - -} From 1239944ebca4d57a590866acecd94d746ca8475f Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 05:03:48 +0800 Subject: [PATCH 14/85] feat: enhance GameRepository2 and related classes with resolved manifest handling and patch preservation --- .../hmcl/game/DefaultGameRepository2.java | 81 +++++++++++++++++-- .../hmcl/game/GameInstanceManifest.java | 65 ++++++++++++++- .../hmcl/game/GameInstancePatch.java | 36 ++++++++- .../jackhuang/hmcl/game/GameRepository2.java | 30 +++++-- 4 files changed, 195 insertions(+), 17 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index 0ef5664906..1348f379da 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -151,13 +151,13 @@ public GameInstanceManifest getInstanceManifest(GameInstanceID instanceId) throw } @Override - public GameInstanceManifest getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException { + public GameInstanceManifest.Resolved getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException { InstanceHolder instanceHolder = status.instances.get(instanceId); if (instanceHolder == null) { throw new NoSuchGameInstanceException(instanceId); } - GameInstanceManifest resolvedManifest = instanceHolder.resolvedManifest; + GameInstanceManifest.Resolved resolvedManifest = instanceHolder.resolvedManifest; if (resolvedManifest == null) { resolvedManifest = resolve(instanceHolder.manifest); instanceHolder.resolvedManifest = resolvedManifest; @@ -209,7 +209,7 @@ public Path getRunDirectory(GameInstanceID instanceId) { @Override public Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceException { - GameInstanceManifest manifest = getResolvedInstanceManifest(instanceId); + GameInstanceManifest manifest = getResolvedInstanceManifest(instanceId).manifest(); GameInstanceID jarID = Objects.requireNonNullElse(manifest.jar(), manifest.id()); return getInstanceRoot(instanceId).resolve(jarID + ".jar"); @@ -217,7 +217,8 @@ public Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceE @Override public Path getInstanceJar(GameInstanceManifest manifest) { - return null; + GameInstanceID jarID = Objects.requireNonNullElse(manifest.jar(), manifest.id()); + return getInstanceRoot(manifest.id()).resolve(jarID + ".jar"); } @Override @@ -396,8 +397,23 @@ public Path getSchematicsDirectory(GameInstanceID instanceId) { } @Override - public GameInstanceManifest resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException { - return status.resolve(manifest, new HashSet<>()); + public GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException { + return toResolved(status.resolve(manifest, new HashSet<>())); + } + + @Override + public GameInstanceManifest.Standalone resolvePreservingPatches(GameInstanceManifest manifest) throws NoSuchGameInstanceException { + GameInstanceManifest standaloneManifest = status.resolvePreservingPatches(manifest, new HashSet<>()); + GameInstanceID jar = resolve(manifest).manifest().jar(); + if (jar != null) { + standaloneManifest = standaloneManifest.withJar(jar); + } + return new GameInstanceManifest.Standalone(standaloneManifest); + } + + private static GameInstanceManifest.Resolved toResolved(GameInstanceManifest manifest) { + List appliedPatches = manifest.patches() == null ? List.of() : manifest.patches(); + return new GameInstanceManifest.Resolved(manifest.withPatches(null), appliedPatches); } protected static class Status { @@ -452,13 +468,64 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, return currentManifest.withId(manifest.id()); } + private GameInstanceManifest resolvePreservingPatches(GameInstanceManifest manifest, + Set resolvedSoFar) throws NoSuchGameInstanceException { + GameInstanceManifest currentManifest = manifest.isRoot() + ? manifest + : addPatches( + addPatches(new GameInstanceManifest(manifest.id()), Collections.singleton(manifest.toPatch())), + manifest.patches()); + + if (manifest.inheritsFrom() != null) { + // To maximize the compatibility. + if (!resolvedSoFar.add(manifest.id())) { + LOG.warning("Found circular dependency versions: " + resolvedSoFar); + } else { + InstanceHolder parentInstance = instances.get(manifest.inheritsFrom()); + if (parentInstance == null) { + throw new NoSuchGameInstanceException(manifest.inheritsFrom()); + } + + currentManifest = addPatches( + addPatches(resolvePreservingPatches(parentInstance.manifest, resolvedSoFar), Collections.singleton(manifest.toPatch())), + manifest.patches()); + } + } + + return currentManifest.withId(manifest.id()); + } + + private static GameInstanceManifest addPatches(GameInstanceManifest manifest, @Nullable Collection additional) { + if (additional == null || additional.isEmpty()) { + return manifest; + } + + Set patchIds = new HashSet<>(); + for (GameInstancePatch patch : additional) { + if (patch.id() != null) { + patchIds.add(patch.id()); + } + } + + List patches = new ArrayList<>(); + if (manifest.patches() != null) { + for (GameInstancePatch patch : manifest.patches()) { + if (patch.id() == null || !patchIds.contains(patch.id())) { + patches.add(patch); + } + } + } + patches.addAll(additional); + return manifest.withPatches(patches); + } + } protected static class InstanceHolder { protected final Status status; protected final GameInstanceID id; protected final GameInstanceManifest manifest; - protected @Nullable GameInstanceManifest resolvedManifest; + protected @Nullable GameInstanceManifest.Resolved resolvedManifest; protected @Nullable GameVersionNumber version; protected InstanceHolder(Status status, GameInstanceID id, GameInstanceManifest manifest) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index d3a7e91553..eb7e72f7fb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -56,6 +56,44 @@ public record GameInstanceManifest( @Nullable @Unmodifiable JsonObject rawJson ) { + /// A launch-ready manifest view with inheritance and pending patches applied. + /// + /// @param manifest the final manifest data used by launch-time consumers + /// @param appliedPatches patches that have already been applied to produce the final manifest + @NotNullByDefault + public record Resolved(GameInstanceManifest manifest, + @Unmodifiable List appliedPatches) { + + /// Creates a resolved manifest view. + public Resolved { + Objects.requireNonNull(manifest); + appliedPatches = List.copyOf(appliedPatches); + + if (manifest.inheritsFrom() != null) { + throw new IllegalArgumentException("Resolved manifest cannot inherit from another manifest"); + } + if (manifest.patches() != null && !manifest.patches().isEmpty()) { + throw new IllegalArgumentException("Resolved manifest cannot contain pending patches"); + } + } + } + + /// A manifest view whose inheritance has been folded while preserving pending patches. + /// + /// @param manifest the standalone manifest data that can be saved back to disk + @NotNullByDefault + public record Standalone(GameInstanceManifest manifest) { + + /// Creates a standalone manifest view. + public Standalone { + Objects.requireNonNull(manifest); + + if (manifest.inheritsFrom() != null) { + throw new IllegalArgumentException("Standalone manifest cannot inherit from another manifest"); + } + } + } + GameInstanceManifest merge(GameInstanceManifest parent) { return new GameInstanceManifest( id, @@ -311,8 +349,33 @@ public GameInstanceManifest withPatches(@Nullable List patche return builder.toManifest(); } + /// Converts this manifest into a hidden patch entry for preserving resolved inheritance. + /// + /// @return a patch containing this manifest's launch metadata public GameInstancePatch toPatch() { - throw new UnsupportedOperationException("TODO"); + return new GameInstancePatch( + "resolved." + id, + null, + Integer.MIN_VALUE, + minecraftArguments, + arguments, + mainClass, + inheritsFrom == null ? null : inheritsFrom.toString(), + jar, + assetIndex, + assets, + complianceLevel, + javaVersion, + libraries, + compatibilityRules, + downloads, + logging, + type, + time, + releaseTime, + minimumLauncherVersion, + true, + Map.of()); } private static final class Builder { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 974f10e8f1..da92a0a4cd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -23,9 +23,9 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; @NotNullByDefault public record GameInstancePatch( @@ -53,8 +53,38 @@ public record GameInstancePatch( @Unmodifiable Map unknownFields ) { - public GameInstancePatch withJar(@Nullable String jar) { - throw new UnsupportedOperationException("TODO"); + /// Returns a patch copy with the given jar target. + /// + /// @param jar the jar target, or `null` to let the parent manifest supply it + /// @return a patch with the requested jar target + public GameInstancePatch withJar(@Nullable GameInstanceID jar) { + if (Objects.equals(this.jar, jar)) { + return this; + } + + return new GameInstancePatch( + id, + version, + priority, + minecraftArguments, + arguments, + mainClass, + inheritsFrom, + jar, + assetIndex, + assets, + complianceLevel, + javaVersion, + libraries, + compatibilityRules, + downloads, + logging, + type, + time, + releaseTime, + minimumLauncherVersion, + hidden, + unknownFields); } GameInstanceManifest merge(GameInstanceManifest parent) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java index f6de232945..067380b0ba 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java @@ -32,16 +32,34 @@ @NotNullByDefault public interface GameRepository2 { - GameInstanceManifest resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException; + /// Resolves inheritance and pending patches into a launch-ready manifest view. + /// + /// @param manifest the manifest to resolve + /// @return the resolved manifest view + GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException; + + /// Resolves inheritance while leaving patches as manifest data. + /// + /// @param manifest the manifest to resolve + /// @return the standalone manifest view + GameInstanceManifest.Standalone resolvePreservingPatches(GameInstanceManifest manifest) throws NoSuchGameInstanceException; boolean hasInstance(GameInstanceID instanceId); GameInstanceManifest getInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; - GameInstanceManifest getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; - - default GameInstanceManifest getResolvedPreservingPatchesInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException { - throw new UnsupportedOperationException(); // TODO + /// Returns a cached launch-ready manifest view for the instance. + /// + /// @param instanceId the instance id + /// @return the resolved manifest view + GameInstanceManifest.Resolved getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; + + /// Returns a standalone manifest view for the instance. + /// + /// @param instanceId the instance id + /// @return the standalone manifest view + default GameInstanceManifest.Standalone getResolvedPreservingPatchesInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException { + return resolvePreservingPatches(getInstanceManifest(instanceId)); } int getInstanceCount(); @@ -77,7 +95,7 @@ default Optional getGameVersion(GameInstanceID instanceId) throws NoSuch } default Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceException { - return getInstanceJar(resolve(getInstanceManifest(instanceId))); + return getInstanceJar(getResolvedInstanceManifest(instanceId).manifest()); } boolean renameInstance(GameInstanceID from, GameInstanceID to); From 2864dd45a63600147fe57cc9354f5bcf82f799ab Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 20:48:12 +0800 Subject: [PATCH 15/85] refactor: simplify manifest merging in DefaultGameRepository2 and GameInstancePatch --- .../java/org/jackhuang/hmcl/game/DefaultGameRepository2.java | 2 +- .../main/java/org/jackhuang/hmcl/game/GameInstancePatch.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index 1348f379da..363771b280 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -461,7 +461,7 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, .sorted(Comparator.comparing(GameInstancePatch::priority)) .toList(); for (GameInstancePatch patch : sortedPatches) { - currentManifest = patch.withJar(null).merge(currentManifest); + currentManifest = patch.merge(currentManifest); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index da92a0a4cd..b518c927a6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -94,7 +94,7 @@ GameInstanceManifest merge(GameInstanceManifest parent) { Arguments.merge(parent.arguments(), arguments), mainClass == null ? parent.mainClass() : mainClass, null, // inheritsFrom - jar == null ? parent.jar() : jar, + parent.jar(), assetIndex == null ? parent.assetIndex() : assetIndex, assets == null ? parent.assets() : assets, complianceLevel, From 3227246a156a48f08e01de16b8407fd5befa571a Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 21:04:56 +0800 Subject: [PATCH 16/85] feat: add toJsonObject method for GameInstanceManifest and enhance GameInstancePatch structure --- .../hmcl/game/GameInstanceManifest.java | 125 ++++++++++++++++++ .../hmcl/game/GameInstancePatch.java | 37 +----- 2 files changed, 126 insertions(+), 36 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index eb7e72f7fb..c3f58219cf 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -378,6 +378,131 @@ public GameInstancePatch toPatch() { Map.of()); } + public JsonObject toJsonObject() { + if (rawJson != null) { + return rawJson.deepCopy(); + } + + JsonObject json = new JsonObject(); + json.addProperty("id", id.toString()); + + if (minecraftArguments != null) + json.addProperty("minecraftArguments", minecraftArguments); + if (arguments != null) + json.add("arguments", JsonUtils.GSON.toJsonTree(arguments)); + if (mainClass != null) + json.addProperty("mainClass", mainClass); + if (inheritsFrom != null) + json.addProperty("inheritsFrom", inheritsFrom.toString()); + if (jar != null) + json.addProperty("jar", jar.toString()); + if (assetIndex != null) + json.add("assetIndex", JsonUtils.GSON.toJsonTree(assetIndex)); + if (assets != null) + json.addProperty("assets", assets); + if (complianceLevel != null) + json.addProperty("complianceLevel", complianceLevel); + if (javaVersion != null) + json.add("javaVersion", JsonUtils.GSON.toJsonTree(javaVersion)); + if (libraries != null) + json.add("libraries", JsonUtils.GSON.toJsonTree(libraries)); + if (compatibilityRules != null) + json.add("compatibilityRules", JsonUtils.GSON.toJsonTree(compatibilityRules)); + if (downloads != null) { + JsonObject downloadsObject = new JsonObject(); + for (Map.Entry entry : downloads.entrySet()) { + downloadsObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + json.add("downloads", downloadsObject); + } + if (logging != null) { + JsonObject loggingObject = new JsonObject(); + for (Map.Entry entry : logging.entrySet()) { + loggingObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + json.add("logging", loggingObject); + } + if (type != null) + json.addProperty("type", type.name()); + if (time != null) + json.addProperty("time", time); + if (releaseTime != null) + json.addProperty("releaseTime", releaseTime); + if (minimumLauncherVersion != null) + json.addProperty("minimumLauncherVersion", minimumLauncherVersion); + if (root != null) + json.addProperty("root", root); + if (hidden != null) + json.addProperty("hidden", hidden); + if (patches != null) { + JsonArray patchesArray = new JsonArray(patches.size()); + for (GameInstancePatch patch : patches) { + JsonObject patchJson = new JsonObject(); + for (Map.Entry entry : patch.unknownFields().entrySet()) { + patchJson.add(entry.getKey(), entry.getValue().deepCopy()); + } + + if (patch.id() != null) + patchJson.addProperty("id", patch.id()); + if (patch.version() != null) + patchJson.addProperty("version", patch.version()); + if (patch.priority() != null) + patchJson.addProperty("priority", patch.priority()); + if (patch.minecraftArguments() != null) + patchJson.addProperty("minecraftArguments", patch.minecraftArguments()); + if (patch.arguments() != null) + patchJson.add("arguments", JsonUtils.GSON.toJsonTree(patch.arguments())); + if (patch.mainClass() != null) + patchJson.addProperty("mainClass", patch.mainClass()); + if (patch.inheritsFrom() != null) + patchJson.addProperty("inheritsFrom", patch.inheritsFrom()); + if (patch.jar() != null) + patchJson.addProperty("jar", patch.jar().toString()); + if (patch.assetIndex() != null) + patchJson.add("assetIndex", JsonUtils.GSON.toJsonTree(patch.assetIndex())); + if (patch.assets() != null) + patchJson.addProperty("assets", patch.assets()); + if (patch.complianceLevel() != null) + patchJson.addProperty("complianceLevel", patch.complianceLevel()); + if (patch.javaVersion() != null) + patchJson.add("javaVersion", JsonUtils.GSON.toJsonTree(patch.javaVersion())); + if (patch.libraries() != null) + patchJson.add("libraries", JsonUtils.GSON.toJsonTree(patch.libraries())); + if (patch.compatibilityRules() != null) + patchJson.add("compatibilityRules", JsonUtils.GSON.toJsonTree(patch.compatibilityRules())); + if (patch.downloads() != null) { + JsonObject downloadsObject = new JsonObject(); + for (Map.Entry entry : patch.downloads().entrySet()) { + downloadsObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + patchJson.add("downloads", downloadsObject); + } + if (patch.logging() != null) { + JsonObject loggingObject = new JsonObject(); + for (Map.Entry entry : patch.logging().entrySet()) { + loggingObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + patchJson.add("logging", loggingObject); + } + if (patch.type() != null) + patchJson.addProperty("type", patch.type().name()); + if (patch.time() != null) + patchJson.addProperty("time", patch.time()); + if (patch.releaseTime() != null) + patchJson.addProperty("releaseTime", patch.releaseTime()); + if (patch.minimumLauncherVersion() != null) + patchJson.addProperty("minimumLauncherVersion", patch.minimumLauncherVersion()); + if (patch.hidden() != null) + patchJson.addProperty("hidden", patch.hidden()); + + patchesArray.add(patchJson); + } + json.add("patches", patchesArray); + } + + return json; + } + private static final class Builder { // @formatter:off private @Nullable GameInstanceID id; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index b518c927a6..17965b059b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -25,13 +25,12 @@ import java.util.List; import java.util.Map; -import java.util.Objects; @NotNullByDefault public record GameInstancePatch( @Nullable String id, @Nullable String version, - Integer priority, + @Nullable Integer priority, @Nullable String minecraftArguments, @Nullable Arguments arguments, @Nullable String mainClass, @@ -53,40 +52,6 @@ public record GameInstancePatch( @Unmodifiable Map unknownFields ) { - /// Returns a patch copy with the given jar target. - /// - /// @param jar the jar target, or `null` to let the parent manifest supply it - /// @return a patch with the requested jar target - public GameInstancePatch withJar(@Nullable GameInstanceID jar) { - if (Objects.equals(this.jar, jar)) { - return this; - } - - return new GameInstancePatch( - id, - version, - priority, - minecraftArguments, - arguments, - mainClass, - inheritsFrom, - jar, - assetIndex, - assets, - complianceLevel, - javaVersion, - libraries, - compatibilityRules, - downloads, - logging, - type, - time, - releaseTime, - minimumLauncherVersion, - hidden, - unknownFields); - } - GameInstanceManifest merge(GameInstanceManifest parent) { return new GameInstanceManifest( parent.id(), From e0b9e9eaedf130df6d320f144d882fabbacbca2b Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 21:09:25 +0800 Subject: [PATCH 17/85] feat: implement toJsonObject method in GameInstancePatch and update GameInstanceManifest for patch serialization --- .../hmcl/game/GameInstanceManifest.java | 70 +---- .../hmcl/game/GameInstancePatch.java | 254 ++++++++++++++++++ 2 files changed, 263 insertions(+), 61 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index c3f58219cf..3e59983dbb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -297,7 +297,9 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t if (value instanceof JsonArray array) { List list = new ArrayList<>(array.size()); for (JsonElement element : array) { - // TODO + if (element instanceof JsonObject object) { + list.add(GameInstancePatch.fromJson(object)); + } } builder.patches = List.copyOf(list); } @@ -437,65 +439,7 @@ public JsonObject toJsonObject() { if (patches != null) { JsonArray patchesArray = new JsonArray(patches.size()); for (GameInstancePatch patch : patches) { - JsonObject patchJson = new JsonObject(); - for (Map.Entry entry : patch.unknownFields().entrySet()) { - patchJson.add(entry.getKey(), entry.getValue().deepCopy()); - } - - if (patch.id() != null) - patchJson.addProperty("id", patch.id()); - if (patch.version() != null) - patchJson.addProperty("version", patch.version()); - if (patch.priority() != null) - patchJson.addProperty("priority", patch.priority()); - if (patch.minecraftArguments() != null) - patchJson.addProperty("minecraftArguments", patch.minecraftArguments()); - if (patch.arguments() != null) - patchJson.add("arguments", JsonUtils.GSON.toJsonTree(patch.arguments())); - if (patch.mainClass() != null) - patchJson.addProperty("mainClass", patch.mainClass()); - if (patch.inheritsFrom() != null) - patchJson.addProperty("inheritsFrom", patch.inheritsFrom()); - if (patch.jar() != null) - patchJson.addProperty("jar", patch.jar().toString()); - if (patch.assetIndex() != null) - patchJson.add("assetIndex", JsonUtils.GSON.toJsonTree(patch.assetIndex())); - if (patch.assets() != null) - patchJson.addProperty("assets", patch.assets()); - if (patch.complianceLevel() != null) - patchJson.addProperty("complianceLevel", patch.complianceLevel()); - if (patch.javaVersion() != null) - patchJson.add("javaVersion", JsonUtils.GSON.toJsonTree(patch.javaVersion())); - if (patch.libraries() != null) - patchJson.add("libraries", JsonUtils.GSON.toJsonTree(patch.libraries())); - if (patch.compatibilityRules() != null) - patchJson.add("compatibilityRules", JsonUtils.GSON.toJsonTree(patch.compatibilityRules())); - if (patch.downloads() != null) { - JsonObject downloadsObject = new JsonObject(); - for (Map.Entry entry : patch.downloads().entrySet()) { - downloadsObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); - } - patchJson.add("downloads", downloadsObject); - } - if (patch.logging() != null) { - JsonObject loggingObject = new JsonObject(); - for (Map.Entry entry : patch.logging().entrySet()) { - loggingObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); - } - patchJson.add("logging", loggingObject); - } - if (patch.type() != null) - patchJson.addProperty("type", patch.type().name()); - if (patch.time() != null) - patchJson.addProperty("time", patch.time()); - if (patch.releaseTime() != null) - patchJson.addProperty("releaseTime", patch.releaseTime()); - if (patch.minimumLauncherVersion() != null) - patchJson.addProperty("minimumLauncherVersion", patch.minimumLauncherVersion()); - if (patch.hidden() != null) - patchJson.addProperty("hidden", patch.hidden()); - - patchesArray.add(patchJson); + patchesArray.add(patch.toJsonObject()); } json.add("patches", patchesArray); } @@ -579,7 +523,11 @@ public void setPatches(@Nullable List patches) { if (patches != null) { this.patches = List.copyOf(patches); if (rawJson != null) { - // TODO: set patches + JsonArray array = new JsonArray(this.patches.size()); + for (GameInstancePatch patch : this.patches) { + array.add(patch.toJsonObject()); + } + rawJson.add("patches", array); } } else { this.patches = null; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 17965b059b..e902e00985 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -17,12 +17,21 @@ */ package org.jackhuang.hmcl.game; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -52,6 +61,251 @@ public record GameInstancePatch( @Unmodifiable Map unknownFields ) { + public static GameInstancePatch fromJson(JsonObject json) throws JsonParseException { + @Nullable String id = null; + @Nullable String version = null; + @Nullable Integer priority = null; + @Nullable String minecraftArguments = null; + @Nullable Arguments arguments = null; + @Nullable String mainClass = null; + @Nullable String inheritsFrom = null; + @Nullable GameInstanceID jar = null; + @Nullable AssetIndexInfo assetIndex = null; + @Nullable String assets = null; + @Nullable Integer complianceLevel = null; + @Nullable GameJavaVersion javaVersion = null; + @Nullable List libraries = null; + @Nullable List compatibilityRules = null; + @Nullable Map downloads = null; + @Nullable Map logging = null; + @Nullable ReleaseType type = null; + @Nullable String time = null; + @Nullable String releaseTime = null; + @Nullable Integer minimumLauncherVersion = null; + @Nullable Boolean hidden = null; + Map unknownFields = new LinkedHashMap<>(); + + for (Map.Entry entry : json.entrySet()) { + String memberName = entry.getKey(); + JsonElement value = entry.getValue(); + + switch (memberName) { + case "id" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + id = primitive.getAsString(); + } + } + case "version" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + version = primitive.getAsString(); + } + } + case "priority" -> { + if (value instanceof JsonPrimitive primitive && primitive.isNumber()) { + priority = primitive.getAsInt(); + } + } + case "minecraftArguments" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + minecraftArguments = primitive.getAsString(); + } + } + case "arguments" -> arguments = JsonUtils.GSON.fromJson(value, Arguments.class); + case "mainClass" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + mainClass = primitive.getAsString(); + } + } + case "inheritsFrom" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + inheritsFrom = primitive.getAsString(); + } + } + case "jar" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + try { + jar = new GameInstanceID(primitive.getAsString()); + } catch (IllegalArgumentException ignored) { + } + } + } + case "assetIndex" -> assetIndex = JsonUtils.GSON.fromJson(value, AssetIndexInfo.class); + case "assets" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + assets = primitive.getAsString(); + } + } + case "complianceLevel" -> { + if (value instanceof JsonPrimitive primitive && primitive.isNumber()) { + complianceLevel = primitive.getAsInt(); + } + } + case "javaVersion" -> javaVersion = JsonUtils.GSON.fromJson(value, GameJavaVersion.class); + case "libraries" -> { + if (value instanceof JsonArray array) { + List list = new ArrayList<>(array.size()); + for (JsonElement element : array) { + if (element instanceof JsonObject object) { + list.add(Library.fromJson(object)); + } + } + libraries = List.copyOf(list); + } + } + case "compatibilityRules" -> { + if (value instanceof JsonArray array) { + List list = new ArrayList<>(array.size()); + for (JsonElement element : array) { + list.add(JsonUtils.GSON.fromJson(element, CompatibilityRule.class)); + } + compatibilityRules = List.copyOf(list); + } + } + case "downloads" -> { + if (value instanceof JsonObject object) { + Map map = new EnumMap<>(DownloadType.class); + for (Map.Entry downloadEntry : object.entrySet()) { + try { + DownloadType downloadType = DownloadType.valueOf(downloadEntry.getKey()); + map.put(downloadType, JsonUtils.GSON.fromJson(downloadEntry.getValue(), DownloadInfo.class)); + } catch (IllegalArgumentException ignored) { + } + } + downloads = Collections.unmodifiableMap(map); + } + } + case "logging" -> { + if (value instanceof JsonObject object) { + Map map = new EnumMap<>(DownloadType.class); + for (Map.Entry loggingEntry : object.entrySet()) { + try { + DownloadType downloadType = DownloadType.valueOf(loggingEntry.getKey()); + map.put(downloadType, JsonUtils.GSON.fromJson(loggingEntry.getValue(), LoggingInfo.class)); + } catch (IllegalArgumentException ignored) { + } + } + logging = Collections.unmodifiableMap(map); + } + } + case "type" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + try { + type = ReleaseType.valueOf(primitive.getAsString()); + } catch (IllegalArgumentException ignored) { + } + } + } + case "time" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + time = primitive.getAsString(); + } + } + case "releaseTime" -> { + if (value instanceof JsonPrimitive primitive && primitive.isString()) { + releaseTime = primitive.getAsString(); + } + } + case "minimumLauncherVersion" -> { + if (value instanceof JsonPrimitive primitive && primitive.isNumber()) { + minimumLauncherVersion = primitive.getAsInt(); + } + } + case "hidden" -> { + if (value instanceof JsonPrimitive primitive && primitive.isBoolean()) { + hidden = primitive.getAsBoolean(); + } + } + default -> unknownFields.put(memberName, value.deepCopy()); + } + } + + return new GameInstancePatch( + id, + version, + priority, + minecraftArguments, + arguments, + mainClass, + inheritsFrom, + jar, + assetIndex, + assets, + complianceLevel, + javaVersion, + libraries, + compatibilityRules, + downloads, + logging, + type, + time, + releaseTime, + minimumLauncherVersion, + hidden, + Collections.unmodifiableMap(unknownFields)); + } + + public JsonObject toJsonObject() { + JsonObject json = new JsonObject(); + for (Map.Entry entry : unknownFields.entrySet()) { + json.add(entry.getKey(), entry.getValue().deepCopy()); + } + + if (id != null) + json.addProperty("id", id); + if (version != null) + json.addProperty("version", version); + if (priority != null) + json.addProperty("priority", priority); + if (minecraftArguments != null) + json.addProperty("minecraftArguments", minecraftArguments); + if (arguments != null) + json.add("arguments", JsonUtils.GSON.toJsonTree(arguments)); + if (mainClass != null) + json.addProperty("mainClass", mainClass); + if (inheritsFrom != null) + json.addProperty("inheritsFrom", inheritsFrom); + if (jar != null) + json.addProperty("jar", jar.toString()); + if (assetIndex != null) + json.add("assetIndex", JsonUtils.GSON.toJsonTree(assetIndex)); + if (assets != null) + json.addProperty("assets", assets); + if (complianceLevel != null) + json.addProperty("complianceLevel", complianceLevel); + if (javaVersion != null) + json.add("javaVersion", JsonUtils.GSON.toJsonTree(javaVersion)); + if (libraries != null) + json.add("libraries", JsonUtils.GSON.toJsonTree(libraries)); + if (compatibilityRules != null) + json.add("compatibilityRules", JsonUtils.GSON.toJsonTree(compatibilityRules)); + if (downloads != null) { + JsonObject downloadsObject = new JsonObject(); + for (Map.Entry entry : downloads.entrySet()) { + downloadsObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + json.add("downloads", downloadsObject); + } + if (logging != null) { + JsonObject loggingObject = new JsonObject(); + for (Map.Entry entry : logging.entrySet()) { + loggingObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + json.add("logging", loggingObject); + } + if (type != null) + json.addProperty("type", type.name()); + if (time != null) + json.addProperty("time", time); + if (releaseTime != null) + json.addProperty("releaseTime", releaseTime); + if (minimumLauncherVersion != null) + json.addProperty("minimumLauncherVersion", minimumLauncherVersion); + if (hidden != null) + json.addProperty("hidden", hidden); + + return json; + } + GameInstanceManifest merge(GameInstanceManifest parent) { return new GameInstanceManifest( parent.id(), From 0189beda60b8a534bbf05a729549dfaf15bba18c Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 21:34:36 +0800 Subject: [PATCH 18/85] refactor: change nullable Boolean fields to primitive types in GameInstanceManifest and GameInstancePatch --- .../hmcl/game/DefaultGameRepository2.java | 4 ++-- .../hmcl/game/GameInstanceManifest.java | 22 +++++++++---------- .../hmcl/game/GameInstancePatch.java | 6 ++--- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index 363771b280..1c9ca47494 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -61,8 +61,8 @@ public class DefaultGameRepository2 implements GameRepository2 { null, null, 0, - null, - null, + false, + false, null, null ); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 3e59983dbb..4647e12f11 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -50,15 +50,15 @@ public record GameInstanceManifest( @Nullable String time, @Nullable String releaseTime, @Nullable Integer minimumLauncherVersion, - @Nullable Boolean root, - @Nullable Boolean hidden, + boolean root, + boolean hidden, @Nullable @Unmodifiable List patches, @Nullable @Unmodifiable JsonObject rawJson ) { /// A launch-ready manifest view with inheritance and pending patches applied. /// - /// @param manifest the final manifest data used by launch-time consumers + /// @param manifest the final manifest data used by launch-time consumers /// @param appliedPatches patches that have already been applied to produce the final manifest @NotNullByDefault public record Resolved(GameInstanceManifest manifest, @@ -141,7 +141,7 @@ public GameInstanceManifest(GameInstanceID id) { null, null, true, - null, + false, null, null); } @@ -316,7 +316,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } public boolean isRoot() { - return root != null && root; + return root; } public GameInstanceManifest withId(GameInstanceID id) { @@ -432,10 +432,10 @@ public JsonObject toJsonObject() { json.addProperty("releaseTime", releaseTime); if (minimumLauncherVersion != null) json.addProperty("minimumLauncherVersion", minimumLauncherVersion); - if (root != null) - json.addProperty("root", root); - if (hidden != null) - json.addProperty("hidden", hidden); + if (root) + json.addProperty("root", true); + if (hidden) + json.addProperty("hidden", true); if (patches != null) { JsonArray patchesArray = new JsonArray(patches.size()); for (GameInstancePatch patch : patches) { @@ -467,8 +467,8 @@ private static final class Builder { private @Nullable String time; private @Nullable String releaseTime; private @Nullable Integer minimumLauncherVersion; - private @Nullable Boolean root; - private @Nullable Boolean hidden; + private boolean root = false; + private boolean hidden = false; private @Nullable @Unmodifiable List patches; private @Nullable JsonObject rawJson; // @formatter:on diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index e902e00985..8393225fff 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -57,7 +57,7 @@ public record GameInstancePatch( @Nullable String time, @Nullable String releaseTime, @Nullable Integer minimumLauncherVersion, - @Nullable Boolean hidden, + boolean hidden, @Unmodifiable Map unknownFields ) { @@ -300,8 +300,8 @@ public JsonObject toJsonObject() { json.addProperty("releaseTime", releaseTime); if (minimumLauncherVersion != null) json.addProperty("minimumLauncherVersion", minimumLauncherVersion); - if (hidden != null) - json.addProperty("hidden", hidden); + if (hidden) + json.addProperty("hidden", true); return json; } From c8df614f33143c6bd1eceb956c956e5e07631544 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sun, 5 Jul 2026 21:36:54 +0800 Subject: [PATCH 19/85] refactor: replace nullable Boolean with primitive type and update unknownFields handling in GameInstancePatch --- .../hmcl/game/GameInstanceManifest.java | 2 +- .../jackhuang/hmcl/game/GameInstancePatch.java | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 4647e12f11..32dc19c2ae 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -377,7 +377,7 @@ public GameInstancePatch toPatch() { releaseTime, minimumLauncherVersion, true, - Map.of()); + null); } public JsonObject toJsonObject() { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 8393225fff..98b8e39a31 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -31,7 +31,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -58,7 +57,7 @@ public record GameInstancePatch( @Nullable String releaseTime, @Nullable Integer minimumLauncherVersion, boolean hidden, - @Unmodifiable Map unknownFields + @Nullable @Unmodifiable JsonObject rawJson ) { public static GameInstancePatch fromJson(JsonObject json) throws JsonParseException { @@ -82,8 +81,7 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept @Nullable String time = null; @Nullable String releaseTime = null; @Nullable Integer minimumLauncherVersion = null; - @Nullable Boolean hidden = null; - Map unknownFields = new LinkedHashMap<>(); + boolean hidden = false; for (Map.Entry entry : json.entrySet()) { String memberName = entry.getKey(); @@ -215,7 +213,6 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept hidden = primitive.getAsBoolean(); } } - default -> unknownFields.put(memberName, value.deepCopy()); } } @@ -241,15 +238,16 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept releaseTime, minimumLauncherVersion, hidden, - Collections.unmodifiableMap(unknownFields)); + json.deepCopy()); } public JsonObject toJsonObject() { - JsonObject json = new JsonObject(); - for (Map.Entry entry : unknownFields.entrySet()) { - json.add(entry.getKey(), entry.getValue().deepCopy()); + if (rawJson != null) { + return rawJson.deepCopy(); } + JsonObject json = new JsonObject(); + if (id != null) json.addProperty("id", id); if (version != null) From 3729ff57c364fb92be312ed0f6fcd209e9ff3b0a Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 02:33:49 +0800 Subject: [PATCH 20/85] refactor: change nullable Boolean fields to primitive types in GameInstanceManifest and GameInstancePatch --- .../hmcl/game/GameInstanceManifest.java | 20 ++++++++----------- .../hmcl/game/GameInstancePatch.java | 6 +++--- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 32dc19c2ae..ed9b69f75e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -17,11 +17,7 @@ */ package org.jackhuang.hmcl.game; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; +import com.google.gson.*; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jetbrains.annotations.NotNullByDefault; @@ -50,8 +46,8 @@ public record GameInstanceManifest( @Nullable String time, @Nullable String releaseTime, @Nullable Integer minimumLauncherVersion, - boolean root, - boolean hidden, + @Nullable Boolean root, + @Nullable Boolean hidden, @Nullable @Unmodifiable List patches, @Nullable @Unmodifiable JsonObject rawJson ) { @@ -219,7 +215,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t if (element instanceof JsonObject object) { list.add(Library.fromJson(object)); } else { - // TODO + throw new JsonParseException("Invalid library element: " + element); } } builder.libraries = List.copyOf(list); @@ -432,10 +428,10 @@ public JsonObject toJsonObject() { json.addProperty("releaseTime", releaseTime); if (minimumLauncherVersion != null) json.addProperty("minimumLauncherVersion", minimumLauncherVersion); - if (root) - json.addProperty("root", true); - if (hidden) - json.addProperty("hidden", true); + if (root != null) + json.addProperty("root", root); + if (hidden != null) + json.addProperty("hidden", hidden); if (patches != null) { JsonArray patchesArray = new JsonArray(patches.size()); for (GameInstancePatch patch : patches) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 98b8e39a31..f66fee0cf8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -56,7 +56,7 @@ public record GameInstancePatch( @Nullable String time, @Nullable String releaseTime, @Nullable Integer minimumLauncherVersion, - boolean hidden, + @Nullable Boolean hidden, @Nullable @Unmodifiable JsonObject rawJson ) { @@ -298,8 +298,8 @@ public JsonObject toJsonObject() { json.addProperty("releaseTime", releaseTime); if (minimumLauncherVersion != null) json.addProperty("minimumLauncherVersion", minimumLauncherVersion); - if (hidden) - json.addProperty("hidden", true); + if (hidden != null) + json.addProperty("hidden", hidden); return json; } From bc3a6378a3ca73ce764e14ada286d30356b58749 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 03:03:55 +0800 Subject: [PATCH 21/85] refactor: rename isRoot method to root in GameInstanceManifest and update related references --- .../hmcl/game/DefaultGameRepository2.java | 4 +-- .../hmcl/game/GameInstanceManifest.java | 35 +++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index 1c9ca47494..de4bef42ae 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -429,7 +429,7 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, GameInstanceManifest currentManifest; if (manifest.inheritsFrom() == null) { - if (manifest.isRoot()) { + if (manifest.root()) { // TODO: Breaking change, require much testing on versions installed with external installer, other launchers, and all kinds of versions. currentManifest = manifest.patches() != null ? new GameInstanceManifest(manifest.id()).withPatches(manifest.patches()) : manifest; } else { @@ -470,7 +470,7 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, private GameInstanceManifest resolvePreservingPatches(GameInstanceManifest manifest, Set resolvedSoFar) throws NoSuchGameInstanceException { - GameInstanceManifest currentManifest = manifest.isRoot() + GameInstanceManifest currentManifest = manifest.root() ? manifest : addPatches( addPatches(new GameInstanceManifest(manifest.id()), Collections.singleton(manifest.toPatch())), diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index ed9b69f75e..37a8b7ad22 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -18,15 +18,20 @@ package org.jackhuang.hmcl.game; import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; +import java.io.IOException; import java.util.*; @NotNullByDefault +@JsonAdapter(GameInstanceManifest.Adapter.class) public record GameInstanceManifest( GameInstanceID id, @Nullable String minecraftArguments, @@ -311,8 +316,9 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t return builder.toManifest(); } - public boolean isRoot() { - return root; + @Override + public Boolean root() { + return root != null && root; } public GameInstanceManifest withId(GameInstanceID id) { @@ -564,4 +570,29 @@ GameInstanceManifest toManifest() { ); } } + + static final class Adapter extends TypeAdapter<@Nullable GameInstanceManifest> { + + @Override + public @Nullable GameInstanceManifest read(JsonReader in) { + JsonElement jsonElement = JsonParser.parseReader(in); + if (jsonElement.isJsonNull()) { + return null; + } + + if (jsonElement instanceof JsonObject jsonObject) { + return GameInstanceManifest.fromJson(jsonObject, false); + } + + throw new JsonParseException("Expected JsonObject but got " + jsonElement.getClass().getName()); + } + + @Override + public void write(JsonWriter out, @Nullable GameInstanceManifest value) throws IOException { + if (value != null) + JsonUtils.GSON.toJson(value.toJsonObject(), out); + else + out.nullValue(); + } + } } From 116da19b811af438d423087d48175ba2e7cc53c5 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 03:18:17 +0800 Subject: [PATCH 22/85] feat: enhance game version handling with event-driven refresh and instance renaming support --- .../hmcl/game/DefaultGameRepository2.java | 215 +++++++++++++++--- .../hmcl/game/GameInstanceManifest.java | 29 ++- .../hmcl/game/GameInstancePatch.java | 4 +- .../hmcl/game/GameInstanceManifestTest.java | 121 ++++++++++ 4 files changed, 336 insertions(+), 33 deletions(-) create mode 100644 HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index de4bef42ae..e409f08b3a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -18,9 +18,10 @@ package org.jackhuang.hmcl.game; import com.google.gson.JsonParseException; -import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.event.*; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.Platform; @@ -30,8 +31,10 @@ import java.io.IOException; import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -82,6 +85,7 @@ private static boolean hasClassicVersion(Path baseDirectory) { } private volatile Status status; + private final ConcurrentHashMap> gameVersions = new ConcurrentHashMap<>(); public DefaultGameRepository2(Path baseDirectory) { this.status = new Status(baseDirectory); @@ -93,6 +97,15 @@ public Path getBaseDirectory() { @Override public void refresh() { + if (EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this)) == Event.Result.DENY) { + return; + } + + refreshImpl(); + EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this)); + } + + private void refreshImpl() { Status newStatus = new Status(status.baseDirectory); if (hasClassicVersion(newStatus.baseDirectory)) { @@ -104,26 +117,69 @@ public void refresh() { if (Files.isDirectory(versionsDir)) { try (Stream stream = Files.list(versionsDir)) { stream.parallel().filter(Files::isDirectory).flatMap(dir -> { - GameInstanceID id = new GameInstanceID(FileUtils.getName(dir)); + GameInstanceID id; + try { + id = new GameInstanceID(FileUtils.getName(dir)); + } catch (IllegalArgumentException e) { + LOG.warning("Ignoring version folder with invalid id " + dir, e); + return Stream.empty(); + } + Path json = dir.resolve(id + ".json"); if (Files.notExists(json)) { - return Stream.empty(); + List jsons = FileUtils.listFilesByExtension(dir, "json"); + if (jsons.size() == 1) { + LOG.info("Renaming json file " + jsons.get(0) + " to " + json); + + try { + Files.move(jsons.get(0), json); + } catch (IOException e) { + LOG.warning("Cannot rename json file, ignoring version " + id, e); + return Stream.empty(); + } + + Path jar = dir.resolve(FileUtils.getNameWithoutExtension(jsons.get(0)) + ".jar"); + if (Files.exists(jar)) { + try { + Files.move(jar, dir.resolve(id + ".jar")); + } catch (IOException e) { + LOG.warning("Cannot rename jar file, ignoring version " + id, e); + return Stream.empty(); + } + } + } else { + LOG.info("No available json file found, ignoring version " + id); + return Stream.empty(); + } } GameInstanceManifest manifest; try { - manifest = JsonUtils.fromJsonFile(json, GameInstanceManifest.class); - if (manifest == null) - throw new JsonParseException("Manifest is null"); + manifest = readInstanceManifest(json); } catch (Exception e) { LOG.warning("Malformed version json " + id, e); - return Stream.empty(); + if (EventBus.EVENT_BUS.fireEvent(new GameJsonParseFailedEvent(this, json, id.id())) != Event.Result.ALLOW) { + return Stream.empty(); + } + + try { + manifest = readInstanceManifest(json); + } catch (Exception e2) { + LOG.error("User corrected version json is still malformed", e2); + return Stream.empty(); + } } if (!id.equals(manifest.id())) { - // TODO - return Stream.empty(); + try { + moveInstanceFiles(newStatus.baseDirectory, id, manifest.id()); + } catch (IOException e) { + LOG.warning("Ignoring version " + manifest.id() + + " because version id does not match folder name " + id + + ", and we cannot correct it.", e); + return Stream.empty(); + } } return Stream.of(manifest); @@ -134,6 +190,59 @@ public void refresh() { LOG.warning("Failed to load versions from " + versionsDir, e); } } + + Map loadedInstances = new TreeMap<>(); + for (InstanceHolder holder : newStatus.instances.values()) { + try { + GameInstanceManifest resolved = newStatus.resolve(holder.manifest, new HashSet<>()); + if (CompatibilityRule.appliesToCurrentEnvironment(resolved.compatibilityRules())) { + loadedInstances.put(holder.id, holder); + } + } catch (NoSuchGameInstanceException e) { + LOG.warning("Ignoring version " + holder.id + " because it inherits from a nonexistent version."); + } + } + + newStatus.instances.clear(); + newStatus.instances.putAll(loadedInstances); + gameVersions.clear(); + this.status = newStatus; + } + + private static GameInstanceManifest readInstanceManifest(Path json) throws IOException, JsonParseException { + GameInstanceManifest manifest = JsonUtils.fromJsonFile(json, GameInstanceManifest.class); + if (manifest == null) { + throw new JsonParseException("Manifest is null"); + } + return manifest; + } + + private static void moveInstanceFiles(Path baseDirectory, GameInstanceID from, GameInstanceID to) throws IOException { + Path versionsDir = baseDirectory.resolve("versions"); + Path fromDir = versionsDir.resolve(from.id()); + Path toDir = versionsDir.resolve(to.id()); + Files.move(fromDir, toDir); + + Path fromJson = toDir.resolve(from + ".json"); + Path fromJar = toDir.resolve(from + ".jar"); + Path toJson = toDir.resolve(to + ".json"); + Path toJar = toDir.resolve(to + ".jar"); + + boolean hasJarFile = Files.exists(fromJar); + + try { + Files.move(fromJson, toJson); + if (hasJarFile) { + Files.move(fromJar, toJar); + } + } catch (IOException e) { + Lang.ignoringException(() -> Files.move(toJson, fromJson)); + if (hasJarFile) { + Lang.ignoringException(() -> Files.move(toJar, fromJar)); + } + Lang.ignoringException(() -> Files.move(toDir, fromDir)); + throw e; + } } @Override @@ -198,7 +307,7 @@ public Path getLibraryFile(GameInstanceManifest manifest, Library lib) { return getLibrariesDirectory(manifest).resolve(lib.getPath()); } - public Path getArtifactFile(Version version, Artifact artifact) { + public Path getArtifactFile(GameInstanceManifest manifest, Artifact artifact) { return artifact.getPath(getBaseDirectory().resolve("libraries")); } @@ -209,27 +318,76 @@ public Path getRunDirectory(GameInstanceID instanceId) { @Override public Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceException { - GameInstanceManifest manifest = getResolvedInstanceManifest(instanceId).manifest(); - - GameInstanceID jarID = Objects.requireNonNullElse(manifest.jar(), manifest.id()); - return getInstanceRoot(instanceId).resolve(jarID + ".jar"); + return getInstanceJar(getResolvedInstanceManifest(instanceId).manifest()); } @Override public Path getInstanceJar(GameInstanceManifest manifest) { GameInstanceID jarID = Objects.requireNonNullElse(manifest.jar(), manifest.id()); - return getInstanceRoot(manifest.id()).resolve(jarID + ".jar"); + return getInstanceRoot(jarID).resolve(jarID + ".jar"); } @Override public boolean renameInstance(GameInstanceID from, GameInstanceID to) { - return false; + if (EventBus.EVENT_BUS.fireEvent(new RenameVersionEvent(this, from.id(), to.id())) == Event.Result.DENY) { + return false; + } + + try { + Status currentStatus = status; + InstanceHolder fromHolder = currentStatus.instances.get(from); + if (fromHolder == null) { + throw new NoSuchGameInstanceException(from); + } + + moveInstanceFiles(currentStatus.baseDirectory, from, to); + + GameInstanceManifest renamedManifest = fromHolder.manifest; + if (from.equals(renamedManifest.jar())) { + renamedManifest = renamedManifest.withJar(null); + } + renamedManifest = renamedManifest.withId(to); + JsonUtils.writeToJsonFile(getInstanceJson(to), renamedManifest); + + Map updatedInstances = new TreeMap<>(currentStatus.instances); + updatedInstances.remove(from); + updatedInstances.put(to, new InstanceHolder(currentStatus, to, renamedManifest)); + + for (InstanceHolder holder : currentStatus.instances.values()) { + GameInstanceManifest manifest = holder.manifest; + if (from.equals(manifest.inheritsFrom())) { + GameInstanceManifest updatedManifest = manifest.withInheritsFrom(to); + Path targetPath = getInstanceJson(updatedManifest.id()); + Files.createDirectories(targetPath.getParent()); + JsonUtils.writeToJsonFile(targetPath, updatedManifest); + updatedInstances.put(updatedManifest.id(), new InstanceHolder(currentStatus, updatedManifest.id(), updatedManifest)); + } + } + + currentStatus.instances.clear(); + currentStatus.instances.putAll(updatedInstances); + gameVersions.clear(); + return true; + } catch (IOException | JsonParseException | NoSuchGameInstanceException | InvalidPathException e) { + LOG.warning("Unable to rename version " + from + " to " + to, e); + return false; + } } @Override public Optional getGameVersion(GameInstanceManifest manifest) { try { - return getGameVersion(manifest.id()); + GameInstanceManifest resolved = resolve(manifest).manifest(); + Path instanceJar = getInstanceJar(resolved); + return gameVersions.computeIfAbsent(instanceJar, jar -> { + Optional gameVersion = GameVersion.minecraftVersion(jar); + if (gameVersion.isEmpty()) { + LOG.warning("Cannot find out game version of " + manifest.id() + + ", primary jar: " + jar + + ", jar exists: " + Files.exists(jar)); + } + return gameVersion; + }); } catch (NoSuchGameInstanceException e) { return Optional.empty(); } @@ -355,12 +513,16 @@ protected Path reconstructAssets(GameInstanceID instanceId, String assetId) thro } public Task saveAsync(GameInstanceManifest instanceManifest) { - throw new UnsupportedOperationException("TODO"); -// if (instanceManifest.isResolvedPreservingPatches()) { -// return new VersionJsonSaveTask(this, MaintainTask.maintainPreservingPatches(this, instanceManifest)); -// } else { -// return new VersionJsonSaveTask(this, instanceManifest); -// } + return Task.supplyAsync(() -> { + Path json = getInstanceJson(instanceManifest.id()).toAbsolutePath(); + Files.createDirectories(json.getParent()); + JsonUtils.writeToJsonFile(json, instanceManifest); + + Status currentStatus = status; + currentStatus.instances.put(instanceManifest.id(), new InstanceHolder(currentStatus, instanceManifest.id(), instanceManifest)); + gameVersions.clear(); + return instanceManifest; + }); } public Path getModpackConfiguration(GameInstanceID instanceId) { @@ -379,11 +541,6 @@ public boolean isModpack(GameInstanceID instanceId) { return Files.exists(getModpackConfiguration(instanceId)); } - public ModManager getModManager(GameInstanceID instanceId) { - throw new UnsupportedOperationException("TODO"); - // TODO: // return new ModManager(this, instanceId); - } - public Path getSavesDirectory(GameInstanceID instanceId) { return getRunDirectory(instanceId).resolve("saves"); } @@ -435,7 +592,7 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, } else { currentManifest = manifest; } - currentManifest = manifest.jar() == null ? manifest.withJar(manifest.id()) : currentManifest.withJar(manifest.jar()); + currentManifest = currentManifest.withJar(manifest.jar() == null ? manifest.id() : manifest.jar()); } else { // To maximize the compatibility. if (!resolvedSoFar.add(manifest.id())) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 37a8b7ad22..889b3fca3e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -343,6 +343,20 @@ public GameInstanceManifest withJar(@Nullable GameInstanceID jar) { return builder.toManifest(); } + /// Returns a manifest copy with the given parent instance id. + /// + /// @param inheritsFrom the parent instance id, or `null` if this manifest is independent + /// @return a manifest with the requested parent instance id + public GameInstanceManifest withInheritsFrom(@Nullable GameInstanceID inheritsFrom) { + if (Objects.equals(this.inheritsFrom, inheritsFrom)) { + return this; + } + + Builder builder = new Builder(this); + builder.setInheritsFrom(inheritsFrom); + return builder.toManifest(); + } + public GameInstanceManifest withPatches(@Nullable List patches) { if (patches == this.patches) { return this; @@ -469,8 +483,8 @@ private static final class Builder { private @Nullable String time; private @Nullable String releaseTime; private @Nullable Integer minimumLauncherVersion; - private boolean root = false; - private boolean hidden = false; + private @Nullable Boolean root; + private @Nullable Boolean hidden; private @Nullable @Unmodifiable List patches; private @Nullable JsonObject rawJson; // @formatter:on @@ -521,6 +535,17 @@ public void setJar(@Nullable GameInstanceID jar) { } } + public void setInheritsFrom(@Nullable GameInstanceID inheritsFrom) { + this.inheritsFrom = inheritsFrom; + if (rawJson != null) { + if (inheritsFrom != null) { + rawJson.addProperty("inheritsFrom", inheritsFrom.toString()); + } else { + rawJson.remove("inheritsFrom"); + } + } + } + public void setPatches(@Nullable List patches) { if (patches != null) { this.patches = List.copyOf(patches); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index f66fee0cf8..67afe2249f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -81,7 +81,7 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept @Nullable String time = null; @Nullable String releaseTime = null; @Nullable Integer minimumLauncherVersion = null; - boolean hidden = false; + @Nullable Boolean hidden = null; for (Map.Entry entry : json.entrySet()) { String memberName = entry.getKey(); @@ -324,8 +324,8 @@ GameInstanceManifest merge(GameInstanceManifest parent) { time == null ? parent.time() : time, releaseTime == null ? parent.releaseTime() : releaseTime, Lang.merge(minimumLauncherVersion, parent.minimumLauncherVersion(), Math::max), - hidden, true, + hidden, parent.patches(), null); } diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java new file mode 100644 index 0000000000..c5d33ee4c1 --- /dev/null +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java @@ -0,0 +1,121 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import com.google.gson.JsonObject; +import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; + +/// Tests for game instance manifest parsing and resolution behavior. +@NotNullByDefault +public final class GameInstanceManifestTest { + + /// Ordinary manifest edits preserve absent nullable Boolean fields. + @Test + public void testNullableBooleanFieldsAreNotMaterialized() { + GameInstanceManifest manifest = manifest("example", null, null, null, null) + .withJar(new GameInstanceID("parent")); + + JsonObject json = manifest.toJsonObject(); + assertFalse(json.has("root")); + assertFalse(json.has("hidden")); + } + + /// Root manifests with patch lists resolve from the patch view instead of their own body fields. + @Test + public void testRootManifestWithPatchesUsesPatchView() throws NoSuchGameInstanceException { + GameInstanceManifest manifest = manifest( + "example", + "example.Main", + true, + false, + List.of(patch("patch", null))); + + GameInstanceManifest.Resolved resolved = new DefaultGameRepository2(Path.of(".")).resolve(manifest); + + assertNull(resolved.manifest().mainClass()); + assertEquals(List.of(patch("patch", null)), resolved.appliedPatches()); + } + + /// Creates a minimal manifest for tests. + private static GameInstanceManifest manifest( + String id, + @Nullable String mainClass, + @Nullable Boolean root, + @Nullable Boolean hidden, + @Nullable List patches) { + return new GameInstanceManifest( + new GameInstanceID(id), + null, + null, + mainClass, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + root, + hidden, + patches, + null); + } + + /// Creates a minimal patch for tests. + private static GameInstancePatch patch(String id, @Nullable String mainClass) { + return new GameInstancePatch( + id, + null, + 0, + null, + null, + mainClass, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null); + } +} From 7efab768783ecd708e7caca3cc42fb3d32caf421 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 11:48:20 +0800 Subject: [PATCH 23/85] refactor: update GameRepository references to GameRepository2 and adjust related types --- .../jackhuang/hmcl/game/HMCLGameLauncher.java | 6 +- .../hmcl/game/HMCLGameRepository.java | 23 +- .../hmcl/game/HMCLModpackInstallTask.java | 4 +- .../hmcl/game/HMCLModpackProvider.java | 2 +- .../jackhuang/hmcl/game/LauncherHelper.java | 15 +- .../org/jackhuang/hmcl/game/LogExporter.java | 4 +- .../org/jackhuang/hmcl/java/JavaManager.java | 6 +- .../jackhuang/hmcl/setting/GameSettings.java | 2 +- .../jackhuang/hmcl/ui/GameCrashWindow.java | 6 +- .../ui/download/AdditionalInstallersPage.java | 11 +- .../UpdateInstallerWizardProvider.java | 8 +- .../hmcl/ui/game/GameSettingsPage.java | 2 +- .../org/jackhuang/hmcl/ui/main/MainPage.java | 8 +- .../org/jackhuang/hmcl/ui/main/RootPage.java | 6 +- .../hmcl/ui/versions/DownloadListPage.java | 4 +- .../hmcl/ui/versions/DownloadPage.java | 4 +- .../hmcl/ui/versions/GameListPopupMenu.java | 4 +- .../hmcl/ui/versions/InstallerListPage.java | 4 +- .../hmcl/ui/versions/ModListPage.java | 4 +- .../jackhuang/hmcl/ui/versions/Versions.java | 10 +- .../jackhuang/hmcl/util/NativePatcher.java | 12 +- .../hmcl/ui/GameCrashWindowTest.java | 4 +- .../hmcl/addon/LocalAddonManager.java | 8 +- .../jackhuang/hmcl/addon/mod/ModManager.java | 11 +- .../resourcepack/ResourcePackManager.java | 4 +- .../download/DefaultDependencyManager.java | 54 +- .../hmcl/download/DefaultGameBuilder.java | 6 +- .../hmcl/download/DependencyManager.java | 16 +- .../hmcl/download/LibraryAnalyzer.java | 77 ++- .../jackhuang/hmcl/download/MaintainTask.java | 32 +- .../hmcl/download/RemoteVersion.java | 5 +- .../cleanroom/CleanroomInstallTask.java | 15 +- .../cleanroom/CleanroomRemoteVersion.java | 5 +- .../download/fabric/FabricAPIInstallTask.java | 9 +- .../fabric/FabricAPIRemoteVersion.java | 5 +- .../download/fabric/FabricInstallTask.java | 15 +- .../download/fabric/FabricRemoteVersion.java | 5 +- .../download/forge/ForgeInstallProfile.java | 8 +- .../hmcl/download/forge/ForgeInstallTask.java | 19 +- .../download/forge/ForgeNewInstallTask.java | 28 +- .../download/forge/ForgeOldInstallTask.java | 18 +- .../download/forge/ForgeRemoteVersion.java | 5 +- .../download/game/GameAssetDownloadTask.java | 8 +- .../game/GameAssetIndexDownloadTask.java | 8 +- .../hmcl/download/game/GameDownloadTask.java | 6 +- .../hmcl/download/game/GameInstallTask.java | 22 +- .../hmcl/download/game/GameLibrariesTask.java | 24 +- .../hmcl/download/game/GameRemoteVersion.java | 5 +- .../game/GameVerificationFixTask.java | 10 +- .../download/game/VersionJsonSaveTask.java | 58 -- .../LegacyFabricAPIInstallTask.java | 9 +- .../LegacyFabricAPIRemoteVersion.java | 5 +- .../legacyfabric/LegacyFabricInstallTask.java | 15 +- .../LegacyFabricRemoteVersion.java | 5 +- .../liteloader/LiteLoaderInstallTask.java | 10 +- .../liteloader/LiteLoaderRemoteVersion.java | 5 +- .../neoforge/NeoForgeInstallTask.java | 13 +- .../neoforge/NeoForgeOldInstallTask.java | 23 +- .../neoforge/NeoForgeRemoteVersion.java | 5 +- .../optifine/OptiFineInstallTask.java | 18 +- .../optifine/OptiFineRemoteVersion.java | 5 +- .../download/quilt/QuiltAPIInstallTask.java | 9 +- .../download/quilt/QuiltAPIRemoteVersion.java | 5 +- .../hmcl/download/quilt/QuiltInstallTask.java | 15 +- .../download/quilt/QuiltRemoteVersion.java | 5 +- .../hmcl/event/GameJsonParseFailedEvent.java | 2 +- .../hmcl/event/LoadedOneVersionEvent.java | 60 -- .../hmcl/event/RefreshedVersionsEvent.java | 2 +- .../hmcl/event/RefreshingVersionsEvent.java | 2 +- .../hmcl/event/RemoveVersionEvent.java | 2 +- .../hmcl/event/RenameVersionEvent.java | 2 +- .../jackhuang/hmcl/game/ClassicVersion.java | 52 -- .../hmcl/game/DefaultGameRepository.java | 564 ------------------ .../hmcl/game/DefaultGameRepository2.java | 105 +++- .../hmcl/game/GameInstanceManifest.java | 432 ++++++++++++++ .../hmcl/game/GameInstancePatch.java | 277 +++++++++ .../jackhuang/hmcl/game/GameRepository.java | 276 --------- .../jackhuang/hmcl/game/GameRepository2.java | 159 +++++ .../hmcl/game/JavaVersionConstraint.java | 44 +- .../game/NoSuchGameInstanceException.java | 2 +- .../hmcl/game/SimpleVersionProvider.java | 51 -- .../java/org/jackhuang/hmcl/game/Version.java | 512 ---------------- .../jackhuang/hmcl/game/VersionJsonType.java | 23 - .../hmcl/game/VersionLibraryBuilder.java | 8 +- .../hmcl/game/VersionNotFoundException.java | 36 -- .../jackhuang/hmcl/game/VersionProvider.java | 43 -- .../hmcl/game/tlauncher/TLauncherLibrary.java | 73 --- .../hmcl/game/tlauncher/TLauncherVersion.java | 109 ---- .../hmcl/launch/DefaultLauncher.java | 6 +- .../org/jackhuang/hmcl/launch/Launcher.java | 14 +- .../hmcl/modpack/ModpackUpdateTask.java | 6 +- .../modpack/curse/CurseCompletionTask.java | 4 +- .../hmcl/modpack/curse/CurseInstallTask.java | 4 +- .../mcbbs/McbbsModpackCompletionTask.java | 4 +- .../modpack/mcbbs/McbbsModpackExportTask.java | 6 +- .../mcbbs/McbbsModpackLocalInstallTask.java | 15 +- .../mcbbs/McbbsModpackRemoteInstallTask.java | 4 +- .../modrinth/ModrinthCompletionTask.java | 4 +- .../modpack/modrinth/ModrinthInstallTask.java | 4 +- .../modrinth/ModrinthModpackExportTask.java | 6 +- .../modpack/multimc/MultiMCInstancePatch.java | 15 +- .../multimc/MultiMCModpackExportTask.java | 6 +- .../multimc/MultiMCModpackInstallTask.java | 8 +- .../server/ServerModpackCompletionTask.java | 4 +- .../server/ServerModpackExportTask.java | 6 +- .../server/ServerModpackLocalInstallTask.java | 4 +- .../ServerModpackRemoteInstallTask.java | 4 +- 107 files changed, 1467 insertions(+), 2275 deletions(-) delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/event/LoadedOneVersionEvent.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/ClassicVersion.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/SimpleVersionProvider.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionJsonType.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionNotFoundException.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionProvider.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/tlauncher/TLauncherLibrary.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/tlauncher/TLauncherVersion.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java index 75fba03545..6250fdd97c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java @@ -42,15 +42,15 @@ */ public final class HMCLGameLauncher extends DefaultLauncher { - public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) { + public HMCLGameLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { this(repository, version, authInfo, options, null); } - public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { + public HMCLGameLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { this(repository, version, authInfo, options, listener, true); } - public HMCLGameLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { + public HMCLGameLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { super(repository, version, authInfo, options, listener, daemon); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index b250406fa2..0e154406a0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -75,7 +75,7 @@ /// HMCL game repository implementation backed by a GameDirectory and per-instance game settings. @NotNullByDefault -public final class HMCLGameRepository extends DefaultGameRepository { +public final class HMCLGameRepository extends DefaultGameRepository2 { /// References an optional game instance in a repository. /// /// @param repository the owning game repository @@ -201,24 +201,24 @@ private String getSelectedRunningDirectory( return Objects.requireNonNullElse(parent.runningDirectoryProperty().getValue(), ""); } - public Stream getDisplayVersions() { + public Stream getDisplayVersions() { return getVersions().stream() .filter(v -> !v.isHidden()) - .sorted(Comparator.comparing((Version v) -> Lang.requireNonNullElse(v.getReleaseTime(), Instant.EPOCH)) + .sorted(Comparator.comparing((GameInstanceManifest v) -> Lang.requireNonNullElse(v.getReleaseTime(), Instant.EPOCH)) .thenComparing(v -> VersionNumber.asVersion(v.getId()))); } @Override - protected void refreshVersionsImpl() { + protected void refreshImpl() { instanceGameSettings.clear(); loadedInstanceGameSettings.clear(); readOnlyInstanceGameSettings.clear(); - super.refreshVersionsImpl(); - versions.keySet().forEach(this::loadInstanceGameSettings); + super.refreshImpl(); + getVersions().stream().map(GameInstanceManifest::getId).forEach(this::loadInstanceGameSettings); try { Path file = getBaseDirectory().resolve("launcher_profiles.json"); - if (!Files.exists(file) && !versions.isEmpty()) { + if (!Files.exists(file) && !getVersions().isEmpty()) { Files.createDirectories(file.getParent()); Files.writeString(file, PROFILE); } @@ -246,7 +246,7 @@ public void duplicateVersion(String srcId, String dstId, boolean copySaves) thro Path srcDir = getVersionRoot(srcId); Path dstDir = getVersionRoot(dstId); - Version fromVersion = getVersion(srcId); + GameInstanceManifest fromVersion = getVersion(srcId); List blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); blackList.add(srcId + ".jar"); @@ -591,7 +591,7 @@ public Image getVersionIconImage(@Nullable String id) { VersionIconType iconType = setting != null ? Lang.requireNonNullElse(setting.iconProperty().getValue(), VersionIconType.DEFAULT) : VersionIconType.DEFAULT; if (iconType == VersionIconType.DEFAULT) { - Version version = getVersion(id).resolve(this); + GameInstanceManifest version = getVersion(id).resolve(this); Optional iconFile = getVersionIconFile(id); if (iconFile.isPresent()) { try { @@ -827,14 +827,15 @@ public static boolean isValidVersionId(String id) { public boolean versionIdConflicts(String id) { if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { // on Windows, filenames are case-insensitive - for (String existingId : versions.keySet()) { + for (GameInstanceManifest manifest : getVersions()) { + String existingId = manifest.getId(); if (existingId.equalsIgnoreCase(id)) { return true; } } return false; } else { - return versions.containsKey(id); + return hasVersion(id); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java index e7de3f914c..1d759187c2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java @@ -89,9 +89,9 @@ public List> getDependents() { @Override public void execute() throws Exception { String json = CompressingUtils.readTextZipEntry(zipFile, "minecraft/pack.json"); - Version originalVersion = JsonUtils.GSON.fromJson(json, Version.class).setId(name).setJar(null); + GameInstanceManifest originalVersion = JsonUtils.GSON.fromJson(json, GameInstanceManifest.class).setId(name).setJar(null); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(originalVersion, null); - Task libraryTask = Task.supplyAsync(() -> originalVersion); + Task libraryTask = Task.supplyAsync(() -> originalVersion); // reinstall libraries // libraries of Forge and OptiFine should be obtained by installation. for (LibraryAnalyzer.LibraryMark mark : analyzer) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java index 257f39ce78..a2ee651483 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java @@ -63,7 +63,7 @@ public Modpack readManifest(ZipArchiveReader file, Path path, Charset encoding) String manifestJson = CompressingUtils.readTextZipEntry(file, "modpack.json"); Modpack manifest = JsonUtils.fromNonNullJson(manifestJson, HMCLModpack.class).setEncoding(encoding); String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json"); - Version game = JsonUtils.fromNonNullJson(gameJson, Version.class); + GameInstanceManifest game = JsonUtils.fromNonNullJson(gameJson, GameInstanceManifest.class); if (game.getJar() == null) if (StringUtils.isBlank(manifest.getVersion())) throw new JsonParseException("Cannot recognize the game version of modpack " + file + "."); 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 3e7ef72219..d27bb85fac 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -148,7 +148,7 @@ private void launch0() { PROCESSES.removeIf(it -> it.get() == null); DefaultDependencyManager dependencyManager = repository.getDependency(); - AtomicReference version = new AtomicReference<>(MaintainTask.maintain(repository, repository.getResolvedVersion(selectedVersion))); + AtomicReference version = new AtomicReference<>(MaintainTask.maintain(repository, repository.getResolvedVersion(selectedVersion))); Optional gameVersion = repository.getGameVersion(version.get()); boolean integrityCheck = repository.unmarkVersionLaunchedAbnormally(selectedVersion); CountDownLatch launchingLatch = new CountDownLatch(1); @@ -202,7 +202,12 @@ private void launch0() { }) ); }).withStage("launch.state.dependencies") - .thenComposeAsync(() -> gameVersion.map(s -> new GameVerificationFixTask(dependencyManager, s, version.get())).orElse(null)) + .thenComposeAsync(() -> { + if (gameVersion.isEmpty()) { + return null; + } + return new GameVerificationFixTask(dependencyManager, gameVersion.get(), version.get()); + }) .thenComposeAsync(() -> { if (setting.getInheritable(GameSettings::allowAutoAgentProperty) || setting.getInheritable(GameSettings::noJVMOptionsProperty) @@ -386,7 +391,7 @@ public void onStop(boolean success, TaskExecutor executor) { executor.start(); } - private static Task checkGameState(HMCLGameRepository repository, GameSettings.Effective setting, Version version) { + private static Task checkGameState(HMCLGameRepository repository, GameSettings.Effective setting, GameInstanceManifest version) { LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version, repository.getGameVersion(version).orElse(null)); GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(analyzer.getVersion(LibraryAnalyzer.LibraryType.MINECRAFT)); @@ -798,7 +803,7 @@ private final class HMCLProcessListener implements ProcessListener { private final ReentrantLock lock = new ReentrantLock(); private final HMCLGameRepository repository; - private final Version version; + private final GameInstanceManifest version; private final LaunchOptions launchOptions; private ManagedProcess process; private volatile boolean lwjgl; @@ -810,7 +815,7 @@ private final class HMCLProcessListener implements ProcessListener { private Thread submitLogThread; private LinkedBlockingQueue logBuffer; - public HMCLProcessListener(HMCLGameRepository repository, Version version, AuthInfo authInfo, LaunchOptions launchOptions, CountDownLatch launchingLatch, boolean detectWindow) { + public HMCLProcessListener(HMCLGameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions launchOptions, CountDownLatch launchingLatch, boolean detectWindow) { this.repository = repository; this.version = version; this.launchOptions = launchOptions; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java index 3ace4df057..d0249876d8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java @@ -42,7 +42,7 @@ private LogExporter() { } public static CompletableFuture exportLogs( - Path zipFile, DefaultGameRepository gameRepository, String versionId, String logs, String launchScript, + Path zipFile, DefaultGameRepository2 gameRepository, String versionId, String logs, String launchScript, PathMatcher logMatcher) { Path runDirectory = gameRepository.getRunDirectory(versionId); Path baseDirectory = gameRepository.getBaseDirectory(); @@ -53,7 +53,7 @@ public static CompletableFuture exportLogs( while (true) { if (resolvedSoFar.contains(currentVersionId)) break; resolvedSoFar.add(currentVersionId); - Version currentVersion = gameRepository.getVersion(currentVersionId); + GameInstanceManifest currentVersion = gameRepository.getVersion(currentVersionId); versions.add(currentVersionId); if (StringUtils.isNotBlank(currentVersion.getInheritsFrom())) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 3d1c11b8bb..278cc57108 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -29,7 +29,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.GameJavaVersion; import org.jackhuang.hmcl.game.JavaVersionConstraint; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.setting.SettingsManager; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; @@ -315,12 +315,12 @@ private static JavaRuntime chooseJava(@Nullable JavaRuntime java1, JavaRuntime j } @Nullable - public static JavaRuntime findSuitableJava(GameVersionNumber gameVersion, Version version) throws InterruptedException { + public static JavaRuntime findSuitableJava(GameVersionNumber gameVersion, GameInstanceManifest version) throws InterruptedException { return findSuitableJava(getAllJava(), gameVersion, version); } @Nullable - public static JavaRuntime findSuitableJava(Collection javaRuntimes, GameVersionNumber gameVersion, Version version) { + public static JavaRuntime findSuitableJava(Collection javaRuntimes, GameVersionNumber gameVersion, GameInstanceManifest version) { LibraryAnalyzer analyzer = version != null ? LibraryAnalyzer.analyze(version, gameVersion != null ? gameVersion.toString() : null) : null; boolean forceX86 = Architecture.SYSTEM_ARCH == Architecture.ARM64 diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java index 81fdd30229..60a2c57c45 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java @@ -916,7 +916,7 @@ public void setJavaAutoSelected() { } /// Finds the effective Java runtime. - public @Nullable JavaRuntime getJava(@Nullable GameVersionNumber gameVersion, @Nullable Version version) throws InterruptedException { + public @Nullable JavaRuntime getJava(@Nullable GameVersionNumber gameVersion, @Nullable GameInstanceManifest version) throws InterruptedException { JavaVersionType javaVersionType = getInheritable(GameSettings::javaTypeProperty); switch (javaVersionType) { case AUTO: diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java index 1558ee6295..1b5bf3ada2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java @@ -73,7 +73,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class GameCrashWindow extends Stage { - private final Version version; + private final GameInstanceManifest version; private final String memory; private final String total_memory; private final String java; @@ -83,7 +83,7 @@ public class GameCrashWindow extends Stage { private final TextFlow feedbackTextFlow = new TextFlow(); private final ManagedProcess managedProcess; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final ProcessListener.ExitType exitType; private final LaunchOptions launchOptions; private final View view; @@ -91,7 +91,7 @@ public class GameCrashWindow extends Stage { private final List logs; - public GameCrashWindow(ManagedProcess managedProcess, ProcessListener.ExitType exitType, DefaultGameRepository repository, Version version, LaunchOptions launchOptions, List logs) { + public GameCrashWindow(ManagedProcess managedProcess, ProcessListener.ExitType exitType, DefaultGameRepository2 repository, GameInstanceManifest version, LaunchOptions launchOptions, List logs) { Themes.applyNativeDarkMode(this); this.managedProcess = managedProcess; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java index 7464556d7d..c23a81bb2f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java @@ -23,9 +23,8 @@ import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.GameRepository; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.ui.InstallerItem; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.util.Lang; @@ -38,11 +37,11 @@ class AdditionalInstallersPage extends AbstractInstallersPage { protected final BooleanProperty compatible = new SimpleBooleanProperty(); - protected final GameRepository repository; + protected final HMCLGameRepository repository; protected final String gameVersion; - protected final Version version; + protected final GameInstanceManifest version; - public AdditionalInstallersPage(String gameVersion, Version version, WizardController controller, HMCLGameRepository repository, DownloadProvider downloadProvider) { + public AdditionalInstallersPage(String gameVersion, GameInstanceManifest version, WizardController controller, HMCLGameRepository repository, DownloadProvider downloadProvider) { super(controller, gameVersion, downloadProvider); this.gameVersion = gameVersion; this.version = version; @@ -81,7 +80,7 @@ private String getVersion(String id) { @Override protected void reload() { - Version resolved = version.resolvePreservingPatches(repository); + GameInstanceManifest resolved = version.resolvePreservingPatches(repository); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, repository.getGameVersion(resolved).orElse(null)); String game = analyzer.getVersion(MINECRAFT).orElse(null); String currentGameVersion = Lang.nonNull(getVersion("game"), game); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java index 2a51d4d91d..12385b64f6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java @@ -21,8 +21,8 @@ import org.jackhuang.hmcl.download.*; import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask; import org.jackhuang.hmcl.download.game.LibraryDownloadException; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.task.DownloadException; import org.jackhuang.hmcl.task.Task; @@ -49,12 +49,12 @@ public final class UpdateInstallerWizardProvider implements WizardProvider { private final HMCLGameRepository repository; private final DefaultDependencyManager dependencyManager; private final String gameVersion; - private final Version version; + private final GameInstanceManifest version; private final String libraryId; private final String oldLibraryVersion; private final DownloadProvider downloadProvider; - public UpdateInstallerWizardProvider(@NotNull HMCLGameRepository repository, @NotNull String gameVersion, @NotNull Version version, @NotNull String libraryId, @Nullable String oldLibraryVersion) { + public UpdateInstallerWizardProvider(@NotNull HMCLGameRepository repository, @NotNull String gameVersion, @NotNull GameInstanceManifest version, @NotNull String libraryId, @Nullable String oldLibraryVersion) { this.repository = repository; this.gameVersion = gameVersion; this.version = version; @@ -76,7 +76,7 @@ public Object finish(SettingsMap settings) { // We remove library but not save it, // so if installation failed will not break down current version. - Task ret = Task.supplyAsync(() -> version); + Task ret = Task.supplyAsync(() -> version); var hints = new ArrayList(); for (Object value : settings.asStringMap().values()) { if (value instanceof RemoteVersion remoteVersion) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java index 54fef4e3b2..5ec3603a20 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java @@ -2614,7 +2614,7 @@ private void initJavaSubtitle() { if (JavaManager.isInitialized()) { GameVersionNumber gameVersionNumber = this.currentGameVersionNumber.get(); - Version version; + GameInstanceManifest version; if (this.instanceId == null) { version = null; } else { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 3248ee2f9a..8130ea7ea1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -44,8 +44,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.VersionList; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.setting.GameDirectory; import org.jackhuang.hmcl.setting.GameDirectoryManager; @@ -94,7 +94,7 @@ public final class MainPage extends StackPane implements DecoratorPage { private final BooleanProperty showUpdate = new SimpleBooleanProperty(this, "showUpdate"); private final BooleanProperty showUpdateDialog = new SimpleBooleanProperty(this, "showUpdateDialog"); private final ObjectProperty latestVersion = new SimpleObjectProperty<>(this, "latestVersion"); - private final ObservableList versions = FXCollections.observableArrayList(); + private final ObservableList versions = FXCollections.observableArrayList(); private HMCLGameRepository repository; private TransitionPane announcementPane; @@ -397,7 +397,7 @@ public void setCurrentGame(String currentGame) { this.currentGame.set(currentGame); } - public ObservableList getVersions() { + public ObservableList getVersions() { return versions; } @@ -437,7 +437,7 @@ public void setLatestVersion(RemoteVersion latestVersion) { this.latestVersion.set(latestVersion); } - public void initVersions(HMCLGameRepository repository, List versions) { + public void initVersions(HMCLGameRepository repository, List versions) { FXUtils.checkFxUserThread(); this.repository = repository; this.versions.setAll(versions); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 01c4153024..a911335f4e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -23,9 +23,9 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.RefreshedVersionsEvent; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ModpackHelper; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.GameDirectory; import org.jackhuang.hmcl.setting.GameDirectoryManager; @@ -124,10 +124,10 @@ public MainPage getMainPage() { GameDirectoryManager.registerVersionsListener(repository -> { GameDirectory gameDirectory = repository.getGameDirectory(); - List children = repository.getVersions().parallelStream() + List children = repository.getVersions().parallelStream() .filter(version -> !version.isHidden()) .sorted(Comparator - .comparing((Version version) -> Lang.requireNonNullElse(version.getReleaseTime(), Instant.EPOCH)) + .comparing((GameInstanceManifest version) -> Lang.requireNonNullElse(version.getReleaseTime(), Instant.EPOCH)) .thenComparing(version -> VersionNumber.asVersion(repository.getGameVersion(version).orElse(version.getId())))) .collect(Collectors.toList()); runInFX(() -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index bd1f09f697..530500bee9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -38,8 +38,8 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.*; import org.jackhuang.hmcl.download.DownloadProvider; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.addon.RemoteAddonRepository; import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; @@ -124,7 +124,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable String instanc if (versionSelection) { versions.setAll(repository.getDisplayVersions() - .map(Version::getId) + .map(GameInstanceManifest::getId) .collect(Collectors.toList())); selectedVersion.set(repository.getSelectedInstance()); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index fb06c3ca0f..b5980402a4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -31,8 +31,8 @@ import javafx.stage.FileChooser; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.addon.mod.ModLoaderType; import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.addon.RemoteAddonRepository; @@ -271,7 +271,7 @@ protected DownloadPageSkin(DownloadPage control) { if (control.instanceReference.repository() != null && control.instanceReference.instanceId() != null) { HMCLGameRepository repository = control.instanceReference.repository(); - Version game = repository.getResolvedPreservingPatchesVersion(control.instanceReference.instanceId()); + GameInstanceManifest game = repository.getResolvedPreservingPatchesVersion(control.instanceReference.instanceId()); String gameVersion = repository.getGameVersion(game).orElse(null); if (gameVersion != null && control.versions.containsKey(gameVersion)) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java index 54c16f6a58..6dce9afdee 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java @@ -33,8 +33,8 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.ImageContainer; import org.jackhuang.hmcl.ui.construct.RipplerContainer; @@ -50,7 +50,7 @@ public final class GameListPopupMenu extends StackPane { public static void show(Node owner, JFXPopup.PopupVPosition vAlign, JFXPopup.PopupHPosition hAlign, double initOffsetX, double initOffsetY, - HMCLGameRepository repository, List versions) { + HMCLGameRepository repository, List versions) { GameListPopupMenu menu = new GameListPopupMenu(); menu.getItems().setAll(versions.stream() .filter(it -> repository.hasVersion(it.getId())) 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 ed554baa49..6106a295bf 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 @@ -22,8 +22,8 @@ import javafx.scene.control.Skin; import javafx.stage.FileChooser; import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; @@ -45,7 +45,7 @@ public class InstallerListPage extends ListPageBase implements VersionPage.GameInstanceLoadable { private HMCLGameRepository repository; private String versionId; - private Version version; + private GameInstanceManifest version; private String gameVersion; { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java index 5207aab98e..5a01834fca 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java @@ -23,8 +23,8 @@ import javafx.scene.control.Skin; import javafx.stage.FileChooser; import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.addon.mod.LocalModFile; import org.jackhuang.hmcl.addon.mod.ModLoaderType; import org.jackhuang.hmcl.addon.mod.ModManager; @@ -90,7 +90,7 @@ public void loadInstance(HMCLGameRepository repository, String instanceId) { this.repository = repository; this.instanceId = instanceId; - Version resolved = repository.getResolvedPreservingPatchesVersion(instanceId); + GameInstanceManifest resolved = repository.getResolvedPreservingPatchesVersion(instanceId); this.gameVersion = repository.getGameVersion(resolved).orElse(null); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion); modded.set(analyzer.hasModLoader()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index c986694163..d93df43748 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -47,6 +47,7 @@ import org.jackhuang.hmcl.ui.export.ExportWizardProvider; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.TaskCancellationAction; +import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -166,9 +167,12 @@ public static void openFolder(HMCLGameRepository repository, String version) { } public static void installFromJson(HMCLGameRepository repository, Path file) { - Version version; + GameInstanceManifest version; try { - version = repository.readVersionJson(file); + version = JsonUtils.fromJsonFile(file, GameInstanceManifest.class); + if (version == null) { + throw new IllegalArgumentException("Missing game manifest"); + } } catch (Exception e) { Controllers.dialog(i18n("install.new_game.malformed_json"), i18n("message.error"), MessageDialogPane.MessageType.ERROR); return; @@ -178,7 +182,7 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { handler.resolve(); DefaultDependencyManager dependencyManager = repository.getDependency(); - Version newVersion = version.setId(result).setJar(result); + GameInstanceManifest newVersion = version.setId(result).setJar(result); Controllers.taskDialog( Task.allOf(new GameDownloadTask(dependencyManager, null, newVersion), diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java index a5019d739b..72e3e3c8ad 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java @@ -64,7 +64,7 @@ private static Map getNatives(Platform platform) { } // https://github.com/LWJGL/lwjgl3/issues/1111 - public static boolean needPatchMemoryUtil(Version version, int javaVersion) { + public static boolean needPatchMemoryUtil(GameInstanceManifest version, int javaVersion) { return javaVersion >= 25 && javaVersion <= 26 && version.getLibraries().stream().anyMatch(library -> "org.lwjgl".equals(library.getGroupId()) && "lwjgl".equals(library.getArtifactId()) @@ -73,11 +73,11 @@ public static boolean needPatchMemoryUtil(Version version, int javaVersion) { ); } - public static Version patchNative(DefaultGameRepository repository, - Version version, String gameVersion, - JavaRuntime javaVersion, - GameSettings.Effective settings, - List javaArguments) { + public static GameInstanceManifest patchNative(DefaultGameRepository2 repository, + GameInstanceManifest version, String gameVersion, + JavaRuntime javaVersion, + GameSettings.Effective settings, + List javaArguments) { if (settings.get(GameSettings::useCustomNativesProperty)) { if (gameVersion != null && GameVersionNumber.compare(gameVersion, "1.19") < 0) return version; diff --git a/HMCL/src/test/java/org/jackhuang/hmcl/ui/GameCrashWindowTest.java b/HMCL/src/test/java/org/jackhuang/hmcl/ui/GameCrashWindowTest.java index 248e027c07..75b0b4c6fc 100644 --- a/HMCL/src/test/java/org/jackhuang/hmcl/ui/GameCrashWindowTest.java +++ b/HMCL/src/test/java/org/jackhuang/hmcl/ui/GameCrashWindowTest.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.ui; import org.jackhuang.hmcl.JavaFXLauncher; -import org.jackhuang.hmcl.game.ClassicVersion; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.java.JavaInfo; import org.jackhuang.hmcl.game.Log; @@ -52,7 +52,7 @@ public void test() throws Exception { Path workingPath = Path.of(System.getProperty("user.dir")); GameCrashWindow window = new GameCrashWindow(process, ProcessListener.ExitType.APPLICATION_ERROR, null, - new ClassicVersion(), + new GameInstanceManifest("Classic"), new LaunchOptions.Builder() .setJava(new JavaRuntime(workingPath, new JavaInfo(Platform.SYSTEM_PLATFORM, "16", null), false, false)) .setGameDir(workingPath) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java index 0520b67a8f..61795e4d1c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java @@ -17,7 +17,7 @@ */ package org.jackhuang.hmcl.addon; -import org.jackhuang.hmcl.game.GameRepository; +import org.jackhuang.hmcl.game.GameRepository2; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jetbrains.annotations.NotNull; @@ -46,15 +46,15 @@ public static String getLocalAddonName(Path file) { protected final Set<@NotNull T> localFiles = new LinkedHashSet<>(); - protected final GameRepository repository; + protected final GameRepository2 repository; protected final String id; - public LocalAddonManager(GameRepository gameRepository, String versionId) { + public LocalAddonManager(GameRepository2 gameRepository, String versionId) { this.repository = gameRepository; this.id = versionId; } - public GameRepository getRepository() { + public GameRepository2 getRepository() { return repository; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java index 8acc315111..e7bebda0ea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java @@ -22,7 +22,8 @@ import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.addon.meta.*; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.GameRepository; +import org.jackhuang.hmcl.game.GameRepository2; +import org.jackhuang.hmcl.game.NoSuchGameInstanceException; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; @@ -68,7 +69,7 @@ private interface ModMetadataReader { private boolean loaded = false; - public ModManager(GameRepository repository, String id) { + public ModManager(GameRepository2 repository, String id) { super(repository, id); } @@ -178,7 +179,11 @@ public void refresh() throws IOException { localFiles.clear(); localMods.clear(); - analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedPreservingPatchesVersion(id), null); + try { + analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedPreservingPatchesVersion(id), null); + } catch (NoSuchGameInstanceException e) { + throw new IOException(e); + } boolean supportSubfolders = analyzer.has(LibraryAnalyzer.LibraryType.FORGE) || analyzer.has(LibraryAnalyzer.LibraryType.QUILT); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java index d571f6422c..a08d57211d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.addon.resourcepack; import com.google.gson.annotations.SerializedName; -import org.jackhuang.hmcl.game.GameRepository; +import org.jackhuang.hmcl.game.GameRepository2; import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.addon.meta.PackMcMeta; import org.jackhuang.hmcl.util.Pair; @@ -218,7 +218,7 @@ private static List deserializePackList(String json) { private boolean loaded = false; - public ResourcePackManager(GameRepository repository, String id) { + public ResourcePackManager(GameRepository2 repository, String id) { super(repository, id); this.resourcePackDirectory = this.repository.getResourcePackDirectory(this.id); this.optionsFile = repository.getRunDirectory(id).resolve("options.txt"); 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 455aa622e0..06502c12c8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -25,9 +25,10 @@ import org.jackhuang.hmcl.download.neoforge.NeoForgeInstallTask; import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask; import org.jackhuang.hmcl.game.Artifact; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.io.FileUtils; @@ -47,18 +48,18 @@ */ public class DefaultDependencyManager extends AbstractDependencyManager { - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final DownloadProvider downloadProvider; private final DefaultCacheRepository cacheRepository; - public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider, DefaultCacheRepository cacheRepository) { + public DefaultDependencyManager(DefaultGameRepository2 repository, DownloadProvider downloadProvider, DefaultCacheRepository cacheRepository) { this.repository = repository; this.downloadProvider = downloadProvider; this.cacheRepository = cacheRepository; } @Override - public DefaultGameRepository getGameRepository() { + public DefaultGameRepository2 getGameRepository() { return repository; } @@ -78,7 +79,7 @@ public GameBuilder gameBuilder() { } @Override - public Task checkGameCompletionAsync(Version version, boolean integrityCheck) { + public Task checkGameCompletionAsync(GameInstanceManifest version, boolean integrityCheck) { return Task.allOf( Task.composeAsync(() -> { Path versionJar = repository.getVersionJar(version); @@ -94,20 +95,20 @@ public Task checkGameCompletionAsync(Version version, boolean integrityCheck) } @Override - public Task checkLibraryCompletionAsync(Version version, boolean integrityCheck) { + public Task checkLibraryCompletionAsync(GameInstanceManifest version, boolean integrityCheck) { return new GameLibrariesTask(this, version, integrityCheck, version.getLibraries()); } @Override - public Task checkPatchCompletionAsync(Version version, boolean integrityCheck) { + public Task checkPatchCompletionAsync(GameInstanceManifest version, boolean integrityCheck) { return Task.composeAsync(() -> { List> tasks = new ArrayList<>(0); String gameVersion = repository.getGameVersion(version).orElse(null); if (gameVersion == null) return null; - Version original = repository.getVersion(version.getId()); - Version resolved = original.resolvePreservingPatches(repository); + GameInstanceManifest original = repository.getVersion(version.getId()); + GameInstanceManifest resolved = original.resolvePreservingPatches(repository); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion); for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values()) { @@ -123,7 +124,7 @@ public Task checkPatchCompletionAsync(Version version, boolean integrityCheck .orElseGet(() -> resolved.getPatches().stream() .filter(patch -> "optifine".equals(patch.getId())) .findAny() - .map(Version::getVersion) + .map(GameInstancePatch::getVersion) .orElse(null)); boolean needsReInstallation = version.getLibraries().stream() @@ -147,9 +148,7 @@ public Task checkPatchCompletionAsync(Version version, boolean integrityCheck } @Override - public Task installLibraryAsync(String gameVersion, Version baseVersion, String libraryId, String libraryVersion) { - if (baseVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved"); - + public Task installLibraryAsync(String gameVersion, GameInstanceManifest baseVersion, String libraryId, String libraryVersion) { VersionList versionList = getVersionList(libraryId); return versionList.loadAsync(gameVersion) .thenComposeAsync(() -> installLibraryAsync(baseVersion, versionList.getVersion(gameVersion, libraryVersion) @@ -158,12 +157,10 @@ public Task installLibraryAsync(String gameVersion, Version baseVersion } @Override - public Task installLibraryAsync(Version baseVersion, RemoteVersion libraryVersion) { - if (baseVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved"); - - AtomicReference removedLibraryVersion = new AtomicReference<>(); + public Task installLibraryAsync(GameInstanceManifest baseVersion, RemoteVersion libraryVersion) { + AtomicReference removedLibraryVersion = new AtomicReference<>(); - return removeLibraryAsync(baseVersion.resolvePreservingPatches(repository), libraryVersion.getLibraryId()) + return removeLibraryAsync(baseVersion, libraryVersion.getLibraryId()) .thenComposeAsync(version -> { removedLibraryVersion.set(version); return libraryVersion.getInstallTask(this, version); @@ -178,9 +175,7 @@ public Task installLibraryAsync(Version baseVersion, RemoteVersion libr .withStage(String.format("hmcl.install.%s:%s", libraryVersion.getLibraryId(), libraryVersion.getSelfVersion())); } - public Task installLibraryAsync(Version oldVersion, Path installer) { - if (oldVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved"); - + public Task installLibraryAsync(GameInstanceManifest oldVersion, Path installer) { return Task .composeAsync(() -> { try { @@ -205,7 +200,7 @@ public Task installLibraryAsync(Version oldVersion, Path installer) { throw new UnsupportedLibraryInstallerException(); }) - .thenApplyAsync(oldVersion::addPatch); + .thenApplyAsync(patch -> patch == null ? oldVersion : oldVersion.addPatch(patch)); } public static class UnsupportedLibraryInstallerException extends Exception { @@ -219,16 +214,15 @@ public static class UnsupportedLibraryInstallerException extends Exception { * @param libraryId forge/liteloader/optifine/fabric * @return task to remove the specified library */ - public Task removeLibraryAsync(Version version, String libraryId) { + public Task removeLibraryAsync(GameInstanceManifest version, String libraryId) { // MaintainTask requires version that does not inherits from any version. // If we want to remove a library in dependent version, we should keep the dependents not changed // So resolving this game version to preserve all information in this version.json is necessary. - if (version.isResolved()) - throw new IllegalArgumentException("removeLibraryWithoutSavingAsync requires non-resolved version"); - Version independentVersion = version.resolvePreservingPatches(repository); - String gameVersion = repository.getGameVersion(independentVersion).orElse(null); - - return Task.supplyAsync(() -> LibraryAnalyzer.analyze(independentVersion, gameVersion).removeLibrary(libraryId).build()); + return Task.supplyAsync(() -> { + GameInstanceManifest independentVersion = version.resolvePreservingPatches(repository); + String gameVersion = repository.getGameVersion(independentVersion).orElse(null); + return LibraryAnalyzer.analyze(independentVersion, gameVersion).removeLibrary(libraryId).build(); + }); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java index b6966503ea..5c4d0ee75b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java @@ -17,7 +17,7 @@ */ package org.jackhuang.hmcl.download; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.function.ExceptionalFunction; @@ -44,7 +44,7 @@ public DefaultDependencyManager getDependencyManager() { public Task buildAsync() { var hints = new ArrayList(); - Task libraryTask = Task.supplyAsync(() -> new Version(name)); + Task libraryTask = Task.supplyAsync(() -> new GameInstanceManifest(name)); libraryTask = libraryTask.thenComposeAsync(libraryTaskHelper(gameVersion, "game", gameVersion)); hints.add(new Task.StagesHint("hmcl.install.game:" + gameVersion)); hints.add(new Task.StagesHint("hmcl.install.libraries")); @@ -66,7 +66,7 @@ public Task buildAsync() { }).withStagesHints(hints); } - private ExceptionalFunction, ?> libraryTaskHelper(String gameVersion, String libraryId, String libraryVersion) { + private ExceptionalFunction, ?> libraryTaskHelper(String gameVersion, String libraryId, String libraryVersion) { return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, libraryVersion); } } 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 98501fc8cc..ab9515efbd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java @@ -17,8 +17,8 @@ */ package org.jackhuang.hmcl.download; -import org.jackhuang.hmcl.game.GameRepository; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameRepository2; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.CacheRepository; @@ -33,7 +33,7 @@ public interface DependencyManager { /** * The relied game repository. */ - GameRepository getGameRepository(); + GameRepository2 getGameRepository(); /** * The cache repository @@ -46,7 +46,7 @@ public interface DependencyManager { * * @return the task to check game completion. */ - Task checkGameCompletionAsync(Version version, boolean integrityCheck); + Task checkGameCompletionAsync(GameInstanceManifest version, boolean integrityCheck); /** * Check if libraries of this version in complete. @@ -54,7 +54,7 @@ public interface DependencyManager { * * @return the task to check game completion. */ - Task checkLibraryCompletionAsync(Version version, boolean integrityCheck); + Task checkLibraryCompletionAsync(GameInstanceManifest version, boolean integrityCheck); /** * Check if patches of this version in complete. @@ -64,7 +64,7 @@ public interface DependencyManager { * @param integrityCheck check if some libraries are corrupt. * @return the task to check patches completion. */ - Task checkPatchCompletionAsync(Version version, boolean integrityCheck); + Task checkPatchCompletionAsync(GameInstanceManifest version, boolean integrityCheck); /** * The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine. @@ -81,7 +81,7 @@ public interface DependencyManager { * @param libraryVersion the version of being installed library. * @return the task to install the specific library. */ - Task installLibraryAsync(String gameVersion, Version baseVersion, String libraryId, String libraryVersion); + Task installLibraryAsync(String gameVersion, GameInstanceManifest baseVersion, String libraryId, String libraryVersion); /** * Install a library to a version. @@ -91,7 +91,7 @@ public interface DependencyManager { * @param libraryVersion the remote version of being installed library. * @return the task to install the specific library. */ - Task installLibraryAsync(Version baseVersion, RemoteVersion libraryVersion); + Task installLibraryAsync(GameInstanceManifest baseVersion, RemoteVersion libraryVersion); /** * Get registered version list. 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 90f7efd227..072f361eb2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -34,10 +34,10 @@ import static org.jackhuang.hmcl.util.Pair.pair; public final class LibraryAnalyzer implements Iterable { - private Version version; + private GameInstanceManifest version; private final Map> libraries; - private LibraryAnalyzer(Version version, Map> libraries) { + private LibraryAnalyzer(GameInstanceManifest version, Map> libraries) { this.version = version; this.libraries = libraries; } @@ -102,7 +102,7 @@ public boolean hasModLauncher() { ); } - private Version removingMatchedLibrary(Version version, String libraryId) { + private GameInstanceManifest removingMatchedLibrary(GameInstanceManifest version, String libraryId) { LibraryType type = LibraryType.fromPatchId(libraryId); if (type == null) return version; @@ -118,6 +118,22 @@ private Version removingMatchedLibrary(Version version, String libraryId) { return version.setLibraries(libraries); } + private GameInstancePatch removingMatchedLibrary(GameInstancePatch patch, String libraryId) { + LibraryType type = LibraryType.fromPatchId(libraryId); + if (type == null) return patch; + + List libraries = new ArrayList<>(); + List rawLibraries = patch.getLibraries(); + for (Library library : rawLibraries) { + if (type.matchLibrary(library, rawLibraries)) { + // skip + } else { + libraries.add(library); + } + } + return patch.setLibraries(libraries); + } + /** * Remove library by library id * @@ -134,11 +150,11 @@ public LibraryAnalyzer removeLibrary(String libraryId) { return this; } - public Version build() { + public GameInstanceManifest build() { return version; } - public static LibraryAnalyzer analyze(Version version, String gameVersion) { + public static LibraryAnalyzer analyze(GameInstanceManifest version, String gameVersion) { if (version.getInheritsFrom() != null) throw new IllegalArgumentException("LibraryAnalyzer can only analyze independent game version"); @@ -148,7 +164,7 @@ public static LibraryAnalyzer analyze(Version version, String gameVersion) { libraries.put(LibraryType.MINECRAFT.getPatchId(), pair(null, gameVersion)); } - List rawLibraries = version.resolve(null).getLibraries(); + List rawLibraries = version.getLibraries(); for (Library library : rawLibraries) { for (LibraryType type : LibraryType.values()) { if (type.matchLibrary(library, rawLibraries)) { @@ -158,7 +174,7 @@ public static LibraryAnalyzer analyze(Version version, String gameVersion) { } } - for (Version patch : version.getPatches()) { + for (GameInstancePatch patch : version.getPatches()) { if (patch.isHidden()) continue; libraries.put(patch.getId(), pair(null, patch.getVersion())); } @@ -166,8 +182,13 @@ public static LibraryAnalyzer analyze(Version version, String gameVersion) { return new LibraryAnalyzer(version, libraries); } - public static boolean isModded(VersionProvider provider, Version version) { - Version resolvedVersion = version.resolve(provider); + public static boolean isModded(GameRepository2 provider, GameInstanceManifest version) { + GameInstanceManifest resolvedVersion; + try { + resolvedVersion = version.resolve(provider); + } catch (NoSuchGameInstanceException e) { + return false; + } String mainClass = resolvedVersion.getMainClass(); return mainClass != null && (LAUNCH_WRAPPER_MAIN.equals(mainClass) || mainClass.startsWith("net.minecraftforge") @@ -223,7 +244,7 @@ protected boolean matchLibrary(Library library, List libraries) { private final Pattern FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?[0-9.]+)(-([0-9.]+))?$"); @Override - protected String patchVersion(Version gameVersion, String libraryVersion) { + protected String patchVersion(GameInstanceManifest gameVersion, String libraryVersion) { Matcher matcher = FORGE_VERSION_MATCHER.matcher(libraryVersion); if (matcher.find()) { return matcher.group("forge"); @@ -246,14 +267,14 @@ protected boolean matchLibrary(Library library, List libraries) { private final Pattern NEO_FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?[0-9.]+)(-([0-9.]+))?$"); @Override - protected String patchVersion(Version gameVersion, String libraryVersion) { + protected String patchVersion(GameInstanceManifest gameVersion, String libraryVersion) { String res = scanVersion(gameVersion); if (res != null) { return res; } - for (Version patch : gameVersion.getPatches()) { - res = scanVersion(patch); + for (GameInstancePatch patch : gameVersion.getPatches()) { + res = scanPatch(patch); if (res != null) { return res; } @@ -267,7 +288,7 @@ protected String patchVersion(Version gameVersion, String libraryVersion) { return super.patchVersion(gameVersion, libraryVersion); } - private String scanVersion(Version version) { + private String scanVersion(GameInstanceManifest version) { Optional optArgument = version.getArguments(); if (!optArgument.isPresent()) { return null; @@ -293,6 +314,32 @@ private String scanVersion(Version version) { return null; } + private String scanPatch(GameInstancePatch patch) { + Optional optArgument = patch.getArguments(); + if (!optArgument.isPresent()) { + return null; + } + List gameArguments = optArgument.get().game(); + if (gameArguments == null) { + return null; + } + + for (int i = 0; i < gameArguments.size() - 1; i++) { + Argument argument = gameArguments.get(i); + if (argument instanceof StringArgument) { + String argumentValue = ((StringArgument) argument).argument(); + if ("--fml.neoForgeVersion".equals(argumentValue) || "--fml.forgeVersion".equals(argumentValue)) { + Argument next = gameArguments.get(i + 1); + if (next instanceof StringArgument) { + return ((StringArgument) next).argument(); + } + return null; + } + } + } + return null; + } + }, LITELOADER(true, "liteloader", "com\\.mumfrey", "liteloader", ModLoaderType.LITE_LOADER), OPTIFINE(false, "optifine", "(net\\.)?optifine", "^(?!.*launchwrapper).*$", null), @@ -341,7 +388,7 @@ protected boolean matchLibrary(Library library, List libraries) { return group.matcher(library.getGroupId()).matches() && artifact.matcher(library.getArtifactId()).matches(); } - protected String patchVersion(Version gameVersion, String libraryVersion) { + protected String patchVersion(GameInstanceManifest gameVersion, String libraryVersion) { return libraryVersion; } } 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 dc57fdf340..584dede112 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -38,11 +38,11 @@ import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*; import static org.jackhuang.hmcl.util.logging.Logger.LOG; -public class MaintainTask extends Task { - private final GameRepository repository; - private final Version version; +public class MaintainTask extends Task { + private final GameRepository2 repository; + private final GameInstanceManifest version; - public MaintainTask(GameRepository repository, Version version) { + public MaintainTask(GameRepository2 repository, GameInstanceManifest version) { this.repository = repository; this.version = version; @@ -55,11 +55,11 @@ public void execute() { setResult(maintain(repository, version)); } - public static Version maintain(GameRepository repository, Version version) { + public static GameInstanceManifest maintain(GameRepository2 repository, GameInstanceManifest version) { if (version.getInheritsFrom() != null) throw new IllegalArgumentException("MaintainTask requires independent game version"); - String mainClass = version.resolve(null).getMainClass(); + String mainClass = version.resolve(repository).getMainClass(); if (mainClass != null && mainClass.equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN)) { version = maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(repository, unique(version), true), false); @@ -91,14 +91,14 @@ public static Version maintain(GameRepository repository, Version version) { return version; } - public static Version maintainPreservingPatches(GameRepository repository, Version version) { + public static GameInstanceManifest maintainPreservingPatches(GameRepository2 repository, GameInstanceManifest version) { if (!version.isResolvedPreservingPatches()) throw new IllegalArgumentException("MaintainTask requires independent game version"); - Version newVersion = maintain(repository, version.resolve(repository)); - return newVersion.setPatches(version.getPatches()).markAsUnresolved(); + GameInstanceManifest newVersion = maintain(repository, version.resolve(repository)); + return newVersion.setPatches(version.getPatches()); } - private static Version maintainGameWithLaunchWrapper(GameRepository repository, Version version, boolean reorderTweakClass) { + private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository2 repository, GameInstanceManifest version, boolean reorderTweakClass) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); VersionLibraryBuilder builder = new VersionLibraryBuilder(version); String mainClass = null; @@ -143,11 +143,11 @@ private static Version maintainGameWithLaunchWrapper(GameRepository repository, } } - Version ret = builder.build(); + GameInstanceManifest ret = builder.build(); return mainClass == null ? ret : ret.setMainClass(mainClass); } - private static Version maintainGameWithCpwModLauncher(GameRepository repository, Version version) { + private static GameInstanceManifest maintainGameWithCpwModLauncher(GameRepository2 repository, GameInstanceManifest version) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); VersionLibraryBuilder builder = new VersionLibraryBuilder(version); @@ -173,7 +173,7 @@ private static Version maintainGameWithCpwModLauncher(GameRepository repository, return builder.build(); } - private static String updateIgnoreList(GameRepository repository, Version version, String ignoreList) { + private static String updateIgnoreList(GameRepository2 repository, GameInstanceManifest version, String ignoreList) { String[] ignores = ignoreList.split(","); List newIgnoreList = new ArrayList<>(); @@ -205,7 +205,7 @@ private static String updateIgnoreList(GameRepository repository, Version versio } // Fix wrong configurations when launching 1.17+ with Forge. - private static Version maintainGameWithCpwBoostrapLauncher(GameRepository repository, Version version) { + private static GameInstanceManifest maintainGameWithCpwBoostrapLauncher(GameRepository2 repository, GameInstanceManifest version) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); VersionLibraryBuilder builder = new VersionLibraryBuilder(version); @@ -247,7 +247,7 @@ private static Version maintainGameWithCpwBoostrapLauncher(GameRepository reposi return builder.build(); } - private static Version maintainOptiFineLibrary(GameRepository repository, Version version, boolean remove) { + private static GameInstanceManifest maintainOptiFineLibrary(GameRepository2 repository, GameInstanceManifest version, boolean remove) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); List libraries = new ArrayList<>(version.getLibraries()); @@ -284,7 +284,7 @@ private static Version maintainOptiFineLibrary(GameRepository repository, Versio return version.setLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList())); } - public static Version unique(Version version) { + public static GameInstanceManifest unique(GameInstanceManifest version) { List libraries = new ArrayList<>(); SimpleMultimap> multimap = new SimpleMultimap<>(HashMap::new, ArrayList::new); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/RemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/RemoteVersion.java index 99e39a2184..c3bf1da8e0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/RemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/RemoteVersion.java @@ -17,7 +17,8 @@ */ package org.jackhuang.hmcl.download; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.ToStringBuilder; import org.jackhuang.hmcl.util.versioning.VersionNumber; @@ -95,7 +96,7 @@ public Type getVersionType() { return type; } - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { throw new UnsupportedOperationException(this + " cannot be installed yet"); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java index 15bd9e8016..d3093f9b5a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java @@ -23,7 +23,8 @@ import org.jackhuang.hmcl.download.VersionMismatchException; import org.jackhuang.hmcl.download.forge.ForgeNewInstallProfile; import org.jackhuang.hmcl.download.forge.ForgeNewInstallTask; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -38,17 +39,17 @@ import java.util.Map; import java.util.Optional; -public final class CleanroomInstallTask extends Task { +public final class CleanroomInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final CleanroomRemoteVersion remote; private Path installer; private FileDownloadTask dependent; - private Task task; + private Task task; private String selfVersion; - public CleanroomInstallTask(DefaultDependencyManager dependencyManager, Version version, CleanroomRemoteVersion remoteVersion) { + public CleanroomInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, CleanroomRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; @@ -56,7 +57,7 @@ public CleanroomInstallTask(DefaultDependencyManager dependencyManager, Version setSignificance(TaskSignificance.MODERATE); } - public CleanroomInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) { + public CleanroomInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, String selfVersion, Path installer) { this.dependencyManager = dependencyManager; this.version = version; this.selfVersion = selfVersion; @@ -118,7 +119,7 @@ public void execute() throws IOException, VersionMismatchException, UnsupportedI } } - public static Task install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + public static Task install(DefaultDependencyManager dependencyManager, GameInstanceManifest version, Path installer) throws IOException, VersionMismatchException { Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); if (gameVersion.isEmpty()) throw new IOException(); try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomRemoteVersion.java index 6d6bfddbd4..3f88ef351c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; import java.time.Instant; @@ -32,7 +33,7 @@ public CleanroomRemoteVersion(String gameVersion, String selfVersion, Instant re } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new CleanroomInstallTask(dependencyManager, baseVersion, this); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java index d8e0995571..a6a622abd9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java @@ -18,7 +18,8 @@ package org.jackhuang.hmcl.download.fabric; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; @@ -32,14 +33,14 @@ * * @author huangyuhui */ -public final class FabricAPIInstallTask extends Task { +public final class FabricAPIInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final FabricAPIRemoteVersion remote; private final List> dependencies = new ArrayList<>(1); - public FabricAPIInstallTask(DefaultDependencyManager dependencyManager, Version version, FabricAPIRemoteVersion remoteVersion) { + public FabricAPIInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, FabricAPIRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIRemoteVersion.java index 3cbe956905..db2e955ccc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.Task; @@ -55,7 +56,7 @@ public RemoteAddon.Version getVersion() { } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new FabricAPIInstallTask(dependencyManager, baseVersion, this); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java index b21586201a..99e79f0461 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java @@ -24,8 +24,9 @@ import org.jackhuang.hmcl.download.UnsupportedInstallationException; import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.Artifact; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonSerializable; @@ -41,15 +42,15 @@ * * @author huangyuhui */ -public final class FabricInstallTask extends Task { +public final class FabricInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final FabricRemoteVersion remote; private final GetTask launchMetaTask; private final List> dependencies = new ArrayList<>(1); - public FabricInstallTask(DefaultDependencyManager dependencyManager, Version version, FabricRemoteVersion remoteVersion) { + public FabricInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, FabricRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; @@ -92,10 +93,10 @@ public void execute() throws IOException { setResult(getPatch(fabricInfo, remote.getGameVersion(), remote.getSelfVersion())); - dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true)); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); } - private Version getPatch(FabricInfo fabricInfo, String gameVersion, String loaderVersion) { + private GameInstancePatch getPatch(FabricInfo fabricInfo, String gameVersion, String loaderVersion) { JsonObject launcherMeta = fabricInfo.launcherMeta; Arguments arguments = new Arguments(); @@ -125,7 +126,7 @@ private Version getPatch(FabricInfo fabricInfo, String gameVersion, String loade libraries.add(new Library(Artifact.fromDescriptor(fabricInfo.intermediary.maven), "https://maven.fabricmc.net/", null)); libraries.add(new Library(Artifact.fromDescriptor(fabricInfo.loader.maven), "https://maven.fabricmc.net/", null)); - return new Version(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), loaderVersion, Version.PRIORITY_LOADER, arguments, mainClass, libraries); + return new GameInstancePatch(LibraryAnalyzer.LibraryType.FABRIC.getPatchId(), loaderVersion, GameInstancePatch.PRIORITY_LOADER, arguments, mainClass, libraries); } @JsonSerializable diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricRemoteVersion.java index 015edd8014..857c593eea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; import java.util.List; @@ -38,7 +39,7 @@ public class FabricRemoteVersion extends RemoteVersion { } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new FabricInstallTask(dependencyManager, baseVersion, this); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallProfile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallProfile.java index 2053837ddc..6757655f79 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallProfile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallProfile.java @@ -19,7 +19,7 @@ import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.Validation; @@ -34,9 +34,9 @@ public final class ForgeInstallProfile implements Validation { private final ForgeInstall install; @SerializedName("versionInfo") - private final Version versionInfo; + private final GameInstanceManifest versionInfo; - public ForgeInstallProfile(ForgeInstall install, Version versionInfo) { + public ForgeInstallProfile(ForgeInstall install, GameInstanceManifest versionInfo) { this.install = install; this.versionInfo = versionInfo; } @@ -45,7 +45,7 @@ public ForgeInstall getInstall() { return install; } - public Version getVersionInfo() { + public GameInstanceManifest getVersionInfo() { return versionInfo; } 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 922fb75f2a..a11ddffce8 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 @@ -18,7 +18,8 @@ package org.jackhuang.hmcl.download.forge; import org.jackhuang.hmcl.download.*; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -42,16 +43,16 @@ * * @author huangyuhui */ -public final class ForgeInstallTask extends Task { +public final class ForgeInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private Path installer; private final ForgeRemoteVersion remote; private FileDownloadTask dependent; - private Task dependency; + private Task dependency; - public ForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, ForgeRemoteVersion remoteVersion) { + public ForgeInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, ForgeRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; @@ -115,13 +116,13 @@ public void execute() throws IOException, VersionMismatchException, UnsupportedI * Detect Forge installer type. * * @param dependencyManager game repository - * @param version version.json + * @param version instance manifest * @param installer the Forge installer, either the new or old one. * @return true for new, false for old * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. * @throws VersionMismatchException if required game version of installer does not match the actual one. */ - public static boolean detectForgeInstallerType(DependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + public static boolean detectForgeInstallerType(DependencyManager dependencyManager, GameInstanceManifest version, Path installer) throws IOException, VersionMismatchException { Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); if (!gameVersion.isPresent()) throw new IOException(); try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { @@ -148,13 +149,13 @@ public static boolean detectForgeInstallerType(DependencyManager dependencyManag * This method will try to identify this installer whether it is in old or new format. * * @param dependencyManager game repository - * @param version version.json + * @param version instance manifest * @param installer the Forge installer, either the new or old one. * @return the task to install library * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. * @throws VersionMismatchException if required game version of installer does not match the actual one. */ - public static Task install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + public static Task install(DefaultDependencyManager dependencyManager, GameInstanceManifest version, Path installer) throws IOException, VersionMismatchException { Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); if (!gameVersion.isPresent()) throw new IOException(); try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java index ccea924d19..fdd9bdf612 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java @@ -24,11 +24,12 @@ import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask; import org.jackhuang.hmcl.game.Artifact; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.game.DownloadInfo; import org.jackhuang.hmcl.game.DownloadType; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; 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.DigestUtils; @@ -62,7 +63,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.gson.JsonUtils.fromNonNullJson; -public class ForgeNewInstallTask extends Task { +public class ForgeNewInstallTask extends Task { private class ProcessorTask extends Task { @@ -193,21 +194,21 @@ public void execute() throws Exception { } private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository gameRepository; - private final Version version; + private final DefaultGameRepository2 gameRepository; + private final GameInstanceManifest version; private final Path installer; private final List> dependents = new ArrayList<>(1); private final List> dependencies = new ArrayList<>(1); private ForgeNewInstallProfile profile; private List processors; - private Version forgeVersion; + private GameInstanceManifest forgeVersion; private final String selfVersion; private Path tempDir; private AtomicInteger processorDoneCount = new AtomicInteger(0); - public ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) { + public ForgeNewInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, String selfVersion, Path installer) { this.dependencyManager = dependencyManager; this.gameRepository = dependencyManager.getGameRepository(); this.version = version; @@ -296,7 +297,7 @@ public void preExecute() throws Exception { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { profile = JsonUtils.fromNonNullJson(Files.readString(fs.getPath("install_profile.json")), ForgeNewInstallProfile.class); processors = profile.getProcessors(); - forgeVersion = JsonUtils.fromNonNullJson(Files.readString(fs.getPath(profile.getJson())), Version.class); + forgeVersion = JsonUtils.fromNonNullJson(Files.readString(fs.getPath(profile.getJson())), GameInstanceManifest.class); for (Library library : profile.getLibraries()) { Path file = fs.getPath("maven").resolve(library.getPath()); @@ -356,7 +357,7 @@ private Task patchDownloadMojangMappingsTask(Processor processor, Map { - DownloadInfo mappings = fromNonNullJson(json, Version.class) + DownloadInfo mappings = fromNonNullJson(json, GameInstanceManifest.class) .getDownloads().get(DownloadType.CLIENT_MAPPINGS); if (mappings == null) { throw new Exception("client_mappings download info not found"); @@ -425,10 +426,11 @@ public void execute() throws Exception { processorsTask.thenComposeAsync( dependencyManager.checkLibraryCompletionAsync(forgeVersion, true))); - setResult(forgeVersion - .setPriority(Version.PRIORITY_LOADER) - .setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) - .setVersion(selfVersion)); + setResult(GameInstancePatch.fromManifest( + forgeVersion, + LibraryAnalyzer.LibraryType.FORGE.getPatchId(), + selfVersion, + GameInstancePatch.PRIORITY_LOADER)); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java index 61aa3ea335..b9269af72e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java @@ -20,8 +20,9 @@ import org.jackhuang.hmcl.download.ArtifactMalformedException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -35,15 +36,15 @@ import java.util.zip.ZipException; import java.util.zip.ZipFile; -public class ForgeOldInstallTask extends Task { +public class ForgeOldInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final Path installer; private final String selfVersion; private final List> dependencies = new ArrayList<>(1); - ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) { + ForgeOldInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, String selfVersion, Path installer) { this.dependencyManager = dependencyManager; this.version = version; this.installer = installer; @@ -81,10 +82,11 @@ public void execute() throws Exception { is.transferTo(os); } - setResult(installProfile.getVersionInfo() - .setPriority(Version.PRIORITY_LOADER) - .setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) - .setVersion(selfVersion)); + setResult(GameInstancePatch.fromManifest( + installProfile.getVersionInfo(), + LibraryAnalyzer.LibraryType.FORGE.getPatchId(), + selfVersion, + GameInstancePatch.PRIORITY_LOADER)); dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo(), true)); } catch (ZipException ex) { throw new ArtifactMalformedException("Malformed forge installer file", ex); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeRemoteVersion.java index 5550506fc6..34278a822c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; import java.time.Instant; @@ -39,7 +40,7 @@ public ForgeRemoteVersion(String gameVersion, String selfVersion, Instant releas } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new ForgeInstallTask(dependencyManager, baseVersion, this); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java index a366af04a1..09ca6af4ea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java @@ -22,7 +22,7 @@ import org.jackhuang.hmcl.game.AssetIndex; import org.jackhuang.hmcl.game.AssetIndexInfo; import org.jackhuang.hmcl.game.AssetObject; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.CacheRepository; @@ -45,7 +45,7 @@ public final class GameAssetDownloadTask extends Task { private final AbstractDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final AssetIndexInfo assetIndexInfo; private final Path assetIndexFile; private final boolean integrityCheck; @@ -55,10 +55,10 @@ public final class GameAssetDownloadTask extends Task { /** * Constructor. * - * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository} + * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository2} * @param version the game version */ - public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version, boolean forceDownloadingIndex, boolean integrityCheck) { + public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean forceDownloadingIndex, boolean integrityCheck) { this.dependencyManager = dependencyManager; this.version = version.resolve(dependencyManager.getGameRepository()); this.assetIndexInfo = this.version.getAssetIndex(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java index 88beb8b691..ac8ebb30dd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java @@ -21,7 +21,7 @@ import org.jackhuang.hmcl.download.AbstractDependencyManager; import org.jackhuang.hmcl.game.AssetIndex; import org.jackhuang.hmcl.game.AssetIndexInfo; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; @@ -44,17 +44,17 @@ public final class GameAssetIndexDownloadTask extends Task { private final AbstractDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final boolean forceDownloading; private final List> dependencies = new ArrayList<>(1); /** * Constructor. * - * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository} + * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository2} * @param version the resolved version */ - public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version, boolean forceDownloading) { + public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean forceDownloading) { this.dependencyManager = dependencyManager; this.version = version; this.forceDownloading = forceDownloading; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java index c50596836e..e16ffa963c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.download.game; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.CacheRepository; @@ -35,10 +35,10 @@ public final class GameDownloadTask extends Task { private final DefaultDependencyManager dependencyManager; private final String gameVersion; - private final Version version; + private final GameInstanceManifest version; private final List> dependencies = new ArrayList<>(); - public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) { + public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, GameInstanceManifest version) { this.dependencyManager = dependencyManager; this.gameVersion = gameVersion; this.version = version.resolve(dependencyManager.getGameRepository()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java index aea0e04542..898ace018a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java @@ -18,8 +18,9 @@ package org.jackhuang.hmcl.download.game; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -30,16 +31,16 @@ import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.MINECRAFT; -public class GameInstallTask extends Task { +public class GameInstallTask extends Task { - private final DefaultGameRepository gameRepository; + private final DefaultGameRepository2 gameRepository; private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final GameRemoteVersion remote; private final VersionJsonDownloadTask downloadTask; private final List> dependencies = new ArrayList<>(1); - public GameInstallTask(DefaultDependencyManager dependencyManager, Version version, GameRemoteVersion remoteVersion) { + public GameInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, GameRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.gameRepository = dependencyManager.getGameRepository(); this.version = version; @@ -64,11 +65,14 @@ public boolean isRelyingOnDependencies() { @Override public void execute() throws Exception { - Version patch = JsonUtils.fromNonNullJson(downloadTask.getResult(), Version.class) - .setId(MINECRAFT.getPatchId()).setVersion(remote.getGameVersion()).setJar(null).setPriority(Version.PRIORITY_MC); + GameInstancePatch patch = GameInstancePatch.fromManifest( + JsonUtils.fromNonNullJson(downloadTask.getResult(), GameInstanceManifest.class), + MINECRAFT.getPatchId(), + remote.getGameVersion(), + GameInstancePatch.PRIORITY_MC).setJar(null); setResult(patch); - Version version = new Version(this.version.getId()).addPatch(patch); + GameInstanceManifest version = new GameInstanceManifest(this.version.getId()).addPatch(patch); dependencies.add(Task.allOf( new GameDownloadTask(dependencyManager, remote.getGameVersion(), version), Task.allOf( 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 d7305f1cc1..3b80bbb2af 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 @@ -20,10 +20,10 @@ import org.jackhuang.hmcl.download.AbstractDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.MaintainTask; -import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.game.GameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameRepository2; 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.DigestUtils; @@ -55,7 +55,7 @@ public final class GameLibrariesTask extends Task { private final AbstractDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final boolean integrityCheck; private final List libraries; private final List> dependencies = new ArrayList<>(); @@ -63,20 +63,20 @@ public final class GameLibrariesTask extends Task { /** * Constructor. * - * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository} + * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository2} * @param version the game version */ - public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version, boolean integrityCheck) { + public GameLibrariesTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean integrityCheck) { this(dependencyManager, version, integrityCheck, version.resolve(dependencyManager.getGameRepository()).getLibraries()); } /** * Constructor. * - * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository} + * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository2} * @param version the game version */ - public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version, boolean integrityCheck, List libraries) { + public GameLibrariesTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean integrityCheck, List libraries) { this.dependencyManager = dependencyManager; this.version = version; this.integrityCheck = integrityCheck; @@ -91,7 +91,7 @@ public List> getDependencies() { return dependencies; } - public static boolean shouldDownloadLibrary(GameRepository gameRepository, Version version, Library library, boolean integrityCheck) { + public static boolean shouldDownloadLibrary(GameRepository2 gameRepository, GameInstanceManifest version, Library library, boolean integrityCheck) { Path file = gameRepository.getLibraryFile(version, library); if (!Files.isRegularFile(file)) return true; @@ -135,7 +135,7 @@ private static boolean shouldDownloadFMLLib(FMLLib fmlLib, Path file) { @Override public void execute() throws IOException { int progress = 0; - GameRepository gameRepository = dependencyManager.getGameRepository(); + GameRepository2 gameRepository = dependencyManager.getGameRepository(); for (Library library : libraries) { if (!library.appliesToCurrentEnvironment()) { continue; @@ -143,7 +143,7 @@ public void execute() throws IOException { // https://github.com/HMCL-dev/HMCL/issues/3975 if ("net.minecraftforge".equals(library.getGroupId()) && "minecraftforge".equals(library.getArtifactId()) - && gameRepository instanceof DefaultGameRepository defaultGameRepository) { + && gameRepository instanceof DefaultGameRepository2 defaultGameRepository) { List fmlLibs = getFMLLibs(library.getVersion()); if (fmlLibs != null) { Path libDir = defaultGameRepository.getBaseDirectory().resolve("lib") @@ -162,7 +162,7 @@ public void execute() throws IOException { } Path file = gameRepository.getLibraryFile(version, library); - if ("optifine".equals(library.getGroupId()) && Files.exists(file) && GameVersionNumber.asGameVersion(gameRepository.getGameVersion(version)).compareTo("1.20.4") == 0) { + if ("optifine".equals(library.getGroupId()) && Files.exists(file) && GameVersionNumber.asGameVersion(gameRepository.getGameVersion(version).orElse(null)).compareTo("1.20.4") == 0) { String forgeVersion = LibraryAnalyzer.analyze(version, "1.20.4") .getVersion(LibraryAnalyzer.LibraryType.FORGE) .orElse(null); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersion.java index 43ef82d58a..386ac9367a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersion.java @@ -20,8 +20,9 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.game.ReleaseType; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; @@ -48,7 +49,7 @@ public ReleaseType getType() { } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new GameInstallTask(dependencyManager, baseVersion, this); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java index 33407066e5..cc52d93714 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java @@ -19,7 +19,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; @@ -40,18 +40,14 @@ public final class GameVerificationFixTask extends Task { private final DefaultDependencyManager dependencyManager; private final String gameVersion; - private final Version version; + private final GameInstanceManifest version; private final List> dependencies = new ArrayList<>(); - public GameVerificationFixTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) { + public GameVerificationFixTask(DefaultDependencyManager dependencyManager, String gameVersion, GameInstanceManifest version) { this.dependencyManager = dependencyManager; this.gameVersion = gameVersion; this.version = version; - if (!version.isResolved()) { - throw new IllegalArgumentException("GameVerificationFixTask requires a resolved game version"); - } - setSignificance(TaskSignificance.MODERATE); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java deleted file mode 100644 index c60265947b..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.download.game; - -import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.util.gson.JsonUtils; - -import java.nio.file.Files; -import java.nio.file.Path; - -/** - * This task is to save the version json. - * - * @author huangyuhui - */ -public final class VersionJsonSaveTask extends Task { - - private final DefaultGameRepository repository; - private final Version version; - - /** - * Constructor. - * - * @param repository the game repository - * @param version the game version - */ - public VersionJsonSaveTask(DefaultGameRepository repository, Version version) { - this.repository = repository; - this.version = version; - - setSignificance(TaskSignificance.MODERATE); - setResult(version); - } - - @Override - public void execute() throws Exception { - Path json = repository.getVersionJson(version.getId()).toAbsolutePath(); - Files.createDirectories(json.getParent()); - JsonUtils.writeToJsonFile(json, version); - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java index 1a19df5953..556d9743a0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java @@ -18,7 +18,8 @@ package org.jackhuang.hmcl.download.legacyfabric; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; @@ -27,14 +28,14 @@ import java.util.Collection; import java.util.List; -public final class LegacyFabricAPIInstallTask extends Task { +public final class LegacyFabricAPIInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final LegacyFabricAPIRemoteVersion remote; private final List> dependencies = new ArrayList<>(1); - public LegacyFabricAPIInstallTask(DefaultDependencyManager dependencyManager, Version version, LegacyFabricAPIRemoteVersion remoteVersion) { + public LegacyFabricAPIInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, LegacyFabricAPIRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIRemoteVersion.java index 1ae30d4885..fbf157f14e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.Task; @@ -55,7 +56,7 @@ public RemoteAddon.Version getVersion() { } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new LegacyFabricAPIInstallTask(dependencyManager, baseVersion, this); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricInstallTask.java index f2abb9c33a..c404e0c752 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricInstallTask.java @@ -24,8 +24,9 @@ import org.jackhuang.hmcl.download.fabric.FabricInstallTask; import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.Artifact; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -35,15 +36,15 @@ import java.util.Collections; import java.util.List; -public final class LegacyFabricInstallTask extends Task { +public final class LegacyFabricInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final LegacyFabricRemoteVersion remote; private final GetTask launchMetaTask; private final List> dependencies = new ArrayList<>(1); - public LegacyFabricInstallTask(DefaultDependencyManager dependencyManager, Version version, LegacyFabricRemoteVersion remoteVersion) { + public LegacyFabricInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, LegacyFabricRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; @@ -76,10 +77,10 @@ public boolean isRelyingOnDependencies() { public void execute() { setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), FabricInstallTask.FabricInfo.class), remote.getGameVersion(), remote.getSelfVersion())); - dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true)); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); } - private Version getPatch(FabricInstallTask.FabricInfo legacyFabricInfo, String gameVersion, String loaderVersion) { + private GameInstancePatch getPatch(FabricInstallTask.FabricInfo legacyFabricInfo, String gameVersion, String loaderVersion) { JsonObject launcherMeta = legacyFabricInfo.getLauncherMeta(); Arguments arguments = new Arguments(); @@ -110,7 +111,7 @@ private Version getPatch(FabricInstallTask.FabricInfo legacyFabricInfo, String g libraries.add(new Library(Artifact.fromDescriptor(legacyFabricInfo.getIntermediary().getMaven()), getMavenRepositoryByGroup(legacyFabricInfo.getIntermediary().getMaven()), null)); libraries.add(new Library(Artifact.fromDescriptor(legacyFabricInfo.getLoader().getMaven()), getMavenRepositoryByGroup(legacyFabricInfo.getLoader().getMaven()), null)); - return new Version(LibraryAnalyzer.LibraryType.LEGACY_FABRIC.getPatchId(), loaderVersion, Version.PRIORITY_LOADER, arguments, mainClass, libraries); + return new GameInstancePatch(LibraryAnalyzer.LibraryType.LEGACY_FABRIC.getPatchId(), loaderVersion, GameInstancePatch.PRIORITY_LOADER, arguments, mainClass, libraries); } private static String getMavenRepositoryByGroup(String maven) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricRemoteVersion.java index cd29f0fddd..f8a1e88c88 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; import java.util.List; @@ -38,7 +39,7 @@ public class LegacyFabricRemoteVersion extends RemoteVersion { } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new LegacyFabricInstallTask(dependencyManager, baseVersion, this); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java index 9d5113c2bd..74938865cd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java @@ -33,15 +33,15 @@ * * @author huangyuhui */ -public final class LiteLoaderInstallTask extends Task { +public final class LiteLoaderInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final LiteLoaderRemoteVersion remote; private final List> dependents = new ArrayList<>(); private final List> dependencies = new ArrayList<>(1); - public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, Version version, LiteLoaderRemoteVersion remoteVersion) { + public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, LiteLoaderRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; @@ -65,7 +65,7 @@ public void execute() { new LibrariesDownloadInfo(new LibraryDownloadInfo(null, remote.getUrls().get(0))) ); - setResult(new Version(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), + setResult(new GameInstancePatch(LibraryAnalyzer.LibraryType.LITELOADER.getPatchId(), remote.getSelfVersion(), 60000, new Arguments().addGameArguments("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker"), @@ -74,7 +74,7 @@ public void execute() { .setLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration ); - dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true)); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderRemoteVersion.java index ee7e05009c..e2da5e16f8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderRemoteVersion.java @@ -20,8 +20,9 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.Task; import java.util.Collection; @@ -54,7 +55,7 @@ public String getTweakClass() { } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new LiteLoaderInstallTask(dependencyManager, baseVersion, this); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java index c5614c3e9d..1d53f6402b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java @@ -21,7 +21,8 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.VersionMismatchException; import org.jackhuang.hmcl.download.forge.*; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -36,10 +37,10 @@ import static org.jackhuang.hmcl.util.StringUtils.removePrefix; import static org.jackhuang.hmcl.util.StringUtils.removeSuffix; -public final class NeoForgeInstallTask extends Task { +public final class NeoForgeInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final NeoForgeRemoteVersion remoteVersion; @@ -47,9 +48,9 @@ public final class NeoForgeInstallTask extends Task { private FileDownloadTask dependent; - private Task dependency; + private Task dependency; - public NeoForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, NeoForgeRemoteVersion remoteVersion) { + public NeoForgeInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, NeoForgeRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remoteVersion = remoteVersion; @@ -99,7 +100,7 @@ public void execute() throws Exception { dependency = install(dependencyManager, version, installer); } - public static Task install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + public static Task install(DefaultDependencyManager dependencyManager, GameInstanceManifest version, Path installer) throws IOException, VersionMismatchException { Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); if (!gameVersion.isPresent()) throw new IOException(); try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java index 2a7912aeff..c0710ba317 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java @@ -57,7 +57,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.gson.JsonUtils.fromNonNullJson; -public class NeoForgeOldInstallTask extends Task { +public class NeoForgeOldInstallTask extends Task { private class ProcessorTask extends Task { @@ -172,21 +172,21 @@ public void execute() throws Exception { } private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository gameRepository; - private final Version version; + private final DefaultGameRepository2 gameRepository; + private final GameInstanceManifest version; private final Path installer; private final List> dependents = new ArrayList<>(1); private final List> dependencies = new ArrayList<>(1); private ForgeNewInstallProfile profile; private List processors; - private Version neoForgeVersion; + private GameInstanceManifest neoForgeVersion; private final String selfVersion; private Path tempDir; private AtomicInteger processorDoneCount = new AtomicInteger(0); - NeoForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, String selfVersion, Path installer) { + NeoForgeOldInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, String selfVersion, Path installer) { this.dependencyManager = dependencyManager; this.gameRepository = dependencyManager.getGameRepository(); this.version = version; @@ -275,7 +275,7 @@ public void preExecute() throws Exception { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { profile = JsonUtils.fromNonNullJson(Files.readString(fs.getPath("install_profile.json")), ForgeNewInstallProfile.class); processors = profile.getProcessors(); - neoForgeVersion = JsonUtils.fromNonNullJson(Files.readString(fs.getPath(profile.getJson())), Version.class); + neoForgeVersion = JsonUtils.fromNonNullJson(Files.readString(fs.getPath(profile.getJson())), GameInstanceManifest.class); for (Library library : profile.getLibraries()) { Path file = fs.getPath("maven").resolve(library.getPath()); @@ -335,7 +335,7 @@ private Task patchDownloadMojangMappingsTask(Processor processor, Map { - DownloadInfo mappings = fromNonNullJson(json, Version.class) + DownloadInfo mappings = fromNonNullJson(json, GameInstanceManifest.class) .getDownloads().get(DownloadType.CLIENT_MAPPINGS); if (mappings == null) { throw new Exception("client_mappings download info not found"); @@ -404,10 +404,11 @@ public void execute() throws Exception { processorsTask.thenComposeAsync( dependencyManager.checkLibraryCompletionAsync(neoForgeVersion, true))); - setResult(neoForgeVersion - .setPriority(Version.PRIORITY_LOADER) - .setId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId()) - .setVersion(selfVersion)); + setResult(GameInstancePatch.fromManifest( + neoForgeVersion, + LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId(), + selfVersion, + GameInstancePatch.PRIORITY_LOADER)); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeRemoteVersion.java index 7f6e500eac..9582c6c7f7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; import java.util.List; @@ -31,7 +32,7 @@ public NeoForgeRemoteVersion(String gameVersion, String selfVersion, List getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new NeoForgeInstallTask(dependencyManager, baseVersion, this); } 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 f2a87307ef..c87a38b138 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 @@ -48,11 +48,11 @@ * * @author huangyuhui */ -public final class OptiFineInstallTask extends Task { +public final class OptiFineInstallTask extends Task { - private final DefaultGameRepository gameRepository; + private final DefaultGameRepository2 gameRepository; private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final OptiFineRemoteVersion remote; private final Path installer; private final List> dependents = new ArrayList<>(0); @@ -62,11 +62,11 @@ public final class OptiFineInstallTask extends Task { private final Library optiFineLibrary; private final Library optiFineInstallerLibrary; - public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) { + public OptiFineInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, OptiFineRemoteVersion remoteVersion) { this(dependencyManager, version, remoteVersion, null); } - public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion, Path installer) { + public OptiFineInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, OptiFineRemoteVersion remoteVersion, Path installer) { this.dependencyManager = dependencyManager; this.gameRepository = dependencyManager.getGameRepository(); this.version = version; @@ -207,7 +207,7 @@ public void execute() throws Exception { libraries.add(new Library(new Artifact("net.minecraft", "launchwrapper", "1.12"))); } - setResult(new Version( + setResult(new GameInstancePatch( LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), remote.getSelfVersion(), 10000, @@ -216,20 +216,20 @@ public void execute() throws Exception { libraries )); - dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true)); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); } /** * Install OptiFine library from existing local file. * * @param dependencyManager game repository - * @param version version.json + * @param version instance manifest * @param installer the OptiFine installer * @return the task to install library * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. * @throws VersionMismatchException if required game version of installer does not match the actual one. */ - public static Task install(DefaultDependencyManager dependencyManager, Version version, Path installer) throws IOException, VersionMismatchException { + public static Task install(DefaultDependencyManager dependencyManager, GameInstanceManifest version, Path installer) throws IOException, VersionMismatchException { Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); if (!gameVersion.isPresent()) throw new IOException(); try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineRemoteVersion.java index 2efdf48ee5..abf36261e7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; import java.util.List; @@ -37,7 +38,7 @@ public String getFullVersion() { } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new OptiFineInstallTask(dependencyManager, baseVersion, this); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java index 245280e58b..1380181e8e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java @@ -18,7 +18,8 @@ package org.jackhuang.hmcl.download.quilt; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; @@ -32,14 +33,14 @@ * * @author huangyuhui */ -public final class QuiltAPIInstallTask extends Task { +public final class QuiltAPIInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final QuiltAPIRemoteVersion remote; private final List> dependencies = new ArrayList<>(1); - public QuiltAPIInstallTask(DefaultDependencyManager dependencyManager, Version version, QuiltAPIRemoteVersion remoteVersion) { + public QuiltAPIInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, QuiltAPIRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIRemoteVersion.java index bf229454a2..2ccc82a7c2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.Task; @@ -55,7 +56,7 @@ public RemoteAddon.Version getVersion() { } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new QuiltAPIInstallTask(dependencyManager, baseVersion, this); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java index 01642cecf0..03ec4202ee 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java @@ -24,8 +24,9 @@ import org.jackhuang.hmcl.download.UnsupportedInstallationException; import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.Artifact; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonSerializable; @@ -40,15 +41,15 @@ * * @author huangyuhui */ -public final class QuiltInstallTask extends Task { +public final class QuiltInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final Version version; + private final GameInstanceManifest version; private final QuiltRemoteVersion remote; private final GetTask launchMetaTask; private final List> dependencies = new ArrayList<>(1); - public QuiltInstallTask(DefaultDependencyManager dependencyManager, Version version, QuiltRemoteVersion remoteVersion) { + public QuiltInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, QuiltRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.version = version; this.remote = remoteVersion; @@ -87,10 +88,10 @@ public boolean isRelyingOnDependencies() { public void execute() { setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), QuiltInfo.class), remote.getGameVersion(), remote.getSelfVersion())); - dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true)); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); } - private Version getPatch(QuiltInfo quiltInfo, String gameVersion, String loaderVersion) { + private GameInstancePatch getPatch(QuiltInfo quiltInfo, String gameVersion, String loaderVersion) { JsonObject launcherMeta = quiltInfo.launcherMeta; Arguments arguments = new Arguments(); @@ -121,7 +122,7 @@ private Version getPatch(QuiltInfo quiltInfo, String gameVersion, String loaderV libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.intermediary.maven), getMavenRepositoryByGroup(quiltInfo.intermediary.maven), null)); libraries.add(new Library(Artifact.fromDescriptor(quiltInfo.loader.maven), getMavenRepositoryByGroup(quiltInfo.loader.maven), null)); - return new Version(LibraryAnalyzer.LibraryType.QUILT.getPatchId(), loaderVersion, Version.PRIORITY_LOADER, arguments, mainClass, libraries); + return new GameInstancePatch(LibraryAnalyzer.LibraryType.QUILT.getPatchId(), loaderVersion, GameInstancePatch.PRIORITY_LOADER, arguments, mainClass, libraries); } private static String getMavenRepositoryByGroup(String maven) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltRemoteVersion.java index 979e7f794d..bd86cd24ea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltRemoteVersion.java @@ -20,7 +20,8 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.RemoteVersion; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; import java.util.List; @@ -38,7 +39,7 @@ public class QuiltRemoteVersion extends RemoteVersion { } @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Version baseVersion) { + public Task getInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest baseVersion) { return new QuiltInstallTask(dependencyManager, baseVersion, this); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/GameJsonParseFailedEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/GameJsonParseFailedEvent.java index dfd1a05bdf..27385ff085 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/GameJsonParseFailedEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/GameJsonParseFailedEvent.java @@ -34,7 +34,7 @@ public final class GameJsonParseFailedEvent extends Event { /** * - * @param source {@link org.jackhuang.hmcl.game.DefaultGameRepository} + * @param source {@link org.jackhuang.hmcl.game.DefaultGameRepository2} * @param jsonFile the minecraft.json file. * @param version the version name */ diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/LoadedOneVersionEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/LoadedOneVersionEvent.java deleted file mode 100644 index cedba1a294..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/LoadedOneVersionEvent.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.event; - -import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.util.ToStringBuilder; - -/** - * This event gets fired when a minecraft version has been loaded. - *
- * This event is fired on the {@link org.jackhuang.hmcl.event.EventBus#EVENT_BUS} - * - * @author huangyuhui - */ -public final class LoadedOneVersionEvent extends Event { - - private final Version version; - - /** - * - * @param source {@link org.jackhuang.hmcl.game.GameRepository} - * @param version the version id. - */ - public LoadedOneVersionEvent(Object source, Version version) { - super(source); - this.version = version; - } - - public Version getVersion() { - return version; - } - - @Override - public boolean hasResult() { - return true; - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .append("source", source) - .append("version", version) - .toString(); - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java index 81f48f825d..1de5618995 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java @@ -29,7 +29,7 @@ public final class RefreshedVersionsEvent extends Event { /** * Constructor. * - * @param source {@link org.jackhuang.hmcl.game.GameRepository} + * @param source {@link org.jackhuang.hmcl.game.GameRepository2} */ public RefreshedVersionsEvent(Object source) { super(source); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshingVersionsEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshingVersionsEvent.java index ee14df07cf..4c2c6813e1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshingVersionsEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshingVersionsEvent.java @@ -29,7 +29,7 @@ public final class RefreshingVersionsEvent extends Event { /** * Constructor. * - * @param source {@link org.jackhuang.hmcl.game.GameRepository} + * @param source {@link org.jackhuang.hmcl.game.GameRepository2} */ public RefreshingVersionsEvent(Object source) { super(source); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RemoveVersionEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RemoveVersionEvent.java index a3ccae93e1..676ad4e1de 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RemoveVersionEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RemoveVersionEvent.java @@ -32,7 +32,7 @@ public class RemoveVersionEvent extends Event { /** * - * @param source {@link org.jackhuang.hmcl.game.GameRepository} + * @param source {@link org.jackhuang.hmcl.game.GameRepository2} * @param version the version id. */ public RemoveVersionEvent(Object source, String version) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RenameVersionEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RenameVersionEvent.java index 820404222e..9a0d9a4133 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RenameVersionEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RenameVersionEvent.java @@ -32,7 +32,7 @@ public class RenameVersionEvent extends Event { /** * - * @param source {@link org.jackhuang.hmcl.game.GameRepository} + * @param source {@link org.jackhuang.hmcl.game.GameRepository2} * @param from the version id. */ public RenameVersionEvent(Object source, String from, String to) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ClassicVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ClassicVersion.java deleted file mode 100644 index 17bf520673..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ClassicVersion.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.Instant; -import java.util.Arrays; - -/// The Minecraft version for 1.5.x and earlier. -/// -/// @author huangyuhui -public final class ClassicVersion extends Version { - - public ClassicVersion() { - super(true, "Classic", null, null, "${auth_player_name} ${auth_session} --workDir ${game_directory}", - null, "net.minecraft.client.Minecraft", null, null, null, null, null, null, - Arrays.asList(new ClassicLibrary("lwjgl"), new ClassicLibrary("jinput"), new ClassicLibrary("lwjgl_util")), - null, null, null, ReleaseType.UNKNOWN, Instant.now(), Instant.now(), 0, false, false, null); - } - - private static final class ClassicLibrary extends Library { - public ClassicLibrary(String name) { - super(new Artifact("", "", ""), null, - new LibrariesDownloadInfo(new LibraryDownloadInfo("bin/" + name + ".jar"), null), - null, null, null, null, null, null); - } - } - - public static boolean hasClassicVersion(Path baseDirectory) { - Path bin = baseDirectory.resolve("bin"); - return Files.isDirectory(bin) - && Files.exists(bin.resolve("lwjgl.jar")) - && Files.exists(bin.resolve("jinput.jar")) - && Files.exists(bin.resolve("lwjgl_util.jar")); - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java deleted file mode 100644 index fb3f606081..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -import com.google.gson.JsonParseException; -import org.jackhuang.hmcl.download.MaintainTask; -import org.jackhuang.hmcl.download.game.VersionJsonSaveTask; -import org.jackhuang.hmcl.event.*; -import org.jackhuang.hmcl.game.tlauncher.TLauncherVersion; -import org.jackhuang.hmcl.addon.mod.ModManager; -import org.jackhuang.hmcl.modpack.ModpackConfiguration; -import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.ToStringBuilder; -import org.jackhuang.hmcl.util.gson.JsonUtils; -import org.jackhuang.hmcl.util.io.FileUtils; -import org.jackhuang.hmcl.util.platform.Platform; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Stream; - -import static org.jackhuang.hmcl.util.logging.Logger.LOG; - -/** - * An implementation of classic Minecraft game repository. - * - * @author huangyuhui - */ -public class DefaultGameRepository implements GameRepository { - - private Path baseDirectory; - protected Map versions; - private final ConcurrentHashMap> gameVersions = new ConcurrentHashMap<>(); - - public DefaultGameRepository(Path baseDirectory) { - this.baseDirectory = baseDirectory; - } - - public Path getBaseDirectory() { - return baseDirectory; - } - - public void setBaseDirectory(Path baseDirectory) { - this.baseDirectory = baseDirectory; - } - - @Override - public boolean hasVersion(String id) { - return id != null && versions != null && versions.containsKey(id); - } - - @Override - public Version getVersion(String id) { - if (!hasVersion(id)) - throw new VersionNotFoundException("Version '" + id + "' does not exist in " + (versions == null ? "[]" : versions.keySet()) + "."); - return versions.get(id); - } - - @Override - public int getVersionCount() { - return versions == null ? 0 : versions.size(); - } - - @Override - public Collection getVersions() { - return versions == null ? Collections.emptySet() : versions.values(); - } - - @Override - public Path getLibrariesDirectory(Version version) { - return getBaseDirectory().resolve("libraries"); - } - - @Override - public Path getLibraryFile(Version version, Library lib) { - if ("local".equals(lib.getHint())) { - if (lib.getFileName() != null) { - return getVersionRoot(version.getId()).resolve("libraries/" + lib.getFileName()); - } - - return getVersionRoot(version.getId()).resolve("libraries/" + lib.getArtifact().getFileName()); - } - - return getLibrariesDirectory(version).resolve(lib.getPath()); - } - - public Path getArtifactFile(Version version, Artifact artifact) { - return artifact.getPath(getBaseDirectory().resolve("libraries")); - } - - @Override - public Path getRunDirectory(String id) { - return getBaseDirectory(); - } - - @Override - public Path getVersionJar(Version version) { - Version v = version.resolve(this); - String id = Optional.ofNullable(v.getJar()).orElse(v.getId()); - return getVersionRoot(id).resolve(id + ".jar"); - } - - @Override - public Optional getGameVersion(Version version) { - // This implementation may cause multiple flows against the same version entering - // this function, which is accepted because GameVersion::minecraftVersion should - // be consistent. - return gameVersions.computeIfAbsent(getVersionJar(version), versionJar -> { - Optional gameVersion = GameVersion.minecraftVersion(versionJar); - if (gameVersion.isEmpty()) { - LOG.warning("Cannot find out game version of " + version.getId() + ", primary jar: " + versionJar.toString() + ", jar exists: " + Files.exists(versionJar)); - } - return gameVersion; - }); - } - - @Override - public Path getNativeDirectory(String id, Platform platform) { - return getVersionRoot(id).resolve("natives-" + platform); - } - - @Override - public Path getModsDirectory(String id) { - return getRunDirectory(id).resolve("mods"); - } - - @Override - public Path getResourcePackDirectory(String id) { - return getRunDirectory(id).resolve("resourcepacks"); - } - - @Override - public Path getVersionRoot(String id) { - return getBaseDirectory().resolve("versions/" + id); - } - - public Path getVersionJson(String id) { - return getVersionRoot(id).resolve(id + ".json"); - } - - public Version readVersionJson(String id) throws IOException, JsonParseException { - return readVersionJson(getVersionJson(id)); - } - - public Version readVersionJson(Path file) throws IOException, JsonParseException { - String jsonText = Files.readString(file); - try { - // Try TLauncher version json format - return JsonUtils.fromNonNullJson(jsonText, TLauncherVersion.class).toVersion(); - } catch (JsonParseException ignored) { - } - - try { - // Try official version json format - return JsonUtils.fromNonNullJson(jsonText, Version.class); - } catch (JsonParseException ignored) { - } - - LOG.warning("Cannot parse version json: " + file + "\n" + jsonText); - throw new JsonParseException("Version json incorrect"); - } - - @Override - public boolean renameVersion(String from, String to) { - if (EventBus.EVENT_BUS.fireEvent(new RenameVersionEvent(this, from, to)) == Event.Result.DENY) - return false; - - try { - Version fromVersion = getVersion(from); - Path fromDir = getVersionRoot(from); - Path toDir = getVersionRoot(to); - Files.move(fromDir, toDir); - - Path fromJson = toDir.resolve(from + ".json"); - Path fromJar = toDir.resolve(from + ".jar"); - Path toJson = toDir.resolve(to + ".json"); - Path toJar = toDir.resolve(to + ".jar"); - - boolean hasJarFile = Files.exists(fromJar); - - try { - Files.move(fromJson, toJson); - if (hasJarFile) Files.move(fromJar, toJar); - } catch (IOException e) { - // recovery - Lang.ignoringException(() -> Files.move(toJson, fromJson)); - if (hasJarFile) Lang.ignoringException(() -> Files.move(toJar, fromJar)); - Lang.ignoringException(() -> Files.move(toDir, fromDir)); - throw e; - } - - if (fromVersion.getId().equals(fromVersion.getJar())) - fromVersion = fromVersion.setJar(null); - JsonUtils.writeToJsonFile(toJson, fromVersion.setId(to)); - - // fix inheritsFrom of versions that inherits from version [from]. - for (Version version : getVersions()) { - if (from.equals(version.getInheritsFrom())) { - Path targetPath = getVersionJson(version.getId()); - Files.createDirectories(targetPath.getParent()); - JsonUtils.writeToJsonFile(targetPath, version.setInheritsFrom(to)); - } - } - return true; - } catch (IOException | JsonParseException | VersionNotFoundException | InvalidPathException e) { - LOG.warning("Unable to rename version " + from + " to " + to, e); - return false; - } - } - - public boolean removeVersionFromDisk(String id) { - if (EventBus.EVENT_BUS.fireEvent(new RemoveVersionEvent(this, id)) == Event.Result.DENY) - return false; - if (!versions.containsKey(id)) - return FileUtils.deleteDirectoryQuietly(getVersionRoot(id)); - Path file = getVersionRoot(id); - if (Files.notExists(file)) - return true; - // test if no file in this version directory is occupied. - Path removedFile = file.toAbsolutePath().resolveSibling(FileUtils.getName(file) + "_removed"); - try { - Files.move(file, removedFile, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - LOG.warning("Failed to rename file " + file, e); - return false; - } - - try { - versions.remove(id); - - if (FileUtils.moveToTrash(removedFile)) { - return true; - } - - // remove json files first to ensure HMCL will not recognize this folder as a valid version. - - for (Path path : FileUtils.listFilesByExtension(removedFile, "json")) { - try { - Files.delete(path); - } catch (IOException e) { - LOG.warning("Failed to delete file " + path, e); - } - } - - // remove the version from version list regardless of whether the directory was removed successfully or not. - try { - FileUtils.deleteDirectory(removedFile); - } catch (IOException e) { - LOG.warning("Unable to remove version folder: " + file, e); - } - return true; - } finally { - refreshVersionsAsync().start(); - } - } - - protected void refreshVersionsImpl() { - Map versions = new TreeMap<>(); - - if (ClassicVersion.hasClassicVersion(getBaseDirectory())) { - Version version = new ClassicVersion(); - versions.put(version.getId(), version); - } - - SimpleVersionProvider provider = new SimpleVersionProvider(); - - Path versionsDir = getBaseDirectory().resolve("versions"); - if (Files.isDirectory(versionsDir)) { - try (Stream stream = Files.list(versionsDir)) { - stream.parallel().filter(Files::isDirectory).flatMap(dir -> { - String id = FileUtils.getName(dir); - Path json = dir.resolve(id + ".json"); - - // If user renamed the json file by mistake or created the json file in a wrong name, - // we will find the only json and rename it to correct name. - if (Files.notExists(json)) { - List jsons = FileUtils.listFilesByExtension(dir, "json"); - if (jsons.size() == 1) { - LOG.info("Renaming json file " + jsons.get(0) + " to " + json); - - try { - Files.move(jsons.get(0), json); - } catch (IOException e) { - LOG.warning("Cannot rename json file, ignoring version " + id, e); - return Stream.empty(); - } - - Path jar = dir.resolve(FileUtils.getNameWithoutExtension(jsons.get(0)) + ".jar"); - if (Files.exists(jar)) { - try { - Files.move(jar, dir.resolve(id + ".jar")); - } catch (IOException e) { - LOG.warning("Cannot rename jar file, ignoring version " + id, e); - return Stream.empty(); - } - } - } else { - LOG.info("No available json file found, ignoring version " + id); - return Stream.empty(); - } - } - - Version version; - try { - version = readVersionJson(json); - } catch (Exception e) { - LOG.warning("Malformed version json " + id, e); - // JsonSyntaxException or IOException or NullPointerException(!!) - if (EventBus.EVENT_BUS.fireEvent(new GameJsonParseFailedEvent(this, json, id)) != Event.Result.ALLOW) - return Stream.empty(); - - try { - version = readVersionJson(json); - } catch (Exception e2) { - LOG.error("User corrected version json is still malformed", e2); - return Stream.empty(); - } - } - - if (!id.equals(version.getId())) { - try { - String from = id; - String to = version.getId(); - Path fromDir = getVersionRoot(from); - Path toDir = getVersionRoot(to); - Files.move(fromDir, toDir); - - Path fromJson = toDir.resolve(from + ".json"); - Path fromJar = toDir.resolve(from + ".jar"); - Path toJson = toDir.resolve(to + ".json"); - Path toJar = toDir.resolve(to + ".jar"); - - try { - Files.move(fromJson, toJson); - if (Files.exists(fromJar)) - Files.move(fromJar, toJar); - } catch (IOException e) { - // recovery - Lang.ignoringException(() -> Files.move(toJson, fromJson)); - Lang.ignoringException(() -> Files.move(toJar, fromJar)); - Lang.ignoringException(() -> Files.move(toDir, fromDir)); - throw e; - } - } catch (IOException e) { - LOG.warning("Ignoring version " + version.getId() + " because version id does not match folder name " + id + ", and we cannot correct it.", e); - return Stream.empty(); - } - } - - return Stream.of(version); - }).forEachOrdered(provider::addVersion); - } catch (IOException e) { - LOG.warning("Failed to load versions from " + versionsDir, e); - } - } - - for (Version version : provider.getVersionMap().values()) { - try { - Version resolved = version.resolve(provider); - - if (resolved.appliesToCurrentEnvironment() && - EventBus.EVENT_BUS.fireEvent(new LoadedOneVersionEvent(this, resolved)) != Event.Result.DENY) - versions.put(version.getId(), version); - } catch (VersionNotFoundException e) { - LOG.warning("Ignoring version " + version.getId() + " because it inherits from a nonexistent version."); - } - } - - this.gameVersions.clear(); - this.versions = versions; - } - - @Override - public void refreshVersions() { - if (EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this)) == Event.Result.DENY) - return; - - refreshVersionsImpl(); - EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this)); - } - - @Override - public AssetIndex getAssetIndex(String version, String assetId) throws IOException { - try { - return Objects.requireNonNull(JsonUtils.fromJsonFile(getIndexFile(version, assetId), AssetIndex.class)); - } catch (JsonParseException | NullPointerException e) { - throw new IOException("Asset index file malformed", e); - } - } - - @Override - public Path getActualAssetDirectory(String version, String assetId) { - try { - return reconstructAssets(version, assetId); - } catch (IOException | JsonParseException e) { - LOG.error("Unable to reconstruct asset directory", e); - return getAssetDirectory(version, assetId); - } - } - - @Override - public Path getAssetDirectory(String version, String assetId) { - return getBaseDirectory().resolve("assets"); - } - - @Override - public Optional getAssetObject(String version, String assetId, String name) throws IOException { - try { - AssetObject assetObject = getAssetIndex(version, assetId).getObjects().get(name); - if (assetObject == null) return Optional.empty(); - return Optional.of(getAssetObject(version, assetId, assetObject)); - } catch (IOException e) { - throw e; - } catch (Exception e) { - throw new IOException("Unrecognized asset object " + name + " in asset " + assetId + " of version " + version, e); - } - } - - @Override - public Path getAssetObject(String version, String assetId, AssetObject obj) { - return getAssetObject(version, getAssetDirectory(version, assetId), obj); - } - - public Path getAssetObject(String version, Path assetDir, AssetObject obj) { - return assetDir.resolve("objects").resolve(obj.getLocation()); - } - - @Override - public Path getIndexFile(String version, String assetId) { - return getAssetDirectory(version, assetId).resolve("indexes").resolve(assetId + ".json"); - } - - @Override - public Path getLoggingObject(String version, String assetId, LoggingInfo loggingInfo) { - return getAssetDirectory(version, assetId).resolve("log_configs").resolve(loggingInfo.file().getId()); - } - - protected Path reconstructAssets(String version, String assetId) throws IOException, JsonParseException { - Path assetsDir = getAssetDirectory(version, assetId); - Path indexFile = getIndexFile(version, assetId); - Path virtualRoot = assetsDir.resolve("virtual").resolve(assetId); - - if (!Files.isRegularFile(indexFile)) - return assetsDir; - - AssetIndex index = JsonUtils.fromJsonFile(indexFile, AssetIndex.class); - - if (index == null) - return assetsDir; - - if (index.isVirtual()) { - Path resourcesDir = getRunDirectory(version).resolve("resources"); - - int cnt = 0; - int tot = index.getObjects().size(); - for (Map.Entry entry : index.getObjects().entrySet()) { - Path target = virtualRoot.resolve(entry.getKey()); - Path original = getAssetObject(version, assetsDir, entry.getValue()); - if (Files.exists(original)) { - cnt++; - if (!Files.isRegularFile(target)) - FileUtils.copyFile(original, target); - - if (index.needMapToResources()) { - target = resourcesDir.resolve(entry.getKey()); - if (!Files.isRegularFile(target)) - FileUtils.copyFile(original, target); - } - } - } - - // If the scale new format existent file is lower then 0.1, use the old format. - if (cnt * 10 < tot) - return assetsDir; - else - return virtualRoot; - } - - return assetsDir; - } - - public Task saveAsync(Version version) { - this.gameVersions.remove(getVersionJar(version)); - if (version.isResolvedPreservingPatches()) { - return new VersionJsonSaveTask(this, MaintainTask.maintainPreservingPatches(this, version)); - } else { - return new VersionJsonSaveTask(this, version); - } - } - - public boolean isLoaded() { - return versions != null; - } - - public Path getModpackConfiguration(String version) { - return getVersionRoot(version).resolve("modpack.json"); - } - - /** - * read modpack configuration for a version. - * - * @param version version installed as modpack - * @return modpack configuration object, or null if this version is not a modpack. - * @throws VersionNotFoundException if version does not exist. - * @throws IOException if an i/o error occurs. - */ - @Nullable - public ModpackConfiguration readModpackConfiguration(String version) throws IOException, VersionNotFoundException { - if (!hasVersion(version)) throw new VersionNotFoundException(version); - Path file = getModpackConfiguration(version); - if (Files.notExists(file)) return null; - return JsonUtils.fromJsonFile(file, ModpackConfiguration.class); - } - - public boolean isModpack(String version) { - return Files.exists(getModpackConfiguration(version)); - } - - public ModManager getModManager(String version) { - return new ModManager(this, version); - } - - public Path getSavesDirectory(String id) { - return getRunDirectory(id).resolve("saves"); - } - - public Path getBackupsDirectory(String id) { - return getRunDirectory(id).resolve("backups"); - } - - public Path getSchematicsDirectory(String id) { - return getRunDirectory(id).resolve("schematics"); - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .append("versions", versions == null ? null : versions.keySet()) - .append("baseDirectory", baseDirectory) - .toString(); - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java index e409f08b3a..1a40ea9aba 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java @@ -18,6 +18,8 @@ package org.jackhuang.hmcl.game; import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.addon.resourcepack.ResourcePackManager; import org.jackhuang.hmcl.event.*; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; @@ -33,6 +35,7 @@ import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -85,6 +88,7 @@ private static boolean hasClassicVersion(Path baseDirectory) { } private volatile Status status; + private volatile boolean loaded; private final ConcurrentHashMap> gameVersions = new ConcurrentHashMap<>(); public DefaultGameRepository2(Path baseDirectory) { @@ -95,6 +99,16 @@ public Path getBaseDirectory() { return status.baseDirectory; } + public void setBaseDirectory(Path baseDirectory) { + this.status = new Status(baseDirectory); + this.loaded = false; + this.gameVersions.clear(); + } + + public boolean isLoaded() { + return loaded; + } + @Override public void refresh() { if (EventBus.EVENT_BUS.fireEvent(new RefreshingVersionsEvent(this)) == Event.Result.DENY) { @@ -102,10 +116,11 @@ public void refresh() { } refreshImpl(); + loaded = true; EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this)); } - private void refreshImpl() { + protected void refreshImpl() { Status newStatus = new Status(status.baseDirectory); if (hasClassicVersion(newStatus.baseDirectory)) { @@ -374,6 +389,59 @@ public boolean renameInstance(GameInstanceID from, GameInstanceID to) { } } + public boolean removeVersionFromDisk(String id) { + try { + return removeInstanceFromDisk(new GameInstanceID(id)); + } catch (IllegalArgumentException e) { + return false; + } + } + + public boolean removeInstanceFromDisk(GameInstanceID id) { + if (EventBus.EVENT_BUS.fireEvent(new RemoveVersionEvent(this, id.id())) == Event.Result.DENY) { + return false; + } + + Status currentStatus = status; + currentStatus.instances.remove(id); + + Path file = getInstanceRoot(id); + if (Files.notExists(file)) { + return true; + } + + Path removedFile = file.toAbsolutePath().resolveSibling(FileUtils.getName(file) + "_removed"); + try { + Files.move(file, removedFile, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + LOG.warning("Unable to remove version folder: " + file, e); + return false; + } + + try { + if (FileUtils.moveToTrash(removedFile)) { + return true; + } + + for (Path path : FileUtils.listFilesByExtension(removedFile, "json")) { + try { + Files.delete(path); + } catch (IOException e) { + LOG.warning("Failed to delete file " + path, e); + } + } + + try { + FileUtils.deleteDirectory(removedFile); + } catch (IOException e) { + LOG.warning("Unable to remove version folder: " + file, e); + } + return true; + } finally { + refreshAsync().start(); + } + } + @Override public Optional getGameVersion(GameInstanceManifest manifest) { try { @@ -529,6 +597,10 @@ public Path getModpackConfiguration(GameInstanceID instanceId) { return getInstanceRoot(instanceId).resolve("modpack.json"); } + public Path getModpackConfiguration(String instanceId) { + return getModpackConfiguration(new GameInstanceID(instanceId)); + } + @Nullable public ModpackConfiguration readModpackConfiguration(GameInstanceID instanceId) throws IOException, NoSuchGameInstanceException { if (!hasInstance(instanceId)) throw new NoSuchGameInstanceException(instanceId); @@ -537,22 +609,51 @@ public ModpackConfiguration readModpackConfiguration(GameInstanceID instanceI return JsonUtils.fromJsonFile(file, ModpackConfiguration.class); } + @Nullable + public ModpackConfiguration readModpackConfiguration(String instanceId) throws IOException, NoSuchGameInstanceException { + return readModpackConfiguration(new GameInstanceID(instanceId)); + } + public boolean isModpack(GameInstanceID instanceId) { return Files.exists(getModpackConfiguration(instanceId)); } + public boolean isModpack(String instanceId) { + return isModpack(new GameInstanceID(instanceId)); + } + public Path getSavesDirectory(GameInstanceID instanceId) { return getRunDirectory(instanceId).resolve("saves"); } + public Path getSavesDirectory(String instanceId) { + return getSavesDirectory(new GameInstanceID(instanceId)); + } + public Path getBackupsDirectory(GameInstanceID instanceID) { return getRunDirectory(instanceID).resolve("backups"); } + public Path getBackupsDirectory(String instanceId) { + return getBackupsDirectory(new GameInstanceID(instanceId)); + } + public Path getSchematicsDirectory(GameInstanceID instanceId) { return getRunDirectory(instanceId).resolve("schematics"); } + public Path getSchematicsDirectory(String instanceId) { + return getSchematicsDirectory(new GameInstanceID(instanceId)); + } + + public ModManager getModManager(String instanceId) { + return new ModManager(this, instanceId); + } + + public ResourcePackManager getResourcePackManager(String instanceId) { + return new ResourcePackManager(this, instanceId); + } + @Override public GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException { return toResolved(status.resolve(manifest, new HashSet<>())); @@ -615,7 +716,7 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, } else if (!manifest.patches().isEmpty()) { // Assume patches themselves do not have patches recursively. List sortedPatches = manifest.patches().stream() - .sorted(Comparator.comparing(GameInstancePatch::priority)) + .sorted(Comparator.comparing(GameInstancePatch::getPriority)) .toList(); for (GameInstancePatch patch : sortedPatches) { currentManifest = patch.merge(currentManifest); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 889b3fca3e..6b0a30cf3e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -21,6 +21,7 @@ import com.google.gson.annotations.JsonAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import org.jackhuang.hmcl.util.Constants; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jetbrains.annotations.NotNullByDefault; @@ -28,6 +29,8 @@ import org.jetbrains.annotations.Unmodifiable; import java.io.IOException; +import java.time.Instant; +import java.time.format.DateTimeParseException; import java.util.*; @NotNullByDefault @@ -147,6 +150,11 @@ public GameInstanceManifest(GameInstanceID id) { null); } + /// Creates an empty root manifest with the given id. + public GameInstanceManifest(String id) { + this(new GameInstanceID(id)); + } + public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) throws JsonParseException { if (copyJson) { json = json.deepCopy(); @@ -321,6 +329,234 @@ public Boolean root() { return root != null && root; } + /// Returns the instance id as a string. + /// + /// @return the instance id string + public String getId() { + return id.id(); + } + + /// Returns legacy game arguments. + /// + /// @return the legacy game arguments + public Optional getMinecraftArguments() { + return Optional.ofNullable(minecraftArguments); + } + + /// Returns structured game arguments. + /// + /// @return the structured game arguments + public Optional getArguments() { + return Optional.ofNullable(arguments); + } + + /// Returns the main class. + /// + /// @return the main class, or `null` when absent + public @Nullable String getMainClass() { + return mainClass; + } + + /// Returns the manifest timestamp. + /// + /// @return the parsed timestamp, or `null` when absent or malformed + public @Nullable Instant getTime() { + return parseInstant(time); + } + + /// Returns the release timestamp. + /// + /// @return the parsed release timestamp, or `null` when absent or malformed + public @Nullable Instant getReleaseTime() { + return parseInstant(releaseTime); + } + + /// Returns the patch version. + /// + /// @return always `null` for manifests + public @Nullable String getVersion() { + return null; + } + + /// Returns the patch priority. + /// + /// @return the lowest priority for manifests + public int getPriority() { + return Integer.MIN_VALUE; + } + + /// Returns the release type. + /// + /// @return the release type, or unknown when absent + public ReleaseType getType() { + return type == null ? ReleaseType.UNKNOWN : type; + } + + /// Returns the primary jar instance id as a string. + /// + /// @return the primary jar instance id, or `null` when absent + public @Nullable String getJar() { + return jar == null ? null : jar.id(); + } + + /// Returns the parent instance id as a string. + /// + /// @return the parent instance id, or `null` when absent + public @Nullable String getInheritsFrom() { + return inheritsFrom == null ? null : inheritsFrom.id(); + } + + /// Returns the minimum launcher version. + /// + /// @return the minimum launcher version, or zero when absent + public int getMinimumLauncherVersion() { + return minimumLauncherVersion == null ? 0 : minimumLauncherVersion; + } + + /// Returns the compliance level. + /// + /// @return the compliance level, or `null` when absent + public @Nullable Integer getComplianceLevel() { + return complianceLevel; + } + + /// Returns the preferred Java version. + /// + /// @return the preferred Java version, or `null` when absent + public @Nullable GameJavaVersion getJavaVersion() { + return javaVersion; + } + + /// Returns whether the manifest is hidden. + /// + /// @return whether the manifest is hidden + public boolean isHidden() { + return hidden != null && hidden; + } + + /// Returns whether the manifest is a root manifest. + /// + /// @return whether the manifest is a root manifest + public boolean isRoot() { + return root(); + } + + /// Returns whether this manifest is already a standalone view. + /// + /// @return whether this manifest has no parent + public boolean isResolvedPreservingPatches() { + return inheritsFrom == null; + } + + /// Returns the pending patches. + /// + /// @return the pending patches, or an empty list when absent + public List getPatches() { + return patches == null ? List.of() : patches; + } + + /// Returns logging metadata. + /// + /// @return logging metadata + public Map getLogging() { + return logging == null ? Map.of() : logging; + } + + /// Returns libraries. + /// + /// @return libraries + public List getLibraries() { + return libraries == null ? List.of() : libraries; + } + + /// Returns compatibility rules. + /// + /// @return compatibility rules + public List getCompatibilityRules() { + return compatibilityRules == null ? List.of() : compatibilityRules; + } + + /// Returns download metadata. + /// + /// @return download metadata + public Map getDownloads() { + return downloads == null ? Map.of() : downloads; + } + + /// Returns client jar download information. + /// + /// @return client jar download information + public DownloadInfo getDownloadInfo() { + DownloadInfo client = downloads == null ? null : downloads.get(DownloadType.CLIENT); + String jarName = jar == null ? id.id() : jar.id(); + if (client == null) { + return new DownloadInfo(String.format("%s%s/%s.jar", Constants.DEFAULT_VERSION_DOWNLOAD_URL, jarName, jarName)); + } else { + return client; + } + } + + /// Returns the asset index metadata. + /// + /// @return asset index metadata + public AssetIndexInfo getAssetIndex() { + String assetsId = assets == null ? "legacy" : assets; + + if (assetIndex == null) { + String hash; + switch (assetsId) { + case "1.8" -> hash = "f6ad102bcaa53b1a58358f16e376d548d44933ec"; + case "14w31a" -> hash = "10a2a0e75b03cfb5a7196abbdf43b54f7fa61deb"; + case "14w25a" -> hash = "32ff354a3be1c4dd83027111e6d79ee4d701d2c0"; + case "1.7.4" -> hash = "545510a60f526b9aa8a38f9c0bc7a74235d21675"; + case "1.7.10" -> hash = "1863782e33ce7b584fc45b037325a1964e095d3e"; + case "1.7.3" -> hash = "f6cf726f4747128d13887010c2cbc44ba83504d9"; + case "pre-1.6" -> hash = "3d8e55480977e32acd9844e545177e69a52f594b"; + case "legacy" -> hash = "770572e819335b6c0a053f8378ad88eda189fc14"; + default -> { + assetsId = "legacy"; + hash = "770572e819335b6c0a053f8378ad88eda189fc14"; + } + } + + String url = Constants.DEFAULT_INDEX_URL + hash + "/" + assetsId + ".json"; + return new AssetIndexInfo(assetsId, url); + } else { + return assetIndex; + } + } + + /// Returns whether this manifest applies to the current environment. + /// + /// @return whether this manifest applies to the current environment + public boolean appliesToCurrentEnvironment() { + return CompatibilityRule.appliesToCurrentEnvironment(compatibilityRules); + } + + /// Resolves this manifest through the repository. + /// + /// @param repository the repository that provides parent manifests + /// @return the resolved manifest + public GameInstanceManifest resolve(GameRepository2 repository) throws NoSuchGameInstanceException { + return repository.resolve(this).manifest(); + } + + /// Resolves this manifest through the repository while preserving patches. + /// + /// @param repository the repository that provides parent manifests + /// @return the standalone manifest + public GameInstanceManifest resolvePreservingPatches(GameRepository2 repository) throws NoSuchGameInstanceException { + return repository.resolvePreservingPatches(this).manifest(); + } + + /// Returns a manifest copy with the given id. + /// + /// @param id the new instance id + /// @return the updated manifest + public GameInstanceManifest setId(String id) { + return withId(new GameInstanceID(id)); + } + public GameInstanceManifest withId(GameInstanceID id) { Objects.requireNonNull(id); @@ -333,6 +569,14 @@ public GameInstanceManifest withId(GameInstanceID id) { return builder.toManifest(); } + /// Returns a manifest copy with the given jar id. + /// + /// @param jar the jar id, or `null` when absent + /// @return the updated manifest + public GameInstanceManifest setJar(@Nullable String jar) { + return withJar(jar == null ? null : new GameInstanceID(jar)); + } + public GameInstanceManifest withJar(@Nullable GameInstanceID jar) { if (Objects.equals(this.jar, jar)) { return this; @@ -343,6 +587,14 @@ public GameInstanceManifest withJar(@Nullable GameInstanceID jar) { return builder.toManifest(); } + /// Returns a manifest copy with the given parent id. + /// + /// @param inheritsFrom the parent id, or `null` when absent + /// @return the updated manifest + public GameInstanceManifest setInheritsFrom(@Nullable String inheritsFrom) { + return withInheritsFrom(inheritsFrom == null ? null : new GameInstanceID(inheritsFrom)); + } + /// Returns a manifest copy with the given parent instance id. /// /// @param inheritsFrom the parent instance id, or `null` if this manifest is independent @@ -357,6 +609,69 @@ public GameInstanceManifest withInheritsFrom(@Nullable GameInstanceID inheritsFr return builder.toManifest(); } + /// Returns a manifest copy with the given legacy game arguments. + public GameInstanceManifest setMinecraftArguments(@Nullable String minecraftArguments) { + Builder builder = new Builder(this); + builder.setMinecraftArguments(minecraftArguments); + return builder.toManifest(); + } + + /// Returns a manifest copy with the given structured arguments. + public GameInstanceManifest setArguments(@Nullable Arguments arguments) { + Builder builder = new Builder(this); + builder.setArguments(arguments); + return builder.toManifest(); + } + + /// Returns a manifest copy with the given main class. + public GameInstanceManifest setMainClass(@Nullable String mainClass) { + Builder builder = new Builder(this); + builder.setMainClass(mainClass); + return builder.toManifest(); + } + + /// Returns a manifest copy with the given asset index. + public GameInstanceManifest setAssetIndex(@Nullable AssetIndexInfo assetIndex) { + Builder builder = new Builder(this); + builder.setAssetIndex(assetIndex); + return builder.toManifest(); + } + + /// Returns a manifest copy with the given Java version. + public GameInstanceManifest setJavaVersion(@Nullable GameJavaVersion javaVersion) { + Builder builder = new Builder(this); + builder.setJavaVersion(javaVersion); + return builder.toManifest(); + } + + /// Returns a manifest copy with the given libraries. + public GameInstanceManifest setLibraries(@Nullable List libraries) { + Builder builder = new Builder(this); + builder.setLibraries(libraries); + return builder.toManifest(); + } + + /// Returns a manifest copy with the given downloads. + public GameInstanceManifest setDownload(@Nullable Map downloads) { + Builder builder = new Builder(this); + builder.setDownloads(downloads); + return builder.toManifest(); + } + + /// Returns a manifest copy with the given logging metadata. + public GameInstanceManifest setLogging(@Nullable Map logging) { + Builder builder = new Builder(this); + builder.setLogging(logging); + return builder.toManifest(); + } + + /// Returns a manifest copy with the given root flag. + public GameInstanceManifest setRoot(@Nullable Boolean root) { + Builder builder = new Builder(this); + builder.setRoot(root); + return builder.toManifest(); + } + public GameInstanceManifest withPatches(@Nullable List patches) { if (patches == this.patches) { return this; @@ -367,6 +682,66 @@ public GameInstanceManifest withPatches(@Nullable List patche return builder.toManifest(); } + /// Returns a manifest copy with the given patches. + public GameInstanceManifest setPatches(@Nullable List patches) { + return withPatches(patches); + } + + /// Returns a manifest copy with additional patches. + public GameInstanceManifest addPatch(GameInstancePatch... additional) { + return addPatches(Arrays.asList(additional)); + } + + /// Returns a manifest copy with additional patches. + public GameInstanceManifest addPatches(@Nullable List additional) { + Set patchIds = new HashSet<>(); + if (additional != null) { + for (GameInstancePatch patch : additional) { + if (patch.id() != null) { + patchIds.add(patch.id()); + } + } + } + + List patches = new ArrayList<>(); + if (this.patches != null) { + for (GameInstancePatch patch : this.patches) { + if (patch.id() == null || !patchIds.contains(patch.id())) { + patches.add(patch); + } + } + } + if (additional != null) { + patches.addAll(additional); + } + return withPatches(patches); + } + + /// Returns a manifest copy without pending patches. + public GameInstanceManifest clearPatches() { + return withPatches(null); + } + + /// Returns a manifest copy without the patch with the given id. + public GameInstanceManifest removePatchById(String patchId) { + if (patches == null) { + return this; + } + + List filtered = new ArrayList<>(); + for (GameInstancePatch patch : patches) { + if (!patchId.equals(patch.id())) { + filtered.add(patch); + } + } + return withPatches(filtered); + } + + /// Returns whether this manifest has a patch with the given id. + public boolean hasPatch(String patchId) { + return patches != null && patches.stream().anyMatch(patch -> patchId.equals(patch.id())); + } + /// Converts this manifest into a hidden patch entry for preserving resolved inheritance. /// /// @return a patch containing this manifest's launch metadata @@ -463,6 +838,18 @@ public JsonObject toJsonObject() { return json; } + private static @Nullable Instant parseInstant(@Nullable String value) { + if (value == null) { + return null; + } + + try { + return Instant.parse(value); + } catch (DateTimeParseException e) { + return null; + } + } + private static final class Builder { // @formatter:off private @Nullable GameInstanceID id; @@ -535,6 +922,21 @@ public void setJar(@Nullable GameInstanceID jar) { } } + public void setMinecraftArguments(@Nullable String minecraftArguments) { + this.minecraftArguments = minecraftArguments; + rawJson = null; + } + + public void setArguments(@Nullable Arguments arguments) { + this.arguments = arguments; + rawJson = null; + } + + public void setMainClass(@Nullable String mainClass) { + this.mainClass = mainClass; + rawJson = null; + } + public void setInheritsFrom(@Nullable GameInstanceID inheritsFrom) { this.inheritsFrom = inheritsFrom; if (rawJson != null) { @@ -546,6 +948,36 @@ public void setInheritsFrom(@Nullable GameInstanceID inheritsFrom) { } } + public void setAssetIndex(@Nullable AssetIndexInfo assetIndex) { + this.assetIndex = assetIndex; + rawJson = null; + } + + public void setJavaVersion(@Nullable GameJavaVersion javaVersion) { + this.javaVersion = javaVersion; + rawJson = null; + } + + public void setLibraries(@Nullable List libraries) { + this.libraries = libraries == null ? null : List.copyOf(libraries); + rawJson = null; + } + + public void setDownloads(@Nullable Map downloads) { + this.downloads = downloads == null ? null : Map.copyOf(downloads); + rawJson = null; + } + + public void setLogging(@Nullable Map logging) { + this.logging = logging == null ? null : Map.copyOf(logging); + rawJson = null; + } + + public void setRoot(@Nullable Boolean root) { + this.root = root; + rawJson = null; + } + public void setPatches(@Nullable List patches) { if (patches != null) { this.patches = List.copyOf(patches); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 67afe2249f..ca74ac682b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -33,6 +33,7 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Optional; @NotNullByDefault public record GameInstancePatch( @@ -60,6 +61,282 @@ public record GameInstancePatch( @Nullable @Unmodifiable JsonObject rawJson ) { + /// Priority for the Minecraft base patch. + public static final int PRIORITY_MC = 0; + + /// Priority for loader patches. + public static final int PRIORITY_LOADER = 30000; + + /// Creates an empty patch with the given id. + public GameInstancePatch(String id) { + this( + id, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null); + } + + /// Creates a patch with launch metadata. + public GameInstancePatch( + String id, + @Nullable String version, + int priority, + @Nullable Arguments arguments, + @Nullable String mainClass, + @Nullable List libraries) { + this( + id, + version, + priority, + null, + arguments, + mainClass, + null, + null, + null, + null, + null, + null, + libraries, + null, + null, + null, + null, + null, + null, + null, + null, + null); + } + + /// Creates a patch from manifest metadata. + public static GameInstancePatch fromManifest( + GameInstanceManifest manifest, + String id, + @Nullable String version, + int priority) { + return new GameInstancePatch( + id, + version, + priority, + manifest.minecraftArguments(), + manifest.arguments(), + manifest.mainClass(), + manifest.inheritsFrom() == null ? null : manifest.inheritsFrom().id(), + manifest.jar(), + manifest.assetIndex(), + manifest.assets(), + manifest.complianceLevel(), + manifest.javaVersion(), + manifest.libraries(), + manifest.compatibilityRules(), + manifest.downloads(), + manifest.logging(), + manifest.type(), + manifest.time(), + manifest.releaseTime(), + manifest.minimumLauncherVersion(), + manifest.hidden(), + null); + } + + /// Returns the patch id. + public @Nullable String getId() { + return id; + } + + /// Returns the patch version. + public @Nullable String getVersion() { + return version; + } + + /// Returns the patch priority. + public int getPriority() { + return priority == null ? Integer.MIN_VALUE : priority; + } + + /// Returns legacy game arguments. + public Optional getMinecraftArguments() { + return Optional.ofNullable(minecraftArguments); + } + + /// Returns structured arguments. + public Optional getArguments() { + return Optional.ofNullable(arguments); + } + + /// Returns the main class. + public @Nullable String getMainClass() { + return mainClass; + } + + /// Returns the jar id. + public @Nullable String getJar() { + return jar == null ? null : jar.id(); + } + + /// Returns the asset index. + public @Nullable AssetIndexInfo getAssetIndex() { + return assetIndex; + } + + /// Returns the Java version. + public @Nullable GameJavaVersion getJavaVersion() { + return javaVersion; + } + + /// Returns libraries. + public List getLibraries() { + return libraries == null ? List.of() : libraries; + } + + /// Returns compatibility rules. + public List getCompatibilityRules() { + return compatibilityRules == null ? List.of() : compatibilityRules; + } + + /// Returns downloads. + public Map getDownloads() { + return downloads == null ? Map.of() : downloads; + } + + /// Returns logging metadata. + public Map getLogging() { + return logging == null ? Map.of() : logging; + } + + /// Returns whether the patch is hidden. + public boolean isHidden() { + return hidden != null && hidden; + } + + /// Returns a patch copy with the given id. + public GameInstancePatch setId(@Nullable String id) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + /// Returns a patch copy with the given version. + public GameInstancePatch setVersion(@Nullable String version) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + /// Returns a patch copy with the given priority. + public GameInstancePatch setPriority(@Nullable Integer priority) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + /// Returns a patch copy with the given jar id. + public GameInstancePatch setJar(@Nullable String jar) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, + jar == null ? null : new GameInstanceID(jar), assetIndex, assets, complianceLevel, javaVersion, + libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, + hidden); + } + + /// Returns a patch copy with the given libraries. + public GameInstancePatch setLibraries(@Nullable List libraries) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + /// Returns a patch copy with the given Java version. + public GameInstancePatch setJavaVersion(@Nullable GameJavaVersion javaVersion) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + /// Returns a patch copy with the given arguments. + public GameInstancePatch setArguments(@Nullable Arguments arguments) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + /// Returns a patch copy with the given main class. + public GameInstancePatch setMainClass(@Nullable String mainClass) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + /// Returns a patch copy with the given asset index. + public GameInstancePatch setAssetIndex(@Nullable AssetIndexInfo assetIndex) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + /// Returns a patch copy with the given downloads. + public GameInstancePatch setDownload(@Nullable Map downloads) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + /// Returns a patch copy with the given logging metadata. + public GameInstancePatch setLogging(@Nullable Map logging) { + return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, + assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, + releaseTime, minimumLauncherVersion, hidden); + } + + private static GameInstancePatch copy( + @Nullable String id, + @Nullable String version, + @Nullable Integer priority, + @Nullable String minecraftArguments, + @Nullable Arguments arguments, + @Nullable String mainClass, + @Nullable String inheritsFrom, + @Nullable GameInstanceID jar, + @Nullable AssetIndexInfo assetIndex, + @Nullable String assets, + @Nullable Integer complianceLevel, + @Nullable GameJavaVersion javaVersion, + @Nullable List libraries, + @Nullable List compatibilityRules, + @Nullable Map downloads, + @Nullable Map logging, + @Nullable ReleaseType type, + @Nullable String time, + @Nullable String releaseTime, + @Nullable Integer minimumLauncherVersion, + @Nullable Boolean hidden) { + return new GameInstancePatch(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, + jar, assetIndex, assets, complianceLevel, javaVersion, + libraries == null ? null : List.copyOf(libraries), + compatibilityRules == null ? null : List.copyOf(compatibilityRules), + downloads == null ? null : Map.copyOf(downloads), + logging == null ? null : Map.copyOf(logging), + type, time, releaseTime, minimumLauncherVersion, hidden, null); + } + public static GameInstancePatch fromJson(JsonObject json) throws JsonParseException { @Nullable String id = null; @Nullable String version = null; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java deleted file mode 100644 index 1b7cc27dcd..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.util.io.FileUtils; -import org.jackhuang.hmcl.util.platform.Platform; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Optional; -import java.util.Set; - -/** - * Supports operations on versioning. - *

- * Note that game repository will not do any operations which need connection with Internet, if do, - * see {@link org.jackhuang.hmcl.download.DependencyManager} - * - * @author huangyuhui - */ -public interface GameRepository extends VersionProvider { - - /** - * Does the version of id exist? - * - * @param id the id of version - * @return true if the version exists - */ - @Override - boolean hasVersion(String id); - - /** - * Get the version - * - * @param id the id of version - * @return the version you want - * @throws VersionNotFoundException if no version is id. - */ - @Override - Version getVersion(String id) throws VersionNotFoundException; - - default Version getResolvedVersion(String id) throws VersionNotFoundException { - return getVersion(id).resolve(this); - } - - default Version getResolvedPreservingPatchesVersion(String id) throws VersionNotFoundException { - return getVersion(id).resolvePreservingPatches(this); - } - - /** - * How many version are there? - */ - int getVersionCount(); - - /** - * Gets the collection of versions - * - * @return the collection of versions - */ - Collection getVersions(); - - /** - * Load version list. - *

- * This method should be called before launching a version. - * A time-costly operation. - * You'd better execute this method in a new thread. - */ - void refreshVersions(); - - default Task refreshVersionsAsync() { - return Task.runAsync(this::refreshVersions); - } - - /** - * Gets the root folder of specific version. - * The root folders the versions must be unique. - * For example, .minecraft/versions/<version name>/. - */ - Path getVersionRoot(String id); - - /** - * Gets the current running directory of the given version for game. - * - * @param id the version id - */ - Path getRunDirectory(String id); - - Path getLibrariesDirectory(Version version); - - /** - * Get the library file in disk. - * This method allows versions and libraries that are not loaded by this game repository. - * - * @param version the reference of game version - * @param lib the library, {@link Version#getLibraries()} - * @return the library file - */ - Path getLibraryFile(Version version, Library lib); - - /** - * Get the directory that native libraries will be unzipped to. - *

- * You'd better return a unique directory. - * Or if it returns a temporary directory, {@link org.jackhuang.hmcl.launch.Launcher#makeLaunchScript} will fail. - * If you do want to return a temporary directory, make {@link org.jackhuang.hmcl.launch.Launcher#makeLaunchScript} - * always fail({@code UnsupportedOperationException}) and not to use it. - * - * @param id version id - * @param platform the platform of native libraries - * @return the native directory - */ - Path getNativeDirectory(String id, Platform platform); - - /// Get the directory for placing mod files. - /// - /// @param id instance id - /// @return the mods directory - Path getModsDirectory(String id); - - /// Get the directory for placing resource packs. - /// - /// @param id instance id - /// @return the resource pack directory - Path getResourcePackDirectory(String id); - - /** - * Get minecraft jar - * - * @param version resolvedVersion - * @return the minecraft jar - */ - Path getVersionJar(Version version); - - /** - * Detect game version. - *

- * This method is time-consuming, but the result will be cached. - * Consider running this job in IO scheduler. - * - * @param version version - * @return game version, or empty if an error occurred in detection. - */ - Optional getGameVersion(Version version); - - /** - * Detect game version. - *

- * This method is time-consuming, but the result will be cached. - * Consider running this job in IO scheduler. - * - * @param versionId id of version - * @return game version, or empty if an error occurred in detection. - */ - default Optional getGameVersion(String versionId) throws VersionNotFoundException { - return getGameVersion(getVersion(versionId)); - } - - /** - * Get minecraft jar - * - * @param version version id - * @return the minecraft jar - */ - default Path getVersionJar(String version) throws VersionNotFoundException { - return getVersionJar(getVersion(version).resolve(this)); - } - - /** - * Rename given version to new name. - * - * @param from The id of original version - * @param to The new id of the version - * @throws UnsupportedOperationException if this game repository does not support renaming a version - * @return true if the operation is done successfully, false if version `from` not found, version json is malformed or I/O errors occurred. - */ - boolean renameVersion(String from, String to); - - /** - * Get actual asset directory. - * Will reconstruct assets or do some blocking tasks if necessary. - * You'd better create a new thread to invoke this method. - * - * @param version the id of specific version that is relevant to {@code assetId} - * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} - * @return the actual asset directory - */ - Path getActualAssetDirectory(String version, String assetId); - - /** - * Get the asset directory according to the asset id. - * - * @param version the id of specific version that is relevant to {@code assetId} - * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} - * @return the asset directory - */ - Path getAssetDirectory(String version, String assetId); - - /** - * Get the file that given asset object refers to - * - * @param version the id of specific version that is relevant to {@code assetId} - * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} - * @param name the asset object name, you can find it in keys of {@link AssetIndex#getObjects()} - * @throws java.io.IOException if I/O operation fails. - * @return the file that given asset object refers to - */ - Optional getAssetObject(String version, String assetId, String name) throws IOException; - - /** - * Get the file that given asset object refers to - * - * @param version the id of specific version that is relevant to {@code assetId} - * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} - * @param obj the asset object, you can find it in {@link AssetIndex#getObjects()} - * @return the file that given asset object refers to - */ - Path getAssetObject(String version, String assetId, AssetObject obj); - - /** - * Get asset index that assetId represents - * - * @param version the id of specific version that is relevant to {@code assetId} - * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} - * @return the asset index - */ - AssetIndex getAssetIndex(String version, String assetId) throws IOException; - - /** - * Get the asset_index.json which includes asset objects information. - * - * @param version the id of specific version that is relevant to {@code assetId} - * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} - */ - Path getIndexFile(String version, String assetId); - - /** - * Get logging object - * - * @param version the id of specific version that is relevant to {@code assetId} - * @param assetId the asset id, you can find it in {@link AssetIndexInfo#getId()} {@link Version#getAssetIndex()} - * @param loggingInfo the logging info - * @return the file that loggingInfo refers to - */ - Path getLoggingObject(String version, String assetId, LoggingInfo loggingInfo); - - default Set getClasspath(Version version) { - Set classpath = new LinkedHashSet<>(); - for (Library library : version.getLibraries()) - if (library.appliesToCurrentEnvironment() && !library.isNative()) { - Path f = getLibraryFile(version, library); - if (Files.isRegularFile(f)) - classpath.add(FileUtils.getAbsolutePath(f)); - } - return classpath; - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java index 067380b0ba..2c678cf1a7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.Platform; import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.Files; @@ -46,14 +47,46 @@ public interface GameRepository2 { boolean hasInstance(GameInstanceID instanceId); + /// Returns whether the instance exists. + /// + /// @param id the instance id string + /// @return whether the instance exists + default boolean hasVersion(@Nullable String id) { + if (id == null) { + return false; + } + + try { + return hasInstance(new GameInstanceID(id)); + } catch (IllegalArgumentException e) { + return false; + } + } + GameInstanceManifest getInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; + /// Returns an instance manifest by string id. + /// + /// @param id the instance id string + /// @return the instance manifest + default GameInstanceManifest getVersion(String id) throws NoSuchGameInstanceException { + return getInstanceManifest(new GameInstanceID(id)); + } + /// Returns a cached launch-ready manifest view for the instance. /// /// @param instanceId the instance id /// @return the resolved manifest view GameInstanceManifest.Resolved getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; + /// Returns a resolved instance manifest by string id. + /// + /// @param id the instance id string + /// @return the resolved manifest + default GameInstanceManifest getResolvedVersion(String id) throws NoSuchGameInstanceException { + return getResolvedInstanceManifest(new GameInstanceID(id)).manifest(); + } + /// Returns a standalone manifest view for the instance. /// /// @param instanceId the instance id @@ -62,58 +95,184 @@ default GameInstanceManifest.Standalone getResolvedPreservingPatchesInstanceMani return resolvePreservingPatches(getInstanceManifest(instanceId)); } + /// Returns a standalone manifest by string id. + /// + /// @param id the instance id string + /// @return the standalone manifest + default GameInstanceManifest getResolvedPreservingPatchesVersion(String id) throws NoSuchGameInstanceException { + return getResolvedPreservingPatchesInstanceManifest(new GameInstanceID(id)).manifest(); + } + int getInstanceCount(); + /// Returns the instance count. + /// + /// @return the instance count + default int getVersionCount() { + return getInstanceCount(); + } + Collection getInstanceManifests(); + /// Returns all instance manifests. + /// + /// @return all instance manifests + default Collection getVersions() { + return getInstanceManifests(); + } + void refresh(); + /// Refreshes the repository. + default void refreshVersions() { + refresh(); + } + default Task refreshAsync() { return Task.runAsync(this::refresh); } + /// Refreshes the repository asynchronously. + default Task refreshVersionsAsync() { + return refreshAsync(); + } + Path getInstanceRoot(GameInstanceID instanceId); + /// Returns the instance root by string id. + /// + /// @param id the instance id string + /// @return the instance root + default Path getVersionRoot(String id) { + return getInstanceRoot(new GameInstanceID(id)); + } + Path getRunDirectory(GameInstanceID instanceId); + /// Returns the run directory by string id. + /// + /// @param id the instance id string + /// @return the run directory + default Path getRunDirectory(String id) { + return getRunDirectory(new GameInstanceID(id)); + } + Path getLibrariesDirectory(GameInstanceManifest manifest); Path getLibraryFile(GameInstanceManifest manifest, Library lib); Path getNativeDirectory(GameInstanceID instanceId, Platform platform); + /// Returns the native directory by string id. + default Path getNativeDirectory(String id, Platform platform) { + return getNativeDirectory(new GameInstanceID(id), platform); + } + Path getModsDirectory(GameInstanceID instanceId); + /// Returns the mods directory by string id. + default Path getModsDirectory(String id) { + return getModsDirectory(new GameInstanceID(id)); + } + Path getResourcePackDirectory(GameInstanceID instanceId); + /// Returns the resource pack directory by string id. + default Path getResourcePackDirectory(String id) { + return getResourcePackDirectory(new GameInstanceID(id)); + } + Path getInstanceJar(GameInstanceManifest manifest); + /// Returns the instance jar path. + default Path getVersionJar(GameInstanceManifest manifest) { + return getInstanceJar(manifest); + } + + /// Returns the instance jar path by string id. + default Path getVersionJar(String id) { + try { + return getInstanceJar(new GameInstanceID(id)); + } catch (NoSuchGameInstanceException e) { + throw new IllegalArgumentException(e); + } + } + Optional getGameVersion(GameInstanceManifest manifest); default Optional getGameVersion(GameInstanceID instanceId) throws NoSuchGameInstanceException { return getGameVersion(getInstanceManifest(instanceId)); } + /// Returns the detected game version by string id. + default Optional getGameVersion(String id) { + try { + return getGameVersion(new GameInstanceID(id)); + } catch (NoSuchGameInstanceException e) { + return Optional.empty(); + } + } + default Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceException { return getInstanceJar(getResolvedInstanceManifest(instanceId).manifest()); } boolean renameInstance(GameInstanceID from, GameInstanceID to); + /// Renames an instance by string id. + default boolean renameVersion(String from, String to) { + return renameInstance(new GameInstanceID(from), new GameInstanceID(to)); + } + Path getActualAssetDirectory(GameInstanceID instanceId, String assetId); + /// Returns the actual asset directory by string id. + default Path getActualAssetDirectory(String id, String assetId) { + return getActualAssetDirectory(new GameInstanceID(id), assetId); + } + Path getAssetDirectory(GameInstanceID instanceId, String assetId); + /// Returns the asset directory by string id. + default Path getAssetDirectory(String id, String assetId) { + return getAssetDirectory(new GameInstanceID(id), assetId); + } + Optional getAssetObject(GameInstanceID instanceId, String assetId, String name) throws IOException; + /// Returns an asset object by string id. + default Optional getAssetObject(String id, String assetId, String name) throws IOException { + return getAssetObject(new GameInstanceID(id), assetId, name); + } + Path getAssetObject(GameInstanceID instanceId, String assetId, AssetObject obj); + /// Returns an asset object by string id. + default Path getAssetObject(String id, String assetId, AssetObject obj) { + return getAssetObject(new GameInstanceID(id), assetId, obj); + } + AssetIndex getAssetIndex(GameInstanceID instanceId, String assetId) throws IOException; + /// Returns an asset index by string id. + default AssetIndex getAssetIndex(String id, String assetId) throws IOException { + return getAssetIndex(new GameInstanceID(id), assetId); + } + Path getIndexFile(GameInstanceID instanceId, String assetId); + /// Returns an asset index path by string id. + default Path getIndexFile(String id, String assetId) { + return getIndexFile(new GameInstanceID(id), assetId); + } + Path getLoggingObject(GameInstanceID instanceId, String assetId, LoggingInfo loggingInfo); + /// Returns a logging object path by string id. + default Path getLoggingObject(String id, String assetId, LoggingInfo loggingInfo) { + return getLoggingObject(new GameInstanceID(id), assetId, loggingInfo); + } + default Set getClasspath(GameInstanceManifest manifest) { Set classpath = new LinkedHashSet<>(); if (manifest.libraries() != null) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java index f22f407d66..94f06a962a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java @@ -35,13 +35,13 @@ public enum JavaVersionConstraint { VANILLA(true, VersionRange.all(), VersionRange.all()) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { // Give priority to the Java version requirements specified in the version JSON return version == null || version.getJavaVersion() == null; } @Override - public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaRuntime java, LibraryAnalyzer analyzer) { + public boolean checkJava(GameVersionNumber gameVersionNumber, GameInstanceManifest version, JavaRuntime java, LibraryAnalyzer analyzer) { GameJavaVersion minimumJavaVersion = GameJavaVersion.getMinimumJavaVersion(gameVersionNumber); return minimumJavaVersion == null || java.getParsedVersion() >= minimumJavaVersion.majorVersion(); } @@ -49,7 +49,7 @@ public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, J // Minecraft with suggested java version recorded in game json is restrictedly constrained. GAME_JSON(true, VersionRange.all(), VersionRange.all()) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { if (version == null) return false; // We only checks for 1.7.10 and above, since 1.7.2 with Forge can only run on Java 7, but it is recorded Java 8 in game json, which is not correct. @@ -57,7 +57,7 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul } @Override - public VersionRange getJavaVersionRange(Version version, LibraryAnalyzer analyzer) { + public VersionRange getJavaVersionRange(GameInstanceManifest version, LibraryAnalyzer analyzer) { String javaVersion; if (Objects.requireNonNull(version.getJavaVersion()).majorVersion() >= 9) { javaVersion = "" + version.getJavaVersion().majorVersion(); @@ -70,7 +70,7 @@ public VersionRange getJavaVersionRange(Version version, LibraryA // Minecraft<=1.7.2+Forge requires Java<=7, But LegacyModFixer may fix that problem. So only suggest user using Java 7. MODDED_JAVA_7(false, GameVersionNumber.atMost("1.7.2"), VersionNumber.atMost("1.7.999")) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE) && super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer); @@ -78,7 +78,7 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul }, MODDED_JAVA_8(false, GameVersionNumber.between("1.7.10", "1.16.999"), VersionNumber.between("1.8", "1.8.999")) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE) && super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer); @@ -86,7 +86,7 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul }, MODDED_JAVA_16(false, GameVersionNumber.between("1.17", "1.17.999"), VersionNumber.between("16", "16.999")) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE) && super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer); @@ -94,7 +94,7 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul }, MODDED_JAVA_17(false, GameVersionNumber.between("1.18", "1.20.4"), VersionNumber.between("17", "17.999")) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE) && super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer); @@ -102,7 +102,7 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul }, MODDED_JAVA_21(false, GameVersionNumber.atLeast("1.20.5"), VersionNumber.between("21", "21.999")) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.FORGE) && super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer); @@ -110,13 +110,13 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul }, CLEANROOM(true, GameVersionNumber.between("1.12.2", "1.12.999"), VersionRange.all()) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { return analyzer != null && analyzer.has(LibraryAnalyzer.LibraryType.CLEANROOM) && super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer); } @Override - public VersionRange getJavaVersionRange(Version version, LibraryAnalyzer analyzer) { + public VersionRange getJavaVersionRange(GameInstanceManifest version, LibraryAnalyzer analyzer) { if (analyzer == null || !analyzer.has(LibraryAnalyzer.LibraryType.CLEANROOM)) return VersionRange.all(); @@ -132,7 +132,7 @@ public VersionRange getJavaVersionRange(Version version, LibraryA // LaunchWrapper<=1.12 will crash because LaunchWrapper assumes the system class loader is an instance of URLClassLoader (Java 8) LAUNCH_WRAPPER(true, GameVersionNumber.atMost("1.12.999"), VersionNumber.atMost("1.8.999")) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { if (version == null) return false; return super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer) && LAUNCH_WRAPPER_MAIN.equals(version.getMainClass()) && @@ -147,7 +147,7 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul // For example, JDK 9+ 64-bit cannot load 32-bit lwjgl native library. VANILLA_LINUX_JAVA_8(true, GameVersionNumber.atMost("1.12.999"), VersionNumber.atMost("1.8.999")) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { return OperatingSystem.CURRENT_OS == OperatingSystem.LINUX && Architecture.SYSTEM_ARCH == Architecture.X86_64 @@ -156,14 +156,14 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul } @Override - public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaRuntime java, LibraryAnalyzer analyzer) { + public boolean checkJava(GameVersionNumber gameVersionNumber, GameInstanceManifest version, JavaRuntime java, LibraryAnalyzer analyzer) { return java.getArchitecture() != Architecture.X86_64 || super.checkJava(gameVersionNumber, version, java, analyzer); } }, // Minecraft currently does not provide official support for architectures other than x86 and x86-64. VANILLA_X86(false, VersionRange.all(), VersionRange.all()) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { if (java == null || java.getArchitecture() != Architecture.ARM64) return false; @@ -175,14 +175,14 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul } @Override - public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaRuntime java, LibraryAnalyzer analyzer) { + public boolean checkJava(GameVersionNumber gameVersionNumber, GameInstanceManifest version, JavaRuntime java, LibraryAnalyzer analyzer) { return java.getArchitecture().isX86(); } }, // Minecraft 1.16+Forge with crash because JDK-8273826 MODLAUNCHER_8(false, GameVersionNumber.between("1.16.3", "1.17.1"), VersionRange.all()) { @Override - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { if (version == null || java == null || analyzer == null || !super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer)) return false; @@ -207,7 +207,7 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul } @Override - public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaRuntime java, LibraryAnalyzer analyzer) { + public boolean checkJava(GameVersionNumber gameVersionNumber, GameInstanceManifest version, JavaRuntime java, LibraryAnalyzer analyzer) { int parsedJavaVersion = java.getParsedVersion(); if (parsedJavaVersion > 17) { return false; @@ -243,17 +243,17 @@ public VersionRange getGameVersionRange() { return gameVersionRange; } - public VersionRange getJavaVersionRange(Version version, LibraryAnalyzer analyzer) { + public VersionRange getJavaVersionRange(GameInstanceManifest version, LibraryAnalyzer analyzer) { return javaVersionRange; } - public final boolean appliesToVersion(@Nullable GameVersionNumber gameVersionNumber, @Nullable Version version, + public final boolean appliesToVersion(@Nullable GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, LibraryAnalyzer analyzer) { return gameVersionRange.contains(gameVersionNumber) && appliesToVersionImpl(gameVersionNumber, version, java, analyzer); } - protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable Version version, + protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { GameJavaVersion gameJavaVersion; if (version == null || (gameJavaVersion = version.getJavaVersion()) == null) { @@ -271,7 +271,7 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul } @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean checkJava(GameVersionNumber gameVersionNumber, Version version, JavaRuntime java, LibraryAnalyzer analyzer) { + public boolean checkJava(GameVersionNumber gameVersionNumber, GameInstanceManifest version, JavaRuntime java, LibraryAnalyzer analyzer) { return getJavaVersionRange(version, analyzer).contains(java.getVersionNumber()); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/NoSuchGameInstanceException.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/NoSuchGameInstanceException.java index 090ed8c957..8e2c8a064d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/NoSuchGameInstanceException.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/NoSuchGameInstanceException.java @@ -20,7 +20,7 @@ import org.jetbrains.annotations.NotNullByDefault; @NotNullByDefault -public class NoSuchGameInstanceException extends Exception { +public class NoSuchGameInstanceException extends RuntimeException { private final GameInstanceID id; public NoSuchGameInstanceException(GameInstanceID id) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/SimpleVersionProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/SimpleVersionProvider.java deleted file mode 100644 index 9dcec84c99..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/SimpleVersionProvider.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -import java.util.HashMap; -import java.util.Map; - -/** - * - * @author huangyuhui - */ -public class SimpleVersionProvider implements VersionProvider { - - protected final Map versionMap = new HashMap<>(); - - @Override - public boolean hasVersion(String id) { - return versionMap.containsKey(id); - } - - @Override - public Version getVersion(String id) { - if (!hasVersion(id)) - throw new VersionNotFoundException("Version id " + id + " not found"); - else - return versionMap.get(id); - } - - public void addVersion(Version version) { - versionMap.put(version.getId(), version); - } - - public Map getVersionMap() { - return versionMap; - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java deleted file mode 100644 index 26b037822c..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java +++ /dev/null @@ -1,512 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -import com.google.gson.JsonParseException; -import org.jackhuang.hmcl.util.*; -import org.jackhuang.hmcl.util.gson.JsonMap; -import org.jackhuang.hmcl.util.gson.Validation; -import org.jetbrains.annotations.Nullable; - -import java.time.Instant; -import java.util.*; -import java.util.stream.Collectors; - -import static org.jackhuang.hmcl.util.logging.Logger.LOG; - -/** - * - * @author huangyuhui - */ -@Immutable -public class Version implements Comparable, Validation { - - /** - * Patches with higher priority can override info from other patches, such as mainClass. - */ - public static final int PRIORITY_MC = 0, PRIORITY_LOADER = 30000; - - private final String id; - private final String version; - private final Integer priority; - private final String minecraftArguments; - private final Arguments arguments; - private final String mainClass; - private final String inheritsFrom; - private final String jar; - private final AssetIndexInfo assetIndex; - private final String assets; - private final Integer complianceLevel; - @Nullable - private final GameJavaVersion javaVersion; - private final List libraries; - private final List compatibilityRules; - private final JsonMap downloads; - private final JsonMap logging; - private final ReleaseType type; - private final Instant time; - private final Instant releaseTime; - private final Integer minimumLauncherVersion; - private final Boolean root; - private final Boolean hidden; - private final List patches; - - private transient final boolean resolved; - - public Version(String id) { - this(false, id, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, false, true, null); - } - - /** - * Constructor for patch - * - * @param id patch id - * @param version patch version - * @param priority patch priority - * @param arguments patch additional arguments - * @param mainClass main class to override - * @param libraries additional libraries - */ - public Version(String id, String version, int priority, Arguments arguments, String mainClass, List libraries) { - this(false, id, version, priority, null, arguments, mainClass, null, null, null, null, null, null, libraries, null, null, null, null, null, null, null, null, null, null); - } - - public Version(boolean resolved, String id, String version, Integer priority, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, Integer complianceLevel, GameJavaVersion javaVersion, List libraries, List compatibilityRules, Map downloads, Map logging, ReleaseType type, Instant time, Instant releaseTime, Integer minimumLauncherVersion, Boolean hidden, Boolean root, List patches) { - this.resolved = resolved; - this.id = id; - this.version = version; - this.priority = priority; - this.minecraftArguments = minecraftArguments; - this.arguments = arguments; - this.mainClass = mainClass; - this.inheritsFrom = inheritsFrom; - this.jar = jar; - this.assetIndex = assetIndex; - this.assets = assets; - this.complianceLevel = complianceLevel; - this.javaVersion = javaVersion; - this.libraries = Lang.copyList(libraries); - this.compatibilityRules = Lang.copyList(compatibilityRules); - this.downloads = downloads == null ? null : new JsonMap<>(downloads); - this.logging = logging == null ? null : new JsonMap<>(logging); - this.type = type; - this.time = time; - this.releaseTime = releaseTime; - this.minimumLauncherVersion = minimumLauncherVersion; - this.hidden = hidden; - this.root = root; - this.patches = Lang.copyList(patches); - } - - public Optional getMinecraftArguments() { - return Optional.ofNullable(minecraftArguments); - } - - public Optional getArguments() { - return Optional.ofNullable(arguments); - } - - public String getMainClass() { - return mainClass; - } - - public Instant getTime() { - return time; - } - - public String getId() { - return id; - } - - /** - * Version of the patch. - * Exists only when this version object represents a patch. - * Example: 0.5.0.33 for fabric-loader, 28.0.46 for minecraft-forge. - */ - @Nullable - public String getVersion() { - return version; - } - - public int getPriority() { - return priority == null ? Integer.MIN_VALUE : priority; - } - - public ReleaseType getType() { - return type == null ? ReleaseType.UNKNOWN : type; - } - - public Instant getReleaseTime() { - return releaseTime; - } - - public String getJar() { - return jar; - } - - public String getInheritsFrom() { - return inheritsFrom; - } - - public int getMinimumLauncherVersion() { - return minimumLauncherVersion == null ? 0 : minimumLauncherVersion; - } - - public Integer getComplianceLevel() { - return complianceLevel; - } - - public GameJavaVersion getJavaVersion() { - return javaVersion; - } - - public boolean isHidden() { - return hidden == null ? false : hidden; - } - - public boolean isRoot() { - return root == null ? false : root; - } - - public boolean isResolved() { - return resolved; - } - - public boolean isResolvedPreservingPatches() { - return inheritsFrom == null && !resolved; - } - - public List getPatches() { - return patches == null ? Collections.emptyList() : patches; - } - - public Map getLogging() { - return logging == null ? Collections.emptyMap() : Collections.unmodifiableMap(logging); - } - - public List getLibraries() { - return libraries == null ? Collections.emptyList() : Collections.unmodifiableList(libraries); - } - - public List getCompatibilityRules() { - return compatibilityRules == null ? Collections.emptyList() : Collections.unmodifiableList(compatibilityRules); - } - - public Map getDownloads() { - return downloads == null ? Collections.emptyMap() : Collections.unmodifiableMap(downloads); - } - - public DownloadInfo getDownloadInfo() { - DownloadInfo client = downloads == null ? null : downloads.get(DownloadType.CLIENT); - String jarName = jar == null ? id : jar; - if (client == null) - return new DownloadInfo(String.format("%s%s/%s.jar", Constants.DEFAULT_VERSION_DOWNLOAD_URL, jarName, jarName)); - else - return client; - } - - public AssetIndexInfo getAssetIndex() { - String assetsId = assets == null ? "legacy" : assets; - - if (assetIndex == null) { - String hash; - switch (assetsId) { - case "1.8": - hash = "f6ad102bcaa53b1a58358f16e376d548d44933ec"; - break; - case "14w31a": - hash = "10a2a0e75b03cfb5a7196abbdf43b54f7fa61deb"; - break; - case "14w25a": - hash = "32ff354a3be1c4dd83027111e6d79ee4d701d2c0"; - break; - case "1.7.4": - hash = "545510a60f526b9aa8a38f9c0bc7a74235d21675"; - break; - case "1.7.10": - hash = "1863782e33ce7b584fc45b037325a1964e095d3e"; - break; - case "1.7.3": - hash = "f6cf726f4747128d13887010c2cbc44ba83504d9"; - break; - case "pre-1.6": - hash = "3d8e55480977e32acd9844e545177e69a52f594b"; - break; - case "legacy": - default: - assetsId = "legacy"; - hash = "770572e819335b6c0a053f8378ad88eda189fc14"; - } - - String url = Constants.DEFAULT_INDEX_URL + hash + "/" + assetsId + ".json"; - return new AssetIndexInfo(assetsId, url); - } else { - return assetIndex; - } - } - - public boolean appliesToCurrentEnvironment() { - return CompatibilityRule.appliesToCurrentEnvironment(compatibilityRules); - } - - /** - * Resolve given version. - * Resolving version will list all patches within this version and its parents, - * which is for analysis. - */ - public Version resolve(VersionProvider provider) throws VersionNotFoundException { - if (isResolved()) return this; - return resolve(provider, new HashSet<>()).markAsResolved(); - } - - protected Version merge(Version parent, boolean isPatch) { - return new Version( - true, - id, - null, - null, - minecraftArguments == null ? parent.minecraftArguments : minecraftArguments, - Arguments.merge(parent.arguments, arguments), - mainClass == null ? parent.mainClass : mainClass, - null, // inheritsFrom - jar == null ? parent.jar : jar, - assetIndex == null ? parent.assetIndex : assetIndex, - assets == null ? parent.assets : assets, - complianceLevel, - javaVersion == null ? parent.javaVersion : javaVersion, - Lang.merge(this.libraries, parent.libraries), - Lang.merge(parent.compatibilityRules, this.compatibilityRules), - downloads == null ? parent.downloads : downloads, - logging == null ? parent.logging : logging, - type == null ? parent.type : type, - time == null ? parent.time : time, - releaseTime == null ? parent.releaseTime : releaseTime, - Lang.merge(minimumLauncherVersion, parent.minimumLauncherVersion, Math::max), - hidden, - true, - isPatch ? parent.patches : Lang.merge(Lang.merge(parent.patches, Collections.singleton(toPatch())), patches)); - } - - protected Version resolve(VersionProvider provider, Set resolvedSoFar) throws VersionNotFoundException { - Version thisVersion; - - if (inheritsFrom == null) { - if (isRoot()) { - // TODO: Breaking change, require much testing on versions installed with external installer, other launchers, and all kinds of versions. - thisVersion = patches != null ? new Version(id).setPatches(patches) : this; - } else { - thisVersion = this; - } - thisVersion = this.jar == null ? thisVersion.setJar(id) : thisVersion.setJar(this.jar); - } else { - // To maximize the compatibility. - if (!resolvedSoFar.add(id)) { - LOG.warning("Found circular dependency versions: " + resolvedSoFar); - thisVersion = this.jar == null ? this.setJar(id) : this; - } else { - // It is supposed to auto install an version in getVersion. - thisVersion = merge(provider.getVersion(inheritsFrom).resolve(provider, resolvedSoFar), false); - } - } - - if (patches == null) { - // This is a version from external launcher. NO need to resolve the patches. - return thisVersion; - } else if (!patches.isEmpty()) { - // Assume patches themselves do not have patches recursively. - List sortedPatches = patches.stream() - .sorted(Comparator.comparing(Version::getPriority)) - .collect(Collectors.toList()); - for (Version patch : sortedPatches) { - thisVersion = patch.setJar(null).merge(thisVersion, true); - } - } - - return thisVersion.setId(id); - } - - private Version toPatch() { - return this.clearPatches().setHidden(true).setId("resolved." + getId()); - } - - /** - * Resolve the version preserving all dependencies and patches. - */ - public Version resolvePreservingPatches(VersionProvider provider) throws VersionNotFoundException { - return resolvePreservingPatches(provider, new HashSet<>()); - } - - protected Version mergePreservingPatches(Version parent) { - return parent.addPatch(toPatch()).addPatches(patches); - } - - protected Version resolvePreservingPatches(VersionProvider provider, Set resolvedSoFar) throws VersionNotFoundException { - Version thisVersion = isRoot() ? this : new Version(id).addPatch(toPatch()).addPatches(getPatches()); - - if (inheritsFrom == null) { - // keep thisVersion - } else { - // To maximize the compatibility. - if (!resolvedSoFar.add(id)) { - LOG.warning("Found circular dependency versions: " + resolvedSoFar); - // keep thisVersion - } else { - // It is supposed to auto install an version in getVersion. - thisVersion = mergePreservingPatches(provider.getVersion(inheritsFrom).resolvePreservingPatches(provider, resolvedSoFar)); - } - } - - return thisVersion.setId(id).setJar(resolve(provider).getJar()); - } - - public Version markAsResolved() { - return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version markAsUnresolved() { - return new Version(false, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - private Version setHidden(Boolean hidden) { - return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setRoot(Boolean root) { - return new Version(true, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setId(String id) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setVersion(String version) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setPriority(Integer priority) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setMinecraftArguments(String minecraftArguments) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setJavaVersion(GameJavaVersion javaVersion) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setArguments(Arguments arguments) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setMainClass(String mainClass) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setInheritsFrom(String inheritsFrom) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setJar(String jar) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setAssetIndex(AssetIndexInfo assetIndex) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setLibraries(List libraries) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setDownload(JsonMap downloads) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setLogging(Map logging) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version setPatches(List patches) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version addPatch(Version... additional) { - return addPatches(Arrays.asList(additional)); - } - - public Version addPatches(@Nullable List additional) { - Set patchIds = additional == null ? Collections.emptySet() : additional.stream().map(Version::getId).collect(Collectors.toSet()); - List patches = Lang.merge(this.patches == null ? null : this.patches.stream().filter(patch -> !patchIds.contains(patch.getId())).collect(Collectors.toList()), additional); - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, patches); - } - - public Version clearPatches() { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, null); - } - - public Version removePatchById(String patchId) { - return new Version(resolved, id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden, root, - patches == null ? null : patches.stream().filter(patch -> !patchId.equals(patch.getId())).collect(Collectors.toList())); - } - - public boolean hasPatch(String patchId) { - return patches != null && patches.stream().anyMatch(patch -> patchId.equals(patch.getId())); - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof Version && Objects.equals(id, ((Version) obj).id); - } - - @Override - public int compareTo(Version o) { - return id.compareTo(o.id); - } - - @Override - public String toString() { - return new ToStringBuilder(this).append("id", id).toString(); - } - - @Override - public void validate() throws JsonParseException { - if (StringUtils.isBlank(id)) - throw new JsonParseException("Version ID cannot be blank"); - if (downloads != null) - for (Map.Entry entry : downloads.entrySet()) { - if (!(entry.getKey() instanceof DownloadType)) - throw new JsonParseException("Version downloads key must be DownloadType"); - if (!(entry.getValue() instanceof DownloadInfo)) - throw new JsonParseException("Version downloads value must be DownloadInfo"); - } - if (logging != null) - for (Map.Entry entry : logging.entrySet()) { - if (!(entry.getKey() instanceof DownloadType)) - throw new JsonParseException("Version logging key must be DownloadType"); - if (!(entry.getValue() instanceof LoggingInfo)) - throw new JsonParseException("Version logging value must be LoggingInfo"); - } - } - -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionJsonType.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionJsonType.java deleted file mode 100644 index 589200fe62..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionJsonType.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -public enum VersionJsonType { - OFFICIAL, - TLAUNCHER -} 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 113a3ebb44..87b3fef7d4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java @@ -31,7 +31,7 @@ * @author huangyuhui */ public final class VersionLibraryBuilder { - private final Version version; + private final GameInstanceManifest version; private final List mcArgs; private final List game; private final List jvm; @@ -39,7 +39,7 @@ public final class VersionLibraryBuilder { private final boolean useMcArgs; private boolean jvmChanged = false; - public VersionLibraryBuilder(Version version) { + public VersionLibraryBuilder(GameInstanceManifest version) { this.version = version; this.libraries = new ArrayList<>(version.getLibraries()); this.mcArgs = version.getMinecraftArguments().map(StringUtils::tokenize).map(ArrayList::new).orElse(null); @@ -48,8 +48,8 @@ public VersionLibraryBuilder(Version version) { this.useMcArgs = mcArgs != null; } - public Version build() { - Version ret = version; + public GameInstanceManifest build() { + GameInstanceManifest ret = version; if (useMcArgs) { // The official launcher will not parse the "arguments" property when it detects the presence of "mcArgs". // The "arguments" property with the "rule" is simply ignored here. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionNotFoundException.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionNotFoundException.java deleted file mode 100644 index eaf669d093..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionNotFoundException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -/** - * - * @author huangyuhui - */ -public final class VersionNotFoundException extends RuntimeException { - - public VersionNotFoundException() { - } - - public VersionNotFoundException(String message) { - super(message); - } - - public VersionNotFoundException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionProvider.java deleted file mode 100644 index 48fadf764e..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -/** - * Supports version accessing. - * - * @see Version#resolve - * @author huangyuhui - */ -public interface VersionProvider { - - /** - * Does the version of id exist? - * - * @param id the id of version - * @return true if the version exists - */ - boolean hasVersion(String id); - - /** - * Get the version - * - * @param id the id of version - * @return the version you want - */ - Version getVersion(String id) throws VersionNotFoundException; -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/tlauncher/TLauncherLibrary.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/tlauncher/TLauncherLibrary.java deleted file mode 100644 index 9bf55a366a..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/tlauncher/TLauncherLibrary.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game.tlauncher; - -import com.google.gson.annotations.SerializedName; -import org.jackhuang.hmcl.game.Artifact; -import org.jackhuang.hmcl.game.CompatibilityRule; -import org.jackhuang.hmcl.game.ExtractRules; -import org.jackhuang.hmcl.game.LibrariesDownloadInfo; -import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.game.LibraryDownloadInfo; -import org.jackhuang.hmcl.util.Immutable; -import org.jackhuang.hmcl.util.gson.JsonSerializable; - -import java.util.List; -import java.util.Map; - -@Immutable -@JsonSerializable -public final class TLauncherLibrary { - - @SerializedName("name") - private final Artifact name; - private final String url; - private final LibraryDownloadInfo artifact; - - @SerializedName("classifies") // stupid typo made by TLauncher - private final Map classifiers; - private final ExtractRules extract; - private final Map natives; - private final List rules; - private final List checksums; - - public TLauncherLibrary(Artifact name, String url, LibraryDownloadInfo artifact, Map classifiers, ExtractRules extract, Map natives, List rules, List checksums) { - this.name = name; - this.url = url; - this.artifact = artifact; - this.classifiers = classifiers; - this.extract = extract; - this.natives = natives; - this.rules = rules; - this.checksums = checksums; - } - - public Library toLibrary() { - return new Library( - name, - url, - new LibrariesDownloadInfo(artifact, classifiers), - checksums, - extract, - natives, - rules, - null, - null - ); - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/tlauncher/TLauncherVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/tlauncher/TLauncherVersion.java deleted file mode 100644 index d3d5095d5d..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/tlauncher/TLauncherVersion.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game.tlauncher; - -import com.google.gson.JsonParseException; -import org.jackhuang.hmcl.game.*; -import org.jackhuang.hmcl.util.gson.JsonMap; -import org.jackhuang.hmcl.util.gson.TolerableValidationException; -import org.jackhuang.hmcl.util.gson.Validation; -import org.jetbrains.annotations.Nullable; - -import java.time.Instant; -import java.util.List; -import java.util.stream.Collectors; - -public final class TLauncherVersion implements Validation { - - private final String id; - private final String minecraftArguments; - private final Arguments arguments; - private final String mainClass; - private final String inheritsFrom; - private final String jar; - private final AssetIndexInfo assetIndex; - private final String assets; - private final Integer complianceLevel; - @Nullable - private final GameJavaVersion javaVersion; - private final List libraries; - private final List compatibilityRules; - private final JsonMap downloads; - private final JsonMap logging; - private final ReleaseType type; - private final Instant time; - private final Instant releaseTime; - private final Integer minimumLauncherVersion; - private final Integer tlauncherVersion; - - public TLauncherVersion(String id, String minecraftArguments, Arguments arguments, String mainClass, String inheritsFrom, String jar, AssetIndexInfo assetIndex, String assets, Integer complianceLevel, @Nullable GameJavaVersion javaVersion, List libraries, List compatibilityRules, JsonMap downloads, JsonMap logging, ReleaseType type, Instant time, Instant releaseTime, Integer minimumLauncherVersion, Integer tlauncherVersion) { - this.id = id; - this.minecraftArguments = minecraftArguments; - this.arguments = arguments; - this.mainClass = mainClass; - this.inheritsFrom = inheritsFrom; - this.jar = jar; - this.assetIndex = assetIndex; - this.assets = assets; - this.complianceLevel = complianceLevel; - this.javaVersion = javaVersion; - this.libraries = libraries; - this.compatibilityRules = compatibilityRules; - this.downloads = downloads; - this.logging = logging; - this.type = type; - this.time = time; - this.releaseTime = releaseTime; - this.minimumLauncherVersion = minimumLauncherVersion; - this.tlauncherVersion = tlauncherVersion; - } - - @Override - public void validate() throws JsonParseException, TolerableValidationException { - Validation.requireNonNull(tlauncherVersion, "Not TLauncher version json format"); - } - - public Version toVersion() { - return new Version( - false, - id, - null, - null, - minecraftArguments, - arguments, - mainClass, - inheritsFrom, - jar, - assetIndex, - assets, - complianceLevel, - javaVersion, - libraries == null ? null : libraries.stream().map(TLauncherLibrary::toLibrary).collect(Collectors.toList()), - compatibilityRules, - downloads, - logging, - type, - time, - releaseTime, - minimumLauncherVersion, - null, - null, - null - ); - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 1cdf87d256..54b8129a08 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -52,15 +52,15 @@ public class DefaultLauncher extends Launcher { private final LibraryAnalyzer analyzer; - public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) { + public DefaultLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { this(repository, version, authInfo, options, null); } - public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { + public DefaultLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { this(repository, version, authInfo, options, listener, true); } - public DefaultLauncher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { + public DefaultLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { super(repository, version, authInfo, options, listener, daemon); this.analyzer = LibraryAnalyzer.analyze(version, repository.getGameVersion(version).orElse(null)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java index e4a97f9f79..0b46fa0a74 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java @@ -18,9 +18,9 @@ package org.jackhuang.hmcl.launch; import org.jackhuang.hmcl.auth.AuthInfo; -import org.jackhuang.hmcl.game.GameRepository; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameRepository2; import org.jackhuang.hmcl.game.LaunchOptions; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.util.platform.ManagedProcess; import java.io.IOException; @@ -32,22 +32,22 @@ */ public abstract class Launcher { - protected final GameRepository repository; - protected final Version version; + protected final GameRepository2 repository; + protected final GameInstanceManifest version; protected final AuthInfo authInfo; protected final LaunchOptions options; protected final ProcessListener listener; protected final boolean daemon; - public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options) { + public Launcher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { this(repository, version, authInfo, options, null); } - public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { + public Launcher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { this(repository, version, authInfo, options, listener, true); } - public Launcher(GameRepository repository, Version version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { + public Launcher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { this.repository = repository; this.version = version; this.authInfo = authInfo; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java index 9e101f5d4a..6b6773cf3c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java @@ -17,7 +17,7 @@ */ package org.jackhuang.hmcl.modpack; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.io.FileUtils; @@ -28,12 +28,12 @@ public class ModpackUpdateTask extends Task { - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final String id; private final Task updateTask; private final Path backupFolder; - public ModpackUpdateTask(DefaultGameRepository repository, String id, Task updateTask) { + public ModpackUpdateTask(DefaultGameRepository2 repository, String id, Task updateTask) { this.repository = repository; this.id = id; this.updateTask = updateTask; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java index 5885be26fe..73c6647767 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java @@ -21,7 +21,7 @@ import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.modpack.ModpackCompletionException; import org.jackhuang.hmcl.addon.RemoteAddon; @@ -51,7 +51,7 @@ public final class CurseCompletionTask extends Task { private final DefaultDependencyManager dependency; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final ModManager modManager; private final String version; private CurseManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java index b8cf50d8f2..8ad9a6f5fc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.modpack.*; import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Task; @@ -46,7 +46,7 @@ public final class CurseInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final Path zipFile; private final Modpack modpack; private final CurseManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java index 730acc2106..31ce40bf04 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java @@ -19,7 +19,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.modpack.ModpackCompletionException; @@ -51,7 +51,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask { private final DefaultDependencyManager dependency; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final ModManager modManager; private final String version; private final Path configurationFile; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java index 1d20256ffc..b14089bf6b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.modpack.mcbbs; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; @@ -44,12 +44,12 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class McbbsModpackExportTask extends Task { - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final String version; private final ModpackExportInfo info; private final Path modpackFile; - public McbbsModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo info, Path modpackFile) { + public McbbsModpackExportTask(DefaultGameRepository2 repository, String version, ModpackExportInfo info, Path modpackFile) { this.repository = repository; this.version = version; this.info = info.validate(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java index 9666b4c0b8..11a1d5a5a5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java @@ -20,8 +20,9 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.game.Version; +import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.modpack.MinecraftInstanceTask; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; @@ -45,7 +46,7 @@ public final class McbbsModpackLocalInstallTask extends Task { private final McbbsModpackManifest manifest; private final String name; private final boolean update; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final MinecraftInstanceTask instanceTask; private final List> dependencies = new ArrayList<>(2); private final List> dependents = new ArrayList<>(4); @@ -103,14 +104,14 @@ public List> getDependencies() { @Override public void execute() throws Exception { - Version version = repository.readVersionJson(name); - Optional mcbbsPatch = version.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst(); + GameInstanceManifest version = repository.getVersion(name); + Optional mcbbsPatch = version.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst(); if (!update) { - Version patch = new Version(PATCH_NAME).setLibraries(manifest.getLibraries()); + GameInstancePatch patch = new GameInstancePatch(PATCH_NAME).setLibraries(manifest.getLibraries()); dependencies.add(repository.saveAsync(version.addPatch(patch))); } else if (mcbbsPatch.isPresent()) { // This mcbbs modpack was installed by HMCL. - Version patch = mcbbsPatch.get().setLibraries(manifest.getLibraries()); + GameInstancePatch patch = mcbbsPatch.get().setLibraries(manifest.getLibraries()); dependencies.add(repository.saveAsync(version.addPatch(patch))); } else { // This mcbbs modpack was installed by other launchers. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java index bfd6ec23d9..f9a5ce9b32 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -36,7 +36,7 @@ public class McbbsModpackRemoteInstallTask extends Task { private final String name; private final DefaultDependencyManager dependency; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final List> dependencies = new ArrayList<>(1); private final List> dependents = new ArrayList<>(1); private final McbbsModpackManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java index 6794a52cf3..a356071977 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.modpack.modrinth; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.modpack.ModpackCompletionException; import org.jackhuang.hmcl.task.FileDownloadTask; @@ -41,7 +41,7 @@ public class ModrinthCompletionTask extends Task { private final DefaultDependencyManager dependency; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final ModManager modManager; private final String version; private ModrinthManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java index 00e9a0cc56..391898b99e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.modpack.*; import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Task; @@ -40,7 +40,7 @@ public class ModrinthInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final Path zipFile; private final Modpack modpack; private final ModrinthManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java index c589efce48..47ce3b78b0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java @@ -26,7 +26,7 @@ import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackExportInfo; @@ -42,12 +42,12 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class ModrinthModpackExportTask extends Task { - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final String version; private final ModpackExportInfo info; private final Path modpackFile; - public ModrinthModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo info, Path modpackFile) { + public ModrinthModpackExportTask(DefaultGameRepository2 repository, String version, ModpackExportInfo info, Path modpackFile) { this.repository = repository; this.version = version; this.info = info.validate(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java index 452faace33..e3ee144266 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java @@ -24,7 +24,6 @@ import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; -import org.jackhuang.hmcl.util.gson.JsonMap; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -200,7 +199,7 @@ public static MultiMCInstancePatch read(String componentID, String text) { } public static final class ResolvedInstance { - private final Version version; + private final GameInstanceManifest version; private final String gameVersion; @@ -209,7 +208,7 @@ public static final class ResolvedInstance { private final List jarModFileNames; private final List mavenOnlyFiles; - public ResolvedInstance(Version version, String gameVersion, Library mainJar, List jarModFileNames, List mavenOnlyFiles) { + public ResolvedInstance(GameInstanceManifest version, String gameVersion, Library mainJar, List jarModFileNames, List mavenOnlyFiles) { this.version = version; this.gameVersion = gameVersion; this.mainJar = mainJar; @@ -217,7 +216,7 @@ public ResolvedInstance(Version version, String gameVersion, Library mainJar, Li this.mavenOnlyFiles = mavenOnlyFiles; } - public Version getVersion() { + public GameInstanceManifest getVersion() { return version; } @@ -245,7 +244,7 @@ public List getMavenOnlyFiles() { * See to do marks below for more information

* * @param patches List of all Json-Patch. - * @param versionID the version ID. Used when constructing a Version. + * @param versionID the version ID. Used when constructing a manifest. * @return The resolved instance. */ public static ResolvedInstance resolveArtifact(List patches, String versionID) { @@ -386,12 +385,12 @@ public static ResolvedInstance resolveArtifact(List patche mainClass = "org.jackhuang.hmcl.HMCLMultiMCBootstrap"; } - Version version = new Version(versionID) + GameInstanceManifest version = new GameInstanceManifest(versionID) .setArguments(new Arguments().addGameArguments(minecraftArguments).addJVMArgumentsDirect(jvmArguments)) .setMainClass(mainClass) .setLibraries(libraries) .setAssetIndex(assetIndex) - .setDownload(new JsonMap<>(Collections.singletonMap(DownloadType.CLIENT, mainJar.getRawDownloadInfo()))); + .setDownload(Collections.singletonMap(DownloadType.CLIENT, mainJar.getRawDownloadInfo())); /* TODO: Official Version-Json can only store one pre-defined GameJavaVersion, including 8, 11, 16, 17 and 21. An array of all suitable java versions are NOT supported. @@ -412,8 +411,6 @@ public static ResolvedInstance resolveArtifact(List patche message.append(" - Java Version Range: ").append(Arrays.toString(javaMajors)).append('\n'); } - version = version.markAsResolved(); - String gameVersion = null; for (MultiMCInstancePatch patch : patches) { if (MultiMCComponents.getComponent(patch.getID()) == LibraryAnalyzer.LibraryType.MINECRAFT) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java index ca0daeebd7..4207d02314 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.modpack.multimc; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackExportInfo; @@ -41,7 +41,7 @@ * Export the game to a mod pack file. */ public class MultiMCModpackExportTask extends Task { - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final String versionId; private final List whitelist; private final MultiMCInstanceConfiguration configuration; @@ -51,7 +51,7 @@ public class MultiMCModpackExportTask extends Task { * @param output mod pack file. * @param versionId to locate version.json */ - public MultiMCModpackExportTask(DefaultGameRepository repository, String versionId, List whitelist, MultiMCInstanceConfiguration configuration, Path output) { + public MultiMCModpackExportTask(DefaultGameRepository2 repository, String versionId, List whitelist, MultiMCInstanceConfiguration configuration, Path output) { this.repository = repository; this.versionId = versionId; this.whitelist = whitelist; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java index a028e6200d..53e2fdc3c8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java @@ -25,9 +25,9 @@ import org.jackhuang.hmcl.download.game.GameDownloadTask; import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.game.Artifact; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.Library; -import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.modpack.MinecraftInstanceTask; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; @@ -80,7 +80,7 @@ public final class MultiMCModpackInstallTask extends Task> dependents = new ArrayList<>(); private final List> dependencies = new ArrayList<>(); private final DefaultDependencyManager dependencyManager; @@ -285,7 +285,7 @@ public void execute() throws Exception { // Stage #5: Assemble game files. { - Version version = artifact.getVersion(); + GameInstanceManifest version = artifact.getVersion(); dependencies.add(repository.saveAsync(artifact.getVersion())); dependencies.add(new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java index ab06e48e38..c08ad609b4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.FileDownloadTask; @@ -42,7 +42,7 @@ public class ServerModpackCompletionTask extends Task { private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final String version; private ModpackConfiguration manifest; private GetTask dependent; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java index e943675e0f..1daa8a9e1c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.modpack.server; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; @@ -40,12 +40,12 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class ServerModpackExportTask extends Task { - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final String versionId; private final ModpackExportInfo exportInfo; private final Path modpackFile; - public ServerModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo exportInfo, Path modpackFile) { + public ServerModpackExportTask(DefaultGameRepository2 repository, String version, ModpackExportInfo exportInfo, Path modpackFile) { this.repository = repository; this.versionId = version; this.exportInfo = exportInfo.validate(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java index 5a1d382a68..f909cc806c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.modpack.MinecraftInstanceTask; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; @@ -41,7 +41,7 @@ public class ServerModpackLocalInstallTask extends Task { private final Modpack modpack; private final ServerModpackManifest manifest; private final String name; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final List> dependencies = new ArrayList<>(); private final List> dependents = new ArrayList<>(4); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java index ecff6c17ac..e15c43084e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.DefaultGameRepository2; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -36,7 +36,7 @@ public class ServerModpackRemoteInstallTask extends Task { private final String name; private final DefaultDependencyManager dependency; - private final DefaultGameRepository repository; + private final DefaultGameRepository2 repository; private final List> dependencies = new ArrayList<>(1); private final List> dependents = new ArrayList<>(1); private final ServerModpackManifest manifest; From 99228b729be92c691570b34d0a2663229987c1ac Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 11:52:09 +0800 Subject: [PATCH 24/85] refactor: rename DefaultGameRepository2 to DefaultGameRepository and update references --- .../jackhuang/hmcl/game/HMCLGameLauncher.java | 6 +++--- .../hmcl/game/HMCLGameRepository.java | 2 +- .../org/jackhuang/hmcl/game/LogExporter.java | 2 +- .../org/jackhuang/hmcl/ui/GameCrashWindow.java | 4 ++-- .../org/jackhuang/hmcl/util/NativePatcher.java | 2 +- .../hmcl/addon/LocalAddonManager.java | 8 ++++---- .../jackhuang/hmcl/addon/mod/ModManager.java | 4 ++-- .../resourcepack/ResourcePackManager.java | 4 ++-- .../download/DefaultDependencyManager.java | 8 ++++---- .../hmcl/download/DependencyManager.java | 4 ++-- .../hmcl/download/LibraryAnalyzer.java | 2 +- .../jackhuang/hmcl/download/MaintainTask.java | 18 +++++++++--------- .../download/forge/ForgeNewInstallTask.java | 4 ++-- .../download/game/GameAssetDownloadTask.java | 7 ++----- .../game/GameAssetIndexDownloadTask.java | 3 ++- .../hmcl/download/game/GameInstallTask.java | 4 ++-- .../hmcl/download/game/GameLibrariesTask.java | 14 +++++++------- .../neoforge/NeoForgeOldInstallTask.java | 2 +- .../download/optifine/OptiFineInstallTask.java | 2 +- .../hmcl/event/GameJsonParseFailedEvent.java | 3 ++- .../hmcl/event/RefreshedVersionsEvent.java | 4 +++- .../hmcl/event/RefreshingVersionsEvent.java | 4 +++- .../hmcl/event/RemoveVersionEvent.java | 3 ++- .../hmcl/event/RenameVersionEvent.java | 3 ++- ...sitory2.java => DefaultGameRepository.java} | 4 ++-- .../hmcl/game/GameInstanceManifest.java | 4 ++-- ...ameRepository2.java => GameRepository.java} | 2 +- .../jackhuang/hmcl/launch/DefaultLauncher.java | 6 +++--- .../org/jackhuang/hmcl/launch/Launcher.java | 10 +++++----- .../hmcl/modpack/ModpackUpdateTask.java | 6 +++--- .../modpack/curse/CurseCompletionTask.java | 4 ++-- .../hmcl/modpack/curse/CurseInstallTask.java | 4 ++-- .../mcbbs/McbbsModpackCompletionTask.java | 4 ++-- .../modpack/mcbbs/McbbsModpackExportTask.java | 6 +++--- .../mcbbs/McbbsModpackLocalInstallTask.java | 4 ++-- .../mcbbs/McbbsModpackRemoteInstallTask.java | 4 ++-- .../modrinth/ModrinthCompletionTask.java | 4 ++-- .../modpack/modrinth/ModrinthInstallTask.java | 4 ++-- .../modrinth/ModrinthModpackExportTask.java | 6 +++--- .../multimc/MultiMCModpackExportTask.java | 6 +++--- .../multimc/MultiMCModpackInstallTask.java | 4 ++-- .../server/ServerModpackCompletionTask.java | 4 ++-- .../server/ServerModpackExportTask.java | 6 +++--- .../server/ServerModpackLocalInstallTask.java | 4 ++-- .../server/ServerModpackRemoteInstallTask.java | 4 ++-- .../hmcl/game/GameInstanceManifestTest.java | 2 +- 46 files changed, 112 insertions(+), 107 deletions(-) rename HMCLCore/src/main/java/org/jackhuang/hmcl/game/{DefaultGameRepository2.java => DefaultGameRepository.java} (99%) rename HMCLCore/src/main/java/org/jackhuang/hmcl/game/{GameRepository2.java => GameRepository.java} (99%) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java index 6250fdd97c..5022678188 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java @@ -42,15 +42,15 @@ */ public final class HMCLGameLauncher extends DefaultLauncher { - public HMCLGameLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { + public HMCLGameLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { this(repository, version, authInfo, options, null); } - public HMCLGameLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { + public HMCLGameLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { this(repository, version, authInfo, options, listener, true); } - public HMCLGameLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { + public HMCLGameLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { super(repository, version, authInfo, options, listener, daemon); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 0e154406a0..a8aac21e99 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -75,7 +75,7 @@ /// HMCL game repository implementation backed by a GameDirectory and per-instance game settings. @NotNullByDefault -public final class HMCLGameRepository extends DefaultGameRepository2 { +public final class HMCLGameRepository extends DefaultGameRepository { /// References an optional game instance in a repository. /// /// @param repository the owning game repository diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java index d0249876d8..dd8511bf66 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java @@ -42,7 +42,7 @@ private LogExporter() { } public static CompletableFuture exportLogs( - Path zipFile, DefaultGameRepository2 gameRepository, String versionId, String logs, String launchScript, + Path zipFile, DefaultGameRepository gameRepository, String versionId, String logs, String launchScript, PathMatcher logMatcher) { Path runDirectory = gameRepository.getRunDirectory(versionId); Path baseDirectory = gameRepository.getBaseDirectory(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java index 1b5bf3ada2..2e8d44308e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java @@ -83,7 +83,7 @@ public class GameCrashWindow extends Stage { private final TextFlow feedbackTextFlow = new TextFlow(); private final ManagedProcess managedProcess; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final ProcessListener.ExitType exitType; private final LaunchOptions launchOptions; private final View view; @@ -91,7 +91,7 @@ public class GameCrashWindow extends Stage { private final List logs; - public GameCrashWindow(ManagedProcess managedProcess, ProcessListener.ExitType exitType, DefaultGameRepository2 repository, GameInstanceManifest version, LaunchOptions launchOptions, List logs) { + public GameCrashWindow(ManagedProcess managedProcess, ProcessListener.ExitType exitType, DefaultGameRepository repository, GameInstanceManifest version, LaunchOptions launchOptions, List logs) { Themes.applyNativeDarkMode(this); this.managedProcess = managedProcess; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java index 72e3e3c8ad..ddc4561405 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java @@ -73,7 +73,7 @@ public static boolean needPatchMemoryUtil(GameInstanceManifest version, int java ); } - public static GameInstanceManifest patchNative(DefaultGameRepository2 repository, + public static GameInstanceManifest patchNative(DefaultGameRepository repository, GameInstanceManifest version, String gameVersion, JavaRuntime javaVersion, GameSettings.Effective settings, diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java index 61795e4d1c..0520b67a8f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java @@ -17,7 +17,7 @@ */ package org.jackhuang.hmcl.addon; -import org.jackhuang.hmcl.game.GameRepository2; +import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jetbrains.annotations.NotNull; @@ -46,15 +46,15 @@ public static String getLocalAddonName(Path file) { protected final Set<@NotNull T> localFiles = new LinkedHashSet<>(); - protected final GameRepository2 repository; + protected final GameRepository repository; protected final String id; - public LocalAddonManager(GameRepository2 gameRepository, String versionId) { + public LocalAddonManager(GameRepository gameRepository, String versionId) { this.repository = gameRepository; this.id = versionId; } - public GameRepository2 getRepository() { + public GameRepository getRepository() { return repository; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java index e7bebda0ea..2e1b4ec3d4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java @@ -22,7 +22,7 @@ import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.addon.meta.*; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.GameRepository2; +import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.NoSuchGameInstanceException; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; @@ -69,7 +69,7 @@ private interface ModMetadataReader { private boolean loaded = false; - public ModManager(GameRepository2 repository, String id) { + public ModManager(GameRepository repository, String id) { super(repository, id); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java index a08d57211d..d571f6422c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.addon.resourcepack; import com.google.gson.annotations.SerializedName; -import org.jackhuang.hmcl.game.GameRepository2; +import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.addon.meta.PackMcMeta; import org.jackhuang.hmcl.util.Pair; @@ -218,7 +218,7 @@ private static List deserializePackList(String json) { private boolean loaded = false; - public ResourcePackManager(GameRepository2 repository, String id) { + public ResourcePackManager(GameRepository repository, String id) { super(repository, id); this.resourcePackDirectory = this.repository.getResourcePackDirectory(this.id); this.optionsFile = repository.getRunDirectory(id).resolve("options.txt"); 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 06502c12c8..a2c14ea0a2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -25,7 +25,7 @@ import org.jackhuang.hmcl.download.neoforge.NeoForgeInstallTask; import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask; import org.jackhuang.hmcl.game.Artifact; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.game.Library; @@ -48,18 +48,18 @@ */ public class DefaultDependencyManager extends AbstractDependencyManager { - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final DownloadProvider downloadProvider; private final DefaultCacheRepository cacheRepository; - public DefaultDependencyManager(DefaultGameRepository2 repository, DownloadProvider downloadProvider, DefaultCacheRepository cacheRepository) { + public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider, DefaultCacheRepository cacheRepository) { this.repository = repository; this.downloadProvider = downloadProvider; this.cacheRepository = cacheRepository; } @Override - public DefaultGameRepository2 getGameRepository() { + public DefaultGameRepository getGameRepository() { return repository; } 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 ab9515efbd..d709f6daea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.download; import org.jackhuang.hmcl.game.GameInstanceManifest; -import org.jackhuang.hmcl.game.GameRepository2; +import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.CacheRepository; @@ -33,7 +33,7 @@ public interface DependencyManager { /** * The relied game repository. */ - GameRepository2 getGameRepository(); + GameRepository getGameRepository(); /** * The cache repository 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 072f361eb2..0cb419d24c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -182,7 +182,7 @@ public static LibraryAnalyzer analyze(GameInstanceManifest version, String gameV return new LibraryAnalyzer(version, libraries); } - public static boolean isModded(GameRepository2 provider, GameInstanceManifest version) { + public static boolean isModded(GameRepository provider, GameInstanceManifest version) { GameInstanceManifest resolvedVersion; try { resolvedVersion = version.resolve(provider); 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 584dede112..27628a4410 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -39,10 +39,10 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class MaintainTask extends Task { - private final GameRepository2 repository; + private final GameRepository repository; private final GameInstanceManifest version; - public MaintainTask(GameRepository2 repository, GameInstanceManifest version) { + public MaintainTask(GameRepository repository, GameInstanceManifest version) { this.repository = repository; this.version = version; @@ -55,7 +55,7 @@ public void execute() { setResult(maintain(repository, version)); } - public static GameInstanceManifest maintain(GameRepository2 repository, GameInstanceManifest version) { + public static GameInstanceManifest maintain(GameRepository repository, GameInstanceManifest version) { if (version.getInheritsFrom() != null) throw new IllegalArgumentException("MaintainTask requires independent game version"); @@ -91,14 +91,14 @@ public static GameInstanceManifest maintain(GameRepository2 repository, GameInst return version; } - public static GameInstanceManifest maintainPreservingPatches(GameRepository2 repository, GameInstanceManifest version) { + public static GameInstanceManifest maintainPreservingPatches(GameRepository repository, GameInstanceManifest version) { if (!version.isResolvedPreservingPatches()) throw new IllegalArgumentException("MaintainTask requires independent game version"); GameInstanceManifest newVersion = maintain(repository, version.resolve(repository)); return newVersion.setPatches(version.getPatches()); } - private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository2 repository, GameInstanceManifest version, boolean reorderTweakClass) { + private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository repository, GameInstanceManifest version, boolean reorderTweakClass) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); VersionLibraryBuilder builder = new VersionLibraryBuilder(version); String mainClass = null; @@ -147,7 +147,7 @@ private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository return mainClass == null ? ret : ret.setMainClass(mainClass); } - private static GameInstanceManifest maintainGameWithCpwModLauncher(GameRepository2 repository, GameInstanceManifest version) { + private static GameInstanceManifest maintainGameWithCpwModLauncher(GameRepository repository, GameInstanceManifest version) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); VersionLibraryBuilder builder = new VersionLibraryBuilder(version); @@ -173,7 +173,7 @@ private static GameInstanceManifest maintainGameWithCpwModLauncher(GameRepositor return builder.build(); } - private static String updateIgnoreList(GameRepository2 repository, GameInstanceManifest version, String ignoreList) { + private static String updateIgnoreList(GameRepository repository, GameInstanceManifest version, String ignoreList) { String[] ignores = ignoreList.split(","); List newIgnoreList = new ArrayList<>(); @@ -205,7 +205,7 @@ private static String updateIgnoreList(GameRepository2 repository, GameInstanceM } // Fix wrong configurations when launching 1.17+ with Forge. - private static GameInstanceManifest maintainGameWithCpwBoostrapLauncher(GameRepository2 repository, GameInstanceManifest version) { + private static GameInstanceManifest maintainGameWithCpwBoostrapLauncher(GameRepository repository, GameInstanceManifest version) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); VersionLibraryBuilder builder = new VersionLibraryBuilder(version); @@ -247,7 +247,7 @@ private static GameInstanceManifest maintainGameWithCpwBoostrapLauncher(GameRepo return builder.build(); } - private static GameInstanceManifest maintainOptiFineLibrary(GameRepository2 repository, GameInstanceManifest version, boolean remove) { + private static GameInstanceManifest maintainOptiFineLibrary(GameRepository repository, GameInstanceManifest version, boolean remove) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); List libraries = new ArrayList<>(version.getLibraries()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java index fdd9bdf612..b6b389ac6e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java @@ -24,7 +24,7 @@ import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask; import org.jackhuang.hmcl.game.Artifact; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.DownloadInfo; import org.jackhuang.hmcl.game.DownloadType; import org.jackhuang.hmcl.game.GameInstanceManifest; @@ -194,7 +194,7 @@ public void execute() throws Exception { } private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository2 gameRepository; + private final DefaultGameRepository gameRepository; private final GameInstanceManifest version; private final Path installer; private final List> dependents = new ArrayList<>(1); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java index 09ca6af4ea..3210634231 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java @@ -19,10 +19,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.AbstractDependencyManager; -import org.jackhuang.hmcl.game.AssetIndex; -import org.jackhuang.hmcl.game.AssetIndexInfo; -import org.jackhuang.hmcl.game.AssetObject; -import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.CacheRepository; @@ -55,7 +52,7 @@ public final class GameAssetDownloadTask extends Task { /** * Constructor. * - * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository2} + * @param dependencyManager the dependency manager that can provides {@link GameRepository} * @param version the game version */ public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean forceDownloadingIndex, boolean integrityCheck) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java index ac8ebb30dd..572c0735cd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java @@ -22,6 +22,7 @@ import org.jackhuang.hmcl.game.AssetIndex; import org.jackhuang.hmcl.game.AssetIndexInfo; import org.jackhuang.hmcl.game.GameInstanceManifest; +import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; @@ -51,7 +52,7 @@ public final class GameAssetIndexDownloadTask extends Task { /** * Constructor. * - * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository2} + * @param dependencyManager the dependency manager that can provides {@link GameRepository} * @param version the resolved version */ public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean forceDownloading) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java index 898ace018a..d097931dee 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.download.game; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.task.Task; @@ -33,7 +33,7 @@ public class GameInstallTask extends Task { - private final DefaultGameRepository2 gameRepository; + private final DefaultGameRepository gameRepository; private final DefaultDependencyManager dependencyManager; private final GameInstanceManifest version; private final GameRemoteVersion remote; 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 3b80bbb2af..3633c5b350 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 @@ -20,9 +20,9 @@ import org.jackhuang.hmcl.download.AbstractDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.MaintainTask; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.GameInstanceManifest; -import org.jackhuang.hmcl.game.GameRepository2; +import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; @@ -63,7 +63,7 @@ public final class GameLibrariesTask extends Task { /** * Constructor. * - * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository2} + * @param dependencyManager the dependency manager that can provides {@link GameRepository} * @param version the game version */ public GameLibrariesTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean integrityCheck) { @@ -73,7 +73,7 @@ public GameLibrariesTask(AbstractDependencyManager dependencyManager, GameInstan /** * Constructor. * - * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository2} + * @param dependencyManager the dependency manager that can provides {@link GameRepository} * @param version the game version */ public GameLibrariesTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean integrityCheck, List libraries) { @@ -91,7 +91,7 @@ public List> getDependencies() { return dependencies; } - public static boolean shouldDownloadLibrary(GameRepository2 gameRepository, GameInstanceManifest version, Library library, boolean integrityCheck) { + public static boolean shouldDownloadLibrary(GameRepository gameRepository, GameInstanceManifest version, Library library, boolean integrityCheck) { Path file = gameRepository.getLibraryFile(version, library); if (!Files.isRegularFile(file)) return true; @@ -135,7 +135,7 @@ private static boolean shouldDownloadFMLLib(FMLLib fmlLib, Path file) { @Override public void execute() throws IOException { int progress = 0; - GameRepository2 gameRepository = dependencyManager.getGameRepository(); + GameRepository gameRepository = dependencyManager.getGameRepository(); for (Library library : libraries) { if (!library.appliesToCurrentEnvironment()) { continue; @@ -143,7 +143,7 @@ public void execute() throws IOException { // https://github.com/HMCL-dev/HMCL/issues/3975 if ("net.minecraftforge".equals(library.getGroupId()) && "minecraftforge".equals(library.getArtifactId()) - && gameRepository instanceof DefaultGameRepository2 defaultGameRepository) { + && gameRepository instanceof DefaultGameRepository defaultGameRepository) { List fmlLibs = getFMLLibs(library.getVersion()); if (fmlLibs != null) { Path libDir = defaultGameRepository.getBaseDirectory().resolve("lib") diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java index c0710ba317..4497ab72d7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java @@ -172,7 +172,7 @@ public void execute() throws Exception { } private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository2 gameRepository; + private final DefaultGameRepository gameRepository; private final GameInstanceManifest version; private final Path installer; private final List> dependents = new ArrayList<>(1); 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 c87a38b138..f422749add 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 @@ -50,7 +50,7 @@ */ public final class OptiFineInstallTask extends Task { - private final DefaultGameRepository2 gameRepository; + private final DefaultGameRepository gameRepository; private final DefaultDependencyManager dependencyManager; private final GameInstanceManifest version; private final OptiFineRemoteVersion remote; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/GameJsonParseFailedEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/GameJsonParseFailedEvent.java index 27385ff085..b589992ad8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/GameJsonParseFailedEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/GameJsonParseFailedEvent.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.event; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.util.ToStringBuilder; import java.nio.file.Path; @@ -34,7 +35,7 @@ public final class GameJsonParseFailedEvent extends Event { /** * - * @param source {@link org.jackhuang.hmcl.game.DefaultGameRepository2} + * @param source {@link DefaultGameRepository} * @param jsonFile the minecraft.json file. * @param version the version name */ diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java index 1de5618995..a8c36ff822 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java @@ -17,6 +17,8 @@ */ package org.jackhuang.hmcl.event; +import org.jackhuang.hmcl.game.GameRepository; + /** * This event gets fired when all the versions in .minecraft folder are loaded. *
@@ -29,7 +31,7 @@ public final class RefreshedVersionsEvent extends Event { /** * Constructor. * - * @param source {@link org.jackhuang.hmcl.game.GameRepository2} + * @param source {@link GameRepository} */ public RefreshedVersionsEvent(Object source) { super(source); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshingVersionsEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshingVersionsEvent.java index 4c2c6813e1..ca2327b28d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshingVersionsEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshingVersionsEvent.java @@ -17,6 +17,8 @@ */ package org.jackhuang.hmcl.event; +import org.jackhuang.hmcl.game.GameRepository; + /** * This event gets fired when loading versions in a .minecraft folder. *
@@ -29,7 +31,7 @@ public final class RefreshingVersionsEvent extends Event { /** * Constructor. * - * @param source {@link org.jackhuang.hmcl.game.GameRepository2} + * @param source {@link GameRepository} */ public RefreshingVersionsEvent(Object source) { super(source); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RemoveVersionEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RemoveVersionEvent.java index 676ad4e1de..86ec73bacd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RemoveVersionEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RemoveVersionEvent.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.event; +import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.util.ToStringBuilder; /** @@ -32,7 +33,7 @@ public class RemoveVersionEvent extends Event { /** * - * @param source {@link org.jackhuang.hmcl.game.GameRepository2} + * @param source {@link GameRepository} * @param version the version id. */ public RemoveVersionEvent(Object source, String version) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RenameVersionEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RenameVersionEvent.java index 9a0d9a4133..694babf651 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RenameVersionEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RenameVersionEvent.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.event; +import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.util.ToStringBuilder; /** @@ -32,7 +33,7 @@ public class RenameVersionEvent extends Event { /** * - * @param source {@link org.jackhuang.hmcl.game.GameRepository2} + * @param source {@link GameRepository} * @param from the version id. */ public RenameVersionEvent(Object source, String from, String to) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java similarity index 99% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 1a40ea9aba..b1bede3b0e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -43,7 +43,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; @NotNullByDefault -public class DefaultGameRepository2 implements GameRepository2 { +public class DefaultGameRepository implements GameRepository { private static final GameInstanceManifest CLASSIC_MANIFEST = new GameInstanceManifest( new GameInstanceID("Classic"), @@ -91,7 +91,7 @@ private static boolean hasClassicVersion(Path baseDirectory) { private volatile boolean loaded; private final ConcurrentHashMap> gameVersions = new ConcurrentHashMap<>(); - public DefaultGameRepository2(Path baseDirectory) { + public DefaultGameRepository(Path baseDirectory) { this.status = new Status(baseDirectory); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 6b0a30cf3e..187b9cecde 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -537,7 +537,7 @@ public boolean appliesToCurrentEnvironment() { /// /// @param repository the repository that provides parent manifests /// @return the resolved manifest - public GameInstanceManifest resolve(GameRepository2 repository) throws NoSuchGameInstanceException { + public GameInstanceManifest resolve(GameRepository repository) throws NoSuchGameInstanceException { return repository.resolve(this).manifest(); } @@ -545,7 +545,7 @@ public GameInstanceManifest resolve(GameRepository2 repository) throws NoSuchGam /// /// @param repository the repository that provides parent manifests /// @return the standalone manifest - public GameInstanceManifest resolvePreservingPatches(GameRepository2 repository) throws NoSuchGameInstanceException { + public GameInstanceManifest resolvePreservingPatches(GameRepository repository) throws NoSuchGameInstanceException { return repository.resolvePreservingPatches(this).manifest(); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java similarity index 99% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index 2c678cf1a7..6feed89655 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository2.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -32,7 +32,7 @@ import java.util.Set; @NotNullByDefault -public interface GameRepository2 { +public interface GameRepository { /// Resolves inheritance and pending patches into a launch-ready manifest view. /// /// @param manifest the manifest to resolve diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 54b8129a08..3c596e1ead 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -52,15 +52,15 @@ public class DefaultLauncher extends Launcher { private final LibraryAnalyzer analyzer; - public DefaultLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { + public DefaultLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { this(repository, version, authInfo, options, null); } - public DefaultLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { + public DefaultLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { this(repository, version, authInfo, options, listener, true); } - public DefaultLauncher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { + public DefaultLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { super(repository, version, authInfo, options, listener, daemon); this.analyzer = LibraryAnalyzer.analyze(version, repository.getGameVersion(version).orElse(null)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java index 0b46fa0a74..98c2d48d02 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java @@ -19,7 +19,7 @@ import org.jackhuang.hmcl.auth.AuthInfo; import org.jackhuang.hmcl.game.GameInstanceManifest; -import org.jackhuang.hmcl.game.GameRepository2; +import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.util.platform.ManagedProcess; @@ -32,22 +32,22 @@ */ public abstract class Launcher { - protected final GameRepository2 repository; + protected final GameRepository repository; protected final GameInstanceManifest version; protected final AuthInfo authInfo; protected final LaunchOptions options; protected final ProcessListener listener; protected final boolean daemon; - public Launcher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { + public Launcher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { this(repository, version, authInfo, options, null); } - public Launcher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { + public Launcher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { this(repository, version, authInfo, options, listener, true); } - public Launcher(GameRepository2 repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { + public Launcher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { this.repository = repository; this.version = version; this.authInfo = authInfo; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java index 6b6773cf3c..9e101f5d4a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java @@ -17,7 +17,7 @@ */ package org.jackhuang.hmcl.modpack; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.io.FileUtils; @@ -28,12 +28,12 @@ public class ModpackUpdateTask extends Task { - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final String id; private final Task updateTask; private final Path backupFolder; - public ModpackUpdateTask(DefaultGameRepository2 repository, String id, Task updateTask) { + public ModpackUpdateTask(DefaultGameRepository repository, String id, Task updateTask) { this.repository = repository; this.id = id; this.updateTask = updateTask; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java index 73c6647767..5885be26fe 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java @@ -21,7 +21,7 @@ import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.DownloadProvider; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.modpack.ModpackCompletionException; import org.jackhuang.hmcl.addon.RemoteAddon; @@ -51,7 +51,7 @@ public final class CurseCompletionTask extends Task { private final DefaultDependencyManager dependency; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final ModManager modManager; private final String version; private CurseManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java index 8ad9a6f5fc..b8cf50d8f2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.modpack.*; import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Task; @@ -46,7 +46,7 @@ public final class CurseInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final Path zipFile; private final Modpack modpack; private final CurseManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java index 31ce40bf04..730acc2106 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java @@ -19,7 +19,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.modpack.ModpackCompletionException; @@ -51,7 +51,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask { private final DefaultDependencyManager dependency; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final ModManager modManager; private final String version; private final Path configurationFile; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java index b14089bf6b..1d20256ffc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.modpack.mcbbs; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; @@ -44,12 +44,12 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class McbbsModpackExportTask extends Task { - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final String version; private final ModpackExportInfo info; private final Path modpackFile; - public McbbsModpackExportTask(DefaultGameRepository2 repository, String version, ModpackExportInfo info, Path modpackFile) { + public McbbsModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo info, Path modpackFile) { this.repository = repository; this.version = version; this.info = info.validate(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java index 11a1d5a5a5..3e179e893c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.modpack.MinecraftInstanceTask; @@ -46,7 +46,7 @@ public final class McbbsModpackLocalInstallTask extends Task { private final McbbsModpackManifest manifest; private final String name; private final boolean update; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final MinecraftInstanceTask instanceTask; private final List> dependencies = new ArrayList<>(2); private final List> dependents = new ArrayList<>(4); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java index f9a5ce9b32..bfd6ec23d9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -36,7 +36,7 @@ public class McbbsModpackRemoteInstallTask extends Task { private final String name; private final DefaultDependencyManager dependency; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final List> dependencies = new ArrayList<>(1); private final List> dependents = new ArrayList<>(1); private final McbbsModpackManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java index a356071977..6794a52cf3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.modpack.modrinth; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.modpack.ModpackCompletionException; import org.jackhuang.hmcl.task.FileDownloadTask; @@ -41,7 +41,7 @@ public class ModrinthCompletionTask extends Task { private final DefaultDependencyManager dependency; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final ModManager modManager; private final String version; private ModrinthManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java index 391898b99e..00e9a0cc56 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.modpack.*; import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Task; @@ -40,7 +40,7 @@ public class ModrinthInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final Path zipFile; private final Modpack modpack; private final ModrinthManifest manifest; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java index 47ce3b78b0..c589efce48 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java @@ -26,7 +26,7 @@ import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackExportInfo; @@ -42,12 +42,12 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class ModrinthModpackExportTask extends Task { - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final String version; private final ModpackExportInfo info; private final Path modpackFile; - public ModrinthModpackExportTask(DefaultGameRepository2 repository, String version, ModpackExportInfo info, Path modpackFile) { + public ModrinthModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo info, Path modpackFile) { this.repository = repository; this.version = version; this.info = info.validate(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java index 4207d02314..ca0daeebd7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.modpack.multimc; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackExportInfo; @@ -41,7 +41,7 @@ * Export the game to a mod pack file. */ public class MultiMCModpackExportTask extends Task { - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final String versionId; private final List whitelist; private final MultiMCInstanceConfiguration configuration; @@ -51,7 +51,7 @@ public class MultiMCModpackExportTask extends Task { * @param output mod pack file. * @param versionId to locate version.json */ - public MultiMCModpackExportTask(DefaultGameRepository2 repository, String versionId, List whitelist, MultiMCInstanceConfiguration configuration, Path output) { + public MultiMCModpackExportTask(DefaultGameRepository repository, String versionId, List whitelist, MultiMCInstanceConfiguration configuration, Path output) { this.repository = repository; this.versionId = versionId; this.whitelist = whitelist; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java index 53e2fdc3c8..ec9971994b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java @@ -25,7 +25,7 @@ import org.jackhuang.hmcl.download.game.GameDownloadTask; import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.game.Artifact; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.modpack.MinecraftInstanceTask; @@ -80,7 +80,7 @@ public final class MultiMCModpackInstallTask extends Task> dependents = new ArrayList<>(); private final List> dependencies = new ArrayList<>(); private final DefaultDependencyManager dependencyManager; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java index c08ad609b4..ab06e48e38 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.FileDownloadTask; @@ -42,7 +42,7 @@ public class ServerModpackCompletionTask extends Task { private final DefaultDependencyManager dependencyManager; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final String version; private ModpackConfiguration manifest; private GetTask dependent; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java index 1daa8a9e1c..e943675e0f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java @@ -18,7 +18,7 @@ package org.jackhuang.hmcl.modpack.server; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; @@ -40,12 +40,12 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class ServerModpackExportTask extends Task { - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final String versionId; private final ModpackExportInfo exportInfo; private final Path modpackFile; - public ServerModpackExportTask(DefaultGameRepository2 repository, String version, ModpackExportInfo exportInfo, Path modpackFile) { + public ServerModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo exportInfo, Path modpackFile) { this.repository = repository; this.versionId = version; this.exportInfo = exportInfo.validate(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java index f909cc806c..5a1d382a68 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.modpack.MinecraftInstanceTask; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; @@ -41,7 +41,7 @@ public class ServerModpackLocalInstallTask extends Task { private final Modpack modpack; private final ServerModpackManifest manifest; private final String name; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final List> dependencies = new ArrayList<>(); private final List> dependents = new ArrayList<>(4); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java index e15c43084e..ecff6c17ac 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java @@ -20,7 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; -import org.jackhuang.hmcl.game.DefaultGameRepository2; +import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -36,7 +36,7 @@ public class ServerModpackRemoteInstallTask extends Task { private final String name; private final DefaultDependencyManager dependency; - private final DefaultGameRepository2 repository; + private final DefaultGameRepository repository; private final List> dependencies = new ArrayList<>(1); private final List> dependents = new ArrayList<>(1); private final ServerModpackManifest manifest; diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java index c5d33ee4c1..d724020ead 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java @@ -54,7 +54,7 @@ public void testRootManifestWithPatchesUsesPatchView() throws NoSuchGameInstance false, List.of(patch("patch", null))); - GameInstanceManifest.Resolved resolved = new DefaultGameRepository2(Path.of(".")).resolve(manifest); + GameInstanceManifest.Resolved resolved = new DefaultGameRepository(Path.of(".")).resolve(manifest); assertNull(resolved.manifest().mainClass()); assertEquals(List.of(patch("patch", null)), resolved.appliedPatches()); From 86387ca3bda847a01351fa104b12791a6b103777 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 12:02:58 +0800 Subject: [PATCH 25/85] refactor: rename version references to instance in repository and related classes --- .../hmcl/game/HMCLGameRepository.java | 90 +++++++++---------- .../hmcl/game/HMCLModpackInstallTask.java | 4 +- .../jackhuang/hmcl/game/LauncherHelper.java | 6 +- .../org/jackhuang/hmcl/game/LogExporter.java | 2 +- .../jackhuang/hmcl/game/ModpackHelper.java | 14 +-- .../hmcl/setting/GameDirectoryManager.java | 2 +- .../setting/LegacyGameSettingsMigrator.java | 2 +- .../hmcl/ui/download/DownloadPage.java | 4 +- .../hmcl/ui/download/InstallersPage.java | 4 +- .../hmcl/ui/download/LocalModpackPage.java | 6 +- .../hmcl/ui/download/RemoteModpackPage.java | 4 +- .../UpdateInstallerWizardProvider.java | 2 +- .../VanillaInstallWizardProvider.java | 2 +- .../hmcl/ui/game/GameSettingsPage.java | 6 +- .../org/jackhuang/hmcl/ui/main/MainPage.java | 2 +- .../org/jackhuang/hmcl/ui/main/RootPage.java | 4 +- .../hmcl/ui/versions/DownloadListPage.java | 2 +- .../hmcl/ui/versions/DownloadPage.java | 2 +- .../ui/versions/GameAdvancedListItem.java | 8 +- .../jackhuang/hmcl/ui/versions/GameItem.java | 4 +- .../hmcl/ui/versions/GameListItem.java | 2 +- .../hmcl/ui/versions/GameListPage.java | 4 +- .../hmcl/ui/versions/GameListPopupMenu.java | 2 +- .../hmcl/ui/versions/InstallerListPage.java | 10 +-- .../hmcl/ui/versions/ModListPage.java | 2 +- .../hmcl/ui/versions/VersionIconDialog.java | 4 +- .../hmcl/ui/versions/VersionPage.java | 8 +- .../jackhuang/hmcl/ui/versions/Versions.java | 36 ++++---- .../hmcl/setting/GameDirectoriesTest.java | 6 +- .../jackhuang/hmcl/addon/mod/ModManager.java | 2 +- .../resourcepack/ResourcePackManager.java | 2 +- .../download/DefaultDependencyManager.java | 4 +- .../hmcl/download/DefaultGameBuilder.java | 2 +- .../download/forge/ForgeNewInstallTask.java | 4 +- .../hmcl/download/game/GameDownloadTask.java | 2 +- .../game/GameVerificationFixTask.java | 2 +- .../neoforge/NeoForgeOldInstallTask.java | 4 +- .../optifine/OptiFineInstallTask.java | 2 +- .../hmcl/game/DefaultGameRepository.java | 2 +- .../jackhuang/hmcl/game/GameRepository.java | 43 ++------- .../hmcl/launch/DefaultLauncher.java | 12 +-- .../hmcl/modpack/ModpackUpdateTask.java | 8 +- .../modpack/curse/CurseCompletionTask.java | 6 +- .../hmcl/modpack/curse/CurseInstallTask.java | 8 +- .../mcbbs/McbbsModpackCompletionTask.java | 2 +- .../modpack/mcbbs/McbbsModpackExportTask.java | 2 +- .../mcbbs/McbbsModpackLocalInstallTask.java | 8 +- .../mcbbs/McbbsModpackRemoteInstallTask.java | 4 +- .../modrinth/ModrinthCompletionTask.java | 2 +- .../modpack/modrinth/ModrinthInstallTask.java | 6 +- .../modrinth/ModrinthModpackExportTask.java | 2 +- .../multimc/MultiMCModpackExportTask.java | 2 +- .../multimc/MultiMCModpackInstallTask.java | 10 +-- .../server/ServerModpackCompletionTask.java | 2 +- .../server/ServerModpackExportTask.java | 2 +- .../server/ServerModpackLocalInstallTask.java | 4 +- .../ServerModpackRemoteInstallTask.java | 4 +- 57 files changed, 184 insertions(+), 213 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index a8aac21e99..5c47009353 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -107,9 +107,9 @@ public record InstanceReference(HMCLGameRepository repository, @Nullable String /// Instance IDs whose local game settings file has already been checked. private final Set loadedInstanceGameSettings = new HashSet<>(); private final Set readOnlyInstanceGameSettings = new HashSet<>(); - private final Set beingModpackVersions = new HashSet<>(); + private final Set beingModpackInstances = new HashSet<>(); - public final EventManager onVersionIconChanged = new EventManager<>(); + public final EventManager onInstanceIconChanged = new EventManager<>(); /// Creates a repository backed by the given game directory. public HMCLGameRepository(GameDirectory gameDirectory) { @@ -143,8 +143,8 @@ public void setSelectedInstance(@Nullable String instance) { public void refreshSelectedInstance() { @Nullable String selectedInstance = settings().getSelectedInstance(gameDirectory.getId()); @Nullable String refreshedInstance = selectedInstance; - if (!hasVersion(refreshedInstance)) { - refreshedInstance = getVersions().isEmpty() ? null : getVersions().iterator().next().getId(); + if (!hasInstance(refreshedInstance)) { + refreshedInstance = getInstanceManifests().isEmpty() ? null : getInstanceManifests().iterator().next().getId(); } if (!Objects.equals(selectedInstance, refreshedInstance)) { setSelectedInstance(refreshedInstance); @@ -163,8 +163,8 @@ public DefaultDependencyManager getDependency(DownloadProvider downloadProvider) @Override public Path getRunDirectory(String id) { - if (beingModpackVersions.contains(id) || isModpack(id)) { - return getVersionRoot(id); + if (beingModpackInstances.contains(id) || isModpack(id)) { + return getInstanceRoot(id); } GameSettings.Instance localSetting = getInstanceGameSettings(id); @@ -173,13 +173,13 @@ public Path getRunDirectory(String id) { String runningDirectory = getSelectedRunningDirectory(localSetting, useInstanceRunningDirectory); if (StringUtils.isBlank(runningDirectory)) { - return useInstanceRunningDirectory ? getVersionRoot(id) : super.getRunDirectory(id); + return useInstanceRunningDirectory ? getInstanceRoot(id) : super.getRunDirectory(id); } try { return Path.of(runningDirectory); } catch (InvalidPathException ignored) { - return getVersionRoot(id); + return getInstanceRoot(id); } } @@ -201,8 +201,8 @@ private String getSelectedRunningDirectory( return Objects.requireNonNullElse(parent.runningDirectoryProperty().getValue(), ""); } - public Stream getDisplayVersions() { - return getVersions().stream() + public Stream getDisplayInstanceManifests() { + return getInstanceManifests().stream() .filter(v -> !v.isHidden()) .sorted(Comparator.comparing((GameInstanceManifest v) -> Lang.requireNonNullElse(v.getReleaseTime(), Instant.EPOCH)) .thenComparing(v -> VersionNumber.asVersion(v.getId()))); @@ -214,11 +214,11 @@ protected void refreshImpl() { loadedInstanceGameSettings.clear(); readOnlyInstanceGameSettings.clear(); super.refreshImpl(); - getVersions().stream().map(GameInstanceManifest::getId).forEach(this::loadInstanceGameSettings); + getInstanceManifests().stream().map(GameInstanceManifest::getId).forEach(this::loadInstanceGameSettings); try { Path file = getBaseDirectory().resolve("launcher_profiles.json"); - if (!Files.exists(file) && !getVersions().isEmpty()) { + if (!Files.exists(file) && !getInstanceManifests().isEmpty()) { Files.createDirectories(file.getParent()); Files.writeString(file, PROFILE); } @@ -229,7 +229,7 @@ protected void refreshImpl() { public void changeDirectory(Path newDirectory) { setBaseDirectory(newDirectory); - refreshVersionsAsync().start(); + refreshAsync().start(); } private void clean(Path directory) throws IOException { @@ -242,11 +242,11 @@ public void clean(String id) throws IOException { clean(getRunDirectory(id)); } - public void duplicateVersion(String srcId, String dstId, boolean copySaves) throws IOException { - Path srcDir = getVersionRoot(srcId); - Path dstDir = getVersionRoot(dstId); + public void duplicateInstance(String srcId, String dstId, boolean copySaves) throws IOException { + Path srcDir = getInstanceRoot(srcId); + Path dstDir = getInstanceRoot(dstId); - GameInstanceManifest fromVersion = getVersion(srcId); + GameInstanceManifest fromVersion = getInstanceManifest(srcId); List blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); blackList.add(srcId + ".jar"); @@ -273,7 +273,7 @@ public void duplicateVersion(String srcId, String dstId, boolean copySaves) thro boolean copyOriginalGameDir; try { - copyOriginalGameDir = !Files.isSameFile(getRunDirectory(srcId), getVersionRoot(srcId)); + copyOriginalGameDir = !Files.isSameFile(getRunDirectory(srcId), getInstanceRoot(srcId)); } catch (IOException e) { copyOriginalGameDir = true; } @@ -307,7 +307,7 @@ private GameSettings.Instance copyInstanceGameSettings(String id) { /// /// This directory stores instance-scoped files owned by HMCL. public Path getInstanceMetadataDirectory(String id) { - return getVersionRoot(id).resolve(INSTANCE_METADATA_DIRECTORY); + return getInstanceRoot(id).resolve(INSTANCE_METADATA_DIRECTORY); } /// Returns the HMCL-managed configuration directory under the instance metadata directory. @@ -417,7 +417,7 @@ private InstanceGameSettingsLoadResult loadGameSettingsFile(Path file) { } public @Nullable GameSettings.Instance createInstanceGameSettings(String id) { - if (!hasVersion(id)) { + if (!hasInstance(id)) { return null; } if (readOnlyInstanceGameSettings.contains(id)) { @@ -526,7 +526,7 @@ public GameSettings.Effective getEffectiveGameSettings(String id) { } public void applyDefaultIsolationSetting(String id) { - if (!hasVersion(id)) { + if (!hasInstance(id)) { return; } @@ -536,7 +536,7 @@ public void applyDefaultIsolationSetting(String id) { boolean isolated = switch (type) { case NEVER -> false; case ALWAYS -> true; - case MODDED -> LibraryAnalyzer.isModded(this, getVersion(id).resolve(this)); + case MODDED -> LibraryAnalyzer.isModded(this, getInstanceManifest(id).resolve(this)); }; if (isolated) { @@ -547,8 +547,8 @@ public void applyDefaultIsolationSetting(String id) { } } - public Optional getVersionIconFile(String id) { - Path root = getVersionRoot(id); + public Optional getInstanceIconFile(String id) { + Path root = getInstanceRoot(id); for (String extension : FXUtils.IMAGE_EXTENSIONS) { Path file = root.resolve("icon." + extension); @@ -560,7 +560,7 @@ public Optional getVersionIconFile(String id) { return Optional.empty(); } - public void setVersionIconFile(String id, Path iconFile) throws IOException { + public void setInstanceIconFile(String id, Path iconFile) throws IOException { String ext = FileUtils.getExtension(iconFile).toLowerCase(Locale.ROOT); if (!FXUtils.IMAGE_EXTENSIONS.contains(ext)) { throw new IllegalArgumentException("Unsupported icon file: " + ext); @@ -568,11 +568,11 @@ public void setVersionIconFile(String id, Path iconFile) throws IOException { deleteIconFile(id); - FileUtils.copyFile(iconFile, getVersionRoot(id).resolve("icon." + ext)); + FileUtils.copyFile(iconFile, getInstanceRoot(id).resolve("icon." + ext)); } public void deleteIconFile(String id) { - Path root = getVersionRoot(id); + Path root = getInstanceRoot(id); for (String extension : FXUtils.IMAGE_EXTENSIONS) { Path file = root.resolve("icon." + extension); try { @@ -583,7 +583,7 @@ public void deleteIconFile(String id) { } } - public Image getVersionIconImage(@Nullable String id) { + public Image getInstanceIconImage(@Nullable String id) { if (id == null || !isLoaded()) return VersionIconType.DEFAULT.getIcon(); @@ -591,8 +591,8 @@ public Image getVersionIconImage(@Nullable String id) { VersionIconType iconType = setting != null ? Lang.requireNonNullElse(setting.iconProperty().getValue(), VersionIconType.DEFAULT) : VersionIconType.DEFAULT; if (iconType == VersionIconType.DEFAULT) { - GameInstanceManifest version = getVersion(id).resolve(this); - Optional iconFile = getVersionIconFile(id); + GameInstanceManifest version = getInstanceManifest(id).resolve(this); + Optional iconFile = getInstanceIconFile(id); if (iconFile.isPresent()) { try { return FXUtils.loadImage(iconFile.get(), 64, 64, true, true); @@ -770,26 +770,26 @@ public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVe @Override public Path getModpackConfiguration(String version) { - return getVersionRoot(version).resolve("modpack.cfg"); + return getInstanceRoot(version).resolve("modpack.cfg"); } - public void markVersionAsModpack(String id) { - beingModpackVersions.add(id); + public void markInstanceAsModpack(String id) { + beingModpackInstances.add(id); } public void undoMark(String id) { - beingModpackVersions.remove(id); + beingModpackInstances.remove(id); } - public void markVersionLaunchedAbnormally(String id) { + public void markInstanceLaunchedAbnormally(String id) { try { - Files.createFile(getVersionRoot(id).resolve(".abnormal")); + Files.createFile(getInstanceRoot(id).resolve(".abnormal")); } catch (IOException ignored) { } } - public boolean unmarkVersionLaunchedAbnormally(String id) { - Path file = getVersionRoot(id).resolve(".abnormal"); + public boolean unmarkInstanceLaunchedAbnormally(String id) { + Path file = getInstanceRoot(id).resolve(".abnormal"); if (Files.isRegularFile(file)) { try { Files.delete(file); @@ -807,15 +807,15 @@ public boolean unmarkVersionLaunchedAbnormally(String id) { // These version ids are forbidden because they may conflict with modpack configuration filenames - private static final Set FORBIDDEN_VERSION_IDS = new HashSet<>(Arrays.asList( + private static final Set FORBIDDEN_INSTANCE_IDS = new HashSet<>(Arrays.asList( "modpack", "minecraftinstance", "manifest")); - public static boolean isValidVersionId(String id) { - if (FORBIDDEN_VERSION_IDS.contains(id)) + public static boolean isValidInstanceId(String id) { + if (FORBIDDEN_INSTANCE_IDS.contains(id)) return false; if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && - FORBIDDEN_VERSION_IDS.contains(id.toLowerCase(Locale.ROOT))) + FORBIDDEN_INSTANCE_IDS.contains(id.toLowerCase(Locale.ROOT))) return false; return FileUtils.isNameValidForJar(id); @@ -824,10 +824,10 @@ public static boolean isValidVersionId(String id) { /** * Returns true if the given version id conflicts with an existing version. */ - public boolean versionIdConflicts(String id) { + public boolean instanceIdConflicts(String id) { if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { // on Windows, filenames are case-insensitive - for (GameInstanceManifest manifest : getVersions()) { + for (GameInstanceManifest manifest : getInstanceManifests()) { String existingId = manifest.getId(); if (existingId.equalsIgnoreCase(id)) { return true; @@ -835,7 +835,7 @@ public boolean versionIdConflicts(String id) { } return false; } else { - return hasVersion(id); + return hasInstance(id); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java index 1d759187c2..715dbd27e2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java @@ -53,13 +53,13 @@ public HMCLModpackInstallTask(HMCLGameRepository repository, Path zipFile, Modpa Path run = repository.getRunDirectory(name); Path json = repository.getModpackConfiguration(name); - if (repository.hasVersion(name) && Files.notExists(json)) + if (repository.hasInstance(name) && Files.notExists(json)) throw new IllegalArgumentException("Version " + name + " already exists"); dependents.add(dependency.gameBuilder().name(name).gameVersion(modpack.getGameVersion()).buildAsync()); onDone().register(event -> { - if (event.isFailed()) repository.removeVersionFromDisk(name); + if (event.isFailed()) repository.removeInstanceFromDisk(name); }); ModpackConfiguration config = null; 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 d27bb85fac..e46bfc07af 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -148,9 +148,9 @@ private void launch0() { PROCESSES.removeIf(it -> it.get() == null); DefaultDependencyManager dependencyManager = repository.getDependency(); - AtomicReference version = new AtomicReference<>(MaintainTask.maintain(repository, repository.getResolvedVersion(selectedVersion))); + AtomicReference version = new AtomicReference<>(MaintainTask.maintain(repository, repository.getResolvedInstanceManifest(selectedVersion))); Optional gameVersion = repository.getGameVersion(version.get()); - boolean integrityCheck = repository.unmarkVersionLaunchedAbnormally(selectedVersion); + boolean integrityCheck = repository.unmarkInstanceLaunchedAbnormally(selectedVersion); CountDownLatch launchingLatch = new CountDownLatch(1); List javaAgents = new ArrayList<>(0); List javaArguments = new ArrayList<>(0); @@ -1000,7 +1000,7 @@ public void onExit(int exitCode, ExitType exitType) { } if (exitType != ExitType.NORMAL) { - repository.markVersionLaunchedAbnormally(version.getId()); + repository.markInstanceLaunchedAbnormally(version.getId()); runLater(() -> new GameCrashWindow(process, exitType, repository, version, launchOptions, logs).show()); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java index dd8511bf66..4d2350dfc9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java @@ -53,7 +53,7 @@ public static CompletableFuture exportLogs( while (true) { if (resolvedSoFar.contains(currentVersionId)) break; resolvedSoFar.add(currentVersionId); - GameInstanceManifest currentVersion = gameRepository.getVersion(currentVersionId); + GameInstanceManifest currentVersion = gameRepository.getInstanceManifest(currentVersionId); versions.add(currentVersionId); if (StringUtils.isNotBlank(currentVersion.getInheritsFrom())) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index e84044e1b8..93b67ac9df 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -157,10 +157,10 @@ public static ModpackConfiguration readModpackConfiguration(Path file) throws } public static Task getInstallTask(HMCLGameRepository repository, ServerModpackManifest manifest, String name, Modpack modpack) { - repository.markVersionAsModpack(name); + repository.markInstanceAsModpack(name); ExceptionalRunnable success = () -> { - repository.refreshVersions(); + repository.refresh(); GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(name); repository.undoMark(name); if (setting != null) { @@ -201,10 +201,10 @@ public static Task getInstallManuallyCreatedModpackTask(Path zipFile, String } public static Task getInstallTask(HMCLGameRepository repository, Path zipFile, String name, Modpack modpack, String iconUrl) { - repository.markVersionAsModpack(name); + repository.markInstanceAsModpack(name); ExceptionalRunnable success = () -> { - repository.refreshVersions(); + repository.refresh(); GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(name); repository.undoMark(name); if (setting != null) { @@ -239,7 +239,7 @@ public static Task getUpdateTask(HMCLGameRepository repository, ServerModp switch (configuration.getType()) { case ServerModpackRemoteInstallTask.MODPACK_TYPE: return new ModpackUpdateTask(repository, name, new ServerModpackRemoteInstallTask(repository.getDependency(), manifest, name)) - .thenComposeAsync(repository.refreshVersionsAsync()) + .thenComposeAsync(repository.refreshAsync()) .withStagesHints(new Task.StagesHint("hmcl.modpack"), new Task.StagesHint("hmcl.modpack.download", List.of("hmcl.install.assets", "hmcl.install.libraries"))); default: throw new UnsupportedModpackException(); @@ -255,10 +255,10 @@ public static Task getUpdateTask(HMCLGameRepository repository, Path zipFile, if (modpack.getManifest() instanceof MultiMCInstanceConfiguration) return provider.createUpdateTask(repository.getDependency(), name, zipFile, modpack) .thenComposeAsync(() -> createMultiMCPostUpdateTask(repository, (MultiMCInstanceConfiguration) modpack.getManifest(), name)) - .thenComposeAsync(repository.refreshVersionsAsync()); + .thenComposeAsync(repository.refreshAsync()); else return provider.createUpdateTask(repository.getDependency(), name, zipFile, modpack) - .thenComposeAsync(repository.refreshVersionsAsync()); + .thenComposeAsync(repository.refreshAsync()); } public static void toGameSettings(MultiMCInstanceConfiguration c, GameSettings.Instance setting) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java index 58b8939428..f093cbe076 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java @@ -195,7 +195,7 @@ public static void init() { selectedRepository.set(repository); selectedInstance.set(repository.getSelectedInstance()); repository.selectedInstanceProperty().addListener(selectedRepositoryInstanceListener); - repository.refreshVersionsAsync().start(); + repository.refreshAsync().start(); }); selectedGameDirectory.set(currentGameDirectory != null ? currentGameDirectory : mergedGameDirectories.get(0)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java index 16dea2450f..3704b5f8f6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java @@ -132,7 +132,7 @@ public static GameSettings.Preset toPreset(GameSettingsPresetID id, int autoName HMCLGameRepository repository, String instanceId, @Nullable GameSettingsPresetID parent) { - Path instanceRoot = repository.getVersionRoot(instanceId); + Path instanceRoot = repository.getInstanceRoot(instanceId); Path file = instanceRoot.resolve(LEGACY_INSTANCE_SETTINGS_FILENAME); if (!Files.exists(file)) { return null; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index 58a3cc5fc3..342049ba4c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -144,7 +144,7 @@ private static Supplier loadVersionFor(Supplier nodeSuppl public static void download(DownloadProvider downloadProvider, HMCLGameRepository repository, @Nullable String version, RemoteAddon.Version file, String subdirectoryName) { if (version == null) version = repository.getSelectedInstance(); - Path runDirectory = repository.hasVersion(version) ? repository.getRunDirectory(version) : repository.getBaseDirectory(); + Path runDirectory = repository.hasInstance(version) ? repository.getRunDirectory(version) : repository.getBaseDirectory(); Set existingFiles; @@ -319,7 +319,7 @@ private Task finishVersionDownloadingAsync(SettingsMap settings) { }); return builder.buildAsync().whenComplete(any -> { - repository.refreshVersions(); + repository.refresh(); repository.applyDefaultIsolationSetting(name); }).thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(name)); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallersPage.java index 3d07ed74b6..7678129b22 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallersPage.java @@ -42,8 +42,8 @@ public InstallersPage(WizardController controller, HMCLGameRepository repository txtName.getValidators().addAll( new RequiredValidator(), - new Validator(i18n("install.new_game.already_exists"), str -> !repository.versionIdConflicts(str)), - new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId)); + new Validator(i18n("install.new_game.already_exists"), str -> !repository.instanceIdConflicts(str)), + new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId)); installable.bind(createBooleanBinding(txtName::validate, txtName.textProperty())); txtName.textProperty().addListener((obs, oldText, newText) -> isNameModifiedByUser = true); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index c2275a0d9b..39f92f5cc0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -66,8 +66,8 @@ public LocalModpackPage(WizardController controller) { if (installAsVersion) { txtModpackName.getValidators().setAll( new RequiredValidator(), - new Validator(i18n("install.new_game.already_exists"), str -> !repository.versionIdConflicts(str)), - new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId)); + new Validator(i18n("install.new_game.already_exists"), str -> !repository.instanceIdConflicts(str)), + new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId)); } else { txtModpackName.getValidators().setAll( new RequiredValidator(), @@ -75,7 +75,7 @@ public LocalModpackPage(WizardController controller) { && GameDirectoryManager.getGameDirectories().stream() .noneMatch(existingProfile -> str.equals(GameDirectoryManager.getGameDirectoryCustomName(existingProfile)))), - new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId)); + new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId)); } }); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/RemoteModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/RemoteModpackPage.java index 3da22a67ac..bbda5ce46b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/RemoteModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/RemoteModpackPage.java @@ -66,8 +66,8 @@ public RemoteModpackPage(WizardController controller) { txtModpackName.setText(manifest.getName().trim()); txtModpackName.getValidators().addAll( new RequiredValidator(), - new Validator(i18n("install.new_game.already_exists"), str -> !repository.versionIdConflicts(str)), - new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId)); + new Validator(i18n("install.new_game.already_exists"), str -> !repository.instanceIdConflicts(str)), + new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId)); } btnDescription.setVisible(StringUtils.isNotBlank(manifest.getDescription())); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java index 12385b64f6..4566f0a3ac 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java @@ -91,7 +91,7 @@ public Object finish(SettingsMap settings) { } } - return ret.thenComposeAsync(repository::saveAsync).thenComposeAsync(repository.refreshVersionsAsync()).withStagesHints(hints); + return ret.thenComposeAsync(repository::saveAsync).thenComposeAsync(repository.refreshAsync()).withStagesHints(hints); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java index b0289431c9..2c7b5bed7d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java @@ -62,7 +62,7 @@ private Task finishVersionDownloadingAsync(SettingsMap settings) { }); return builder.buildAsync().whenComplete(any -> { - repository.refreshVersions(); + repository.refresh(); repository.applyDefaultIsolationSetting(name); }) .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(name)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java index 5ec3603a20..617fea6c58 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java @@ -1799,7 +1799,7 @@ private String getCurrentInstanceVersionRoot() { return ""; } - return repository.getVersionRoot(instanceId).toString(); + return repository.getInstanceRoot(instanceId).toString(); } /// Keeps a listener attached to the current instance's parent preset property. @@ -2528,7 +2528,7 @@ private void loadIcon() { if (repository == null || instanceId == null) return; - iconPickerItem.setImage(repository.getVersionIconImage(instanceId)); + iconPickerItem.setImage(repository.getInstanceIconImage(instanceId)); } /// Refreshes Java selection controls and keeps inherited parent Java properties observed. @@ -2618,7 +2618,7 @@ private void initJavaSubtitle() { if (this.instanceId == null) { version = null; } else { - version = repository != null ? repository.getResolvedVersion(this.instanceId) : null; + version = repository != null ? repository.getResolvedInstanceManifest(this.instanceId) : null; } try { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 8130ea7ea1..3b512b353d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -342,7 +342,7 @@ private void launchNoGame() { .gameVersion(gameVersion) .buildAsync(); }) - .whenComplete(any -> GameDirectoryManager.getSelectedRepository().refreshVersions()) + .whenComplete(any -> GameDirectoryManager.getSelectedRepository().refresh()) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { GameDirectoryManager.getSelectedRepository().setSelectedInstance(gameVersionHolder.value); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index a911335f4e..7a561dc477 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -124,7 +124,7 @@ public MainPage getMainPage() { GameDirectoryManager.registerVersionsListener(repository -> { GameDirectory gameDirectory = repository.getGameDirectory(); - List children = repository.getVersions().parallelStream() + List children = repository.getInstanceManifests().parallelStream() .filter(version -> !version.isHidden()) .sorted(Comparator .comparing((GameInstanceManifest version) -> Lang.requireNonNullElse(version.getReleaseTime(), Instant.EPOCH)) @@ -263,7 +263,7 @@ private void onRefreshedVersions(HMCLGameRepository repository) { if (!checkedModpack) { checkedModpack = true; - if (repository.getVersionCount() == 0) { + if (repository.getInstanceCount() == 0) { Path zipModpack = Metadata.CURRENT_DIRECTORY.resolve("modpack.zip"); Path mrpackModpack = Metadata.CURRENT_DIRECTORY.resolve("modpack.mrpack"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index 530500bee9..e14cfa5eea 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -123,7 +123,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable String instanc } if (versionSelection) { - versions.setAll(repository.getDisplayVersions() + versions.setAll(repository.getDisplayInstanceManifests() .map(GameInstanceManifest::getId) .collect(Collectors.toList())); selectedVersion.set(repository.getSelectedInstance()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index b5980402a4..03a27a3a6d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -271,7 +271,7 @@ protected DownloadPageSkin(DownloadPage control) { if (control.instanceReference.repository() != null && control.instanceReference.instanceId() != null) { HMCLGameRepository repository = control.instanceReference.repository(); - GameInstanceManifest game = repository.getResolvedPreservingPatchesVersion(control.instanceReference.instanceId()); + GameInstanceManifest game = repository.getResolvedPreservingPatchesManifest(control.instanceReference.instanceId()); String gameVersion = repository.getGameVersion(game).orElse(null); if (gameVersion != null && control.versions.containsKey(gameVersion)) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java index d280204518..9e691d68bb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java @@ -36,7 +36,7 @@ public class GameAdvancedListItem extends AdvancedListItem { private final WeakListenerHolder holder = new WeakListenerHolder(); private HMCLGameRepository repository; @SuppressWarnings("unused") - private Consumer onVersionIconChangedListener; + private Consumer onInstanceIconChangedListener; public GameAdvancedListItem() { this.imageContainer = new ImageContainer(LEFT_GRAPHIC_SIZE); @@ -51,15 +51,15 @@ private void loadVersion(String version) { if (GameDirectoryManager.getSelectedRepository() != repository) { repository = GameDirectoryManager.getSelectedRepository(); if (repository != null) { - onVersionIconChangedListener = repository.onVersionIconChanged.registerWeak(event -> { + onInstanceIconChangedListener = repository.onInstanceIconChanged.registerWeak(event -> { this.loadVersion(repository.getSelectedInstance()); }); } } - if (version != null && repository != null && repository.hasVersion(version)) { + if (version != null && repository != null && repository.hasInstance(version)) { setTitle(i18n("version.manage.manage")); setSubtitle(version); - imageContainer.setImage(repository.getVersionIconImage(version)); + imageContainer.setImage(repository.getInstanceIconImage(version)); } else { setTitle(i18n("version.empty")); setSubtitle(i18n("version.empty.add")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java index a282be3d01..7e3ef33a4a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java @@ -99,7 +99,7 @@ record Result(@Nullable String gameVersion, @Nullable String tag) { } StringBuilder libraries = new StringBuilder(Objects.requireNonNullElse(result.gameVersion, i18n("message.unknown"))); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(id), result.gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(id), result.gameVersion); for (LibraryAnalyzer.LibraryMark mark : analyzer) { String libraryId = mark.getLibraryId(); String libraryVersion = mark.getLibraryVersion(); @@ -118,7 +118,7 @@ record Result(@Nullable String gameVersion, @Nullable String tag) { }, Schedulers.javafx()); title.set(id); - image.set(repository.getVersionIconImage(id)); + image.set(repository.getInstanceIconImage(id)); } public ReadOnlyStringProperty titleProperty() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java index 6fd36783a6..70e4c62052 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java @@ -48,7 +48,7 @@ public void rename() { } public void duplicate() { - Versions.duplicateVersion(repository, id); + Versions.duplicateInstance(repository, id); } public void remove() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java index b83aac3ac5..4510234211 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java @@ -152,7 +152,7 @@ private void loadVersions(HMCLGameRepository repository) { setLoading(true); setFailedReason(null); - List versionItems = repository.getDisplayVersions().map(instance -> new GameListItem(repository, instance.getId())).toList(); + List versionItems = repository.getDisplayInstanceManifests().map(instance -> new GameListItem(repository, instance.getId())).toList(); sourceList.setAll(versionItems); @@ -182,7 +182,7 @@ private Predicate createPredicate(String searchText) { } public void refreshList() { - GameDirectoryManager.getSelectedRepository().refreshVersionsAsync().start(); + GameDirectoryManager.getSelectedRepository().refreshAsync().start(); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java index 6dce9afdee..19d35e6024 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java @@ -53,7 +53,7 @@ public static void show(Node owner, JFXPopup.PopupVPosition vAlign, JFXPopup.Pop HMCLGameRepository repository, List versions) { GameListPopupMenu menu = new GameListPopupMenu(); menu.getItems().setAll(versions.stream() - .filter(it -> repository.hasVersion(it.getId())) + .filter(it -> repository.hasInstance(it.getId())) .map(it -> new GameItem(repository, it.getId())) .toList()); JFXPopup popup = new JFXPopup(menu); 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 6106a295bf..f5cb77df32 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 @@ -64,13 +64,13 @@ protected Skin createDefaultSkin() { public void loadInstance(HMCLGameRepository repository, String instanceId) { this.repository = repository; this.versionId = instanceId; - this.version = repository.getVersion(instanceId); + this.version = repository.getInstanceManifest(instanceId); this.gameVersion = null; CompletableFuture.supplyAsync(() -> { gameVersion = repository.getGameVersion(version).orElse(null); - return LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(instanceId), gameVersion); + return LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); }).thenAcceptAsync(analyzer -> { itemsProperty().clear(); @@ -103,7 +103,7 @@ public void loadInstance(HMCLGameRepository repository, String instanceId) { item.setOnRemove(() -> repository.getDependency().removeLibraryAsync(version, libraryId) .thenComposeAsync(repository::saveAsync) - .withComposeAsync(repository.refreshVersionsAsync()) + .withComposeAsync(repository.refreshAsync()) .withRunAsync(Schedulers.javafx(), () -> loadInstance(this.repository, this.versionId)) .start()); @@ -125,7 +125,7 @@ public void loadInstance(HMCLGameRepository repository, String instanceId) { installerItem.versionProperty().set(new InstallerItem.InstalledState(libraryVersion, false, false)); installerItem.setOnRemove(() -> repository.getDependency().removeLibraryAsync(version, libraryId) .thenComposeAsync(repository::saveAsync) - .withComposeAsync(repository.refreshVersionsAsync()) + .withComposeAsync(repository.refreshAsync()) .withRunAsync(Schedulers.javafx(), () -> loadInstance(this.repository, this.versionId)) .start()); @@ -144,7 +144,7 @@ public void installOffline() { private void doInstallOffline(Path file) { Task task = repository.getDependency().installLibraryAsync(version, file) .thenComposeAsync(repository::saveAsync) - .thenComposeAsync(repository.refreshVersionsAsync()); + .thenComposeAsync(repository.refreshAsync()); task.setName(i18n("install.installer.install_offline")); TaskExecutor executor = task.executor(new TaskListener() { @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java index 5a01834fca..8a31744082 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java @@ -90,7 +90,7 @@ public void loadInstance(HMCLGameRepository repository, String instanceId) { this.repository = repository; this.instanceId = instanceId; - GameInstanceManifest resolved = repository.getResolvedPreservingPatchesVersion(instanceId); + GameInstanceManifest resolved = repository.getResolvedPreservingPatchesManifest(instanceId); this.gameVersion = repository.getGameVersion(resolved).orElse(null); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion); modded.set(analyzer.hasModLoader()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index 99378fa686..aaa7a3cff9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -79,7 +79,7 @@ private void exploreIcon() { Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (selectedFile != null) { try { - repository.setVersionIconFile(versionId, selectedFile); + repository.setInstanceIconFile(versionId, selectedFile); if (setting != null) { setting.iconProperty().setValue(VersionIconType.DEFAULT); @@ -119,7 +119,7 @@ private Node createIcon(VersionIconType type) { @Override protected void onAccept() { - repository.onVersionIconChanged.fireEvent(new Event(this)); + repository.onInstanceIconChanged.fireEvent(new Event(this)); onFinish.run(); super.onAccept(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java index 860c7bf6c7..0ce5def1f1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java @@ -112,7 +112,7 @@ private void checkSelectedVersion() { runInFX(() -> { if (this.instanceReference.get() == null) return; HMCLGameRepository repository = this.instanceReference.get().repository(); - if (!repository.hasVersion(this.instanceReference.get().instanceId())) { + if (!repository.hasInstance(this.instanceReference.get().instanceId())) { if (preferredVersionName != null) { loadVersion(preferredVersionName, repository); } else { @@ -146,7 +146,7 @@ public void loadVersion(String version, HMCLGameRepository repository) { // If we jumped to game list page and deleted this version // and back to this page, we should return to main page. if (this.instanceReference.get() != null && (!getRepository().isLoaded() || - !getRepository().hasVersion(version))) { + !getRepository().hasInstance(version))) { Platform.runLater(() -> fireEvent(new PageCloseEvent())); return; } @@ -176,7 +176,7 @@ private void onNavigated(Navigator.NavigationEvent event) { // If we jumped to game list page and deleted this version // and back to this page, we should return to main page. if (!getRepository().isLoaded() || - !getRepository().hasVersion(getVersion())) { + !getRepository().hasInstance(getVersion())) { Platform.runLater(() -> fireEvent(new PageCloseEvent())); return; } @@ -253,7 +253,7 @@ private void remove() { } private void duplicate() { - Versions.duplicateVersion(getRepository(), getVersion()); + Versions.duplicateInstance(getRepository(), getVersion()); } public HMCLGameRepository getRepository() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index d93df43748..cd659cf104 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -118,14 +118,14 @@ public static void downloadModpackImpl(DownloadProvider downloadProvider, HMCLGa public static void deleteVersion(HMCLGameRepository repository, String version) { boolean isIndependent = repository.getRunDirectory(version).toAbsolutePath().normalize() - .equals(repository.getVersionRoot(version).toAbsolutePath().normalize()); + .equals(repository.getInstanceRoot(version).toAbsolutePath().normalize()); String message = isIndependent ? i18n("version.manage.remove.confirm.independent", version) : i18n("version.manage.remove.confirm.trash", version, version + "_removed"); JFXButton deleteButton = new JFXButton(i18n("button.delete")); deleteButton.getStyleClass().add("dialog-error"); deleteButton.setOnAction(e -> { - Task.supplyAsync(Schedulers.io(), () -> repository.removeVersionFromDisk(version)) + Task.supplyAsync(Schedulers.io(), () -> repository.removeInstanceFromDisk(version)) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception != null || !Boolean.TRUE.equals(result)) { Controllers.dialog(i18n("version.manage.remove.failed"), i18n("message.error"), MessageDialogPane.MessageType.ERROR); @@ -142,11 +142,11 @@ public static CompletableFuture renameVersion(HMCLGameRepository reposit handler.resolve(); return; } - if (repository.renameVersion(version, newName)) { + if (repository.renameInstance(version, newName)) { handler.resolve(); - repository.refreshVersionsAsync() + repository.refreshAsync() .thenRunAsync(Schedulers.javafx(), () -> { - if (repository.hasVersion(newName)) { + if (repository.hasInstance(newName)) { repository.setSelectedInstance(newName); } }).start(); @@ -154,8 +154,8 @@ public static CompletableFuture renameVersion(HMCLGameRepository reposit handler.reject(i18n("version.manage.rename.fail")); } }, version, - new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), - new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.versionIdConflicts(newVersionName) || newVersionName.equals(version))); + new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId), + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName) || newVersionName.equals(version))); } public static void exportVersion(HMCLGameRepository repository, String version) { @@ -193,7 +193,7 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { // ignore failure })) .thenComposeAsync(repository.saveAsync(newVersion)) - .thenRunAsync(repository::refreshVersions) + .thenRunAsync(repository::refresh) .whenComplete(Schedulers.javafx(), (exception) -> { if (exception == null) { repository.setSelectedInstance(result); @@ -202,31 +202,31 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { DownloadProviders.localizeErrorMessage(exception), i18n("install.failed"), MessageDialogPane.MessageType.ERROR); } }), i18n("install.new_game"), TaskCancellationAction.NORMAL); - }, FileUtils.getNameWithoutExtension(file), new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.versionIdConflicts(newVersionName))); + }, FileUtils.getNameWithoutExtension(file), new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId), new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName))); } - public static void duplicateVersion(HMCLGameRepository repository, String version) { + public static void duplicateInstance(HMCLGameRepository repository, String version) { Controllers.prompt( new PromptDialogPane.Builder(i18n("version.manage.duplicate.prompt"), (res, handler) -> { String newVersionName = ((PromptDialogPane.Builder.StringQuestion) res.get(1)).getValue(); boolean copySaves = ((PromptDialogPane.Builder.BooleanQuestion) res.get(2)).getValue(); - Task.runAsync(() -> repository.duplicateVersion(version, newVersionName, copySaves)) - .thenComposeAsync(repository.refreshVersionsAsync()) + Task.runAsync(() -> repository.duplicateInstance(version, newVersionName, copySaves)) + .thenComposeAsync(repository.refreshAsync()) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { handler.resolve(); } else { handler.reject(StringUtils.getStackTrace(exception)); - if (!repository.versionIdConflicts(newVersionName)) { - repository.removeVersionFromDisk(newVersionName); + if (!repository.instanceIdConflicts(newVersionName)) { + repository.removeInstanceFromDisk(newVersionName); } } }).start(); }) .addQuestion(new PromptDialogPane.Builder.HintQuestion(i18n("version.manage.duplicate.confirm"))) .addQuestion(new PromptDialogPane.Builder.StringQuestion(null, version, - new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), - new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.versionIdConflicts(newVersionName)))) + new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId), + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName)))) .addQuestion(new PromptDialogPane.Builder.BooleanQuestion(i18n("version.manage.duplicate.duplicate_save"), false))); } @@ -235,7 +235,7 @@ public static void updateVersion(HMCLGameRepository repository, String version) } public static void updateGameAssets(HMCLGameRepository repository, String version) { - TaskExecutor executor = new GameAssetDownloadTask(repository.getDependency(), repository.getVersion(version), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true) + TaskExecutor executor = new GameAssetDownloadTask(repository.getDependency(), repository.getInstanceManifest(version), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true) .executor(); Controllers.taskDialog(executor, i18n("version.manage.redownload_assets_index"), TaskCancellationAction.NO_CANCEL); executor.start(); @@ -326,7 +326,7 @@ public static void generateLaunchScriptForQuickEnterWorld(HMCLGameRepository rep } private static boolean checkVersionForLaunching(HMCLGameRepository repository, String id) { - if (id == null || !repository.isLoaded() || !repository.hasVersion(id)) { + if (id == null || !repository.isLoaded() || !repository.hasInstance(id)) { JFXButton gotoDownload = new JFXButton(i18n("version.empty.launch.goto_download")); gotoDownload.getStyleClass().add("dialog-accept"); gotoDownload.setOnAction(e -> Controllers.navigate(Controllers.getDownloadPage())); diff --git a/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java b/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java index 3a1977d00c..115e43d235 100644 --- a/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java +++ b/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java @@ -465,7 +465,7 @@ public void legacyInstanceSettingsMigrationStoresLegacyGameDirectoryPresetAsPare new GameDirectoryEnvironment(localDirectories, userDirectories, presets)) { settings().defaultGameSettingsPresetProperty().set(defaultPresetId); HMCLGameRepository repository = new HMCLGameRepository(gameDirectory); - Path versionRoot = repository.getVersionRoot("1.20.1"); + Path versionRoot = repository.getInstanceRoot("1.20.1"); Files.createDirectories(versionRoot); Files.writeString(versionRoot.resolve("hmclversion.cfg"), """ { @@ -506,7 +506,7 @@ public void startupMigrationSkipsLegacyInstanceSettingsFile(@TempDir Path tempDi settings().defaultGameSettingsPresetProperty().set(defaultPresetId); HMCLGameRepository repository = new HMCLGameRepository(gameDirectory); writeVersionJson(repository, "1.20.1"); - Path versionRoot = repository.getVersionRoot("1.20.1"); + Path versionRoot = repository.getInstanceRoot("1.20.1"); Files.writeString(versionRoot.resolve(LegacyGameSettingsMigrator.LEGACY_INSTANCE_SETTINGS_FILENAME), """ { "usesGlobal": true @@ -774,7 +774,7 @@ public void close() throws ReflectiveOperationException { /// Writes a minimal valid version json for repository refresh tests. private static void writeVersionJson(HMCLGameRepository repository, String id) throws IOException { - Path versionRoot = repository.getVersionRoot(id); + Path versionRoot = repository.getInstanceRoot(id); Files.createDirectories(versionRoot); Files.writeString(versionRoot.resolve(id + ".json"), """ { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java index 2e1b4ec3d4..7fa070c630 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java @@ -180,7 +180,7 @@ public void refresh() throws IOException { localMods.clear(); try { - analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedPreservingPatchesVersion(id), null); + analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedPreservingPatchesManifest(id), null); } catch (NoSuchGameInstanceException e) { throw new IOException(e); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java index d571f6422c..c804bdd81a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java @@ -277,7 +277,7 @@ public PackMcMeta.PackVersion getRequiredVersion() { if (requiredVersion == null) { lock.lock(); try { - if (requiredVersion == null) requiredVersion = getPackVersion(getMinecraftVersion(), repository.getVersionJar(id)); + if (requiredVersion == null) requiredVersion = getPackVersion(getMinecraftVersion(), repository.getInstanceJar(id)); } finally { lock.unlock(); } 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 a2c14ea0a2..9335a48925 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -82,7 +82,7 @@ public GameBuilder gameBuilder() { public Task checkGameCompletionAsync(GameInstanceManifest version, boolean integrityCheck) { return Task.allOf( Task.composeAsync(() -> { - Path versionJar = repository.getVersionJar(version); + Path versionJar = repository.getInstanceJar(version); return Files.notExists(versionJar) || FileUtils.size(versionJar) == 0L ? new GameDownloadTask(this, null, version) @@ -107,7 +107,7 @@ public Task checkPatchCompletionAsync(GameInstanceManifest version, boolean i String gameVersion = repository.getGameVersion(version).orElse(null); if (gameVersion == null) return null; - GameInstanceManifest original = repository.getVersion(version.getId()); + GameInstanceManifest original = repository.getInstanceManifest(version.getId()); GameInstanceManifest resolved = original.resolvePreservingPatches(repository); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java index 5c4d0ee75b..e1ac56b747 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java @@ -62,7 +62,7 @@ public Task buildAsync() { return libraryTask.thenComposeAsync(dependencyManager.getGameRepository()::saveAsync).whenComplete(exception -> { if (exception != null) - dependencyManager.getGameRepository().removeVersionFromDisk(name); + dependencyManager.getGameRepository().removeInstanceFromDisk(name); }).withStagesHints(hints); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java index b6b389ac6e..21fe758743 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java @@ -409,8 +409,8 @@ public void execute() throws Exception { } vars.put("SIDE", "client"); - vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getVersionJar(version))); - vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getVersionJar(version))); + vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(version))); + vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(version))); vars.put("ROOT", FileUtils.getAbsolutePath(gameRepository.getBaseDirectory())); vars.put("INSTALLER", installer.toAbsolutePath().toString()); vars.put("LIBRARY_DIR", FileUtils.getAbsolutePath(gameRepository.getLibrariesDirectory(version))); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java index e16ffa963c..22a3239a9e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java @@ -53,7 +53,7 @@ public Collection> getDependencies() { @Override public void execute() { - Path jar = dependencyManager.getGameRepository().getVersionJar(version); + Path jar = dependencyManager.getGameRepository().getInstanceJar(version); var task = new FileDownloadTask( dependencyManager.getDownloadProvider().injectURLWithCandidates(version.getDownloadInfo().getUrl()), diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java index cc52d93714..7c606b8e4d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java @@ -58,7 +58,7 @@ public Collection> getDependencies() { @Override public void execute() throws IOException { - Path jar = dependencyManager.getGameRepository().getVersionJar(version); + Path jar = dependencyManager.getGameRepository().getInstanceJar(version); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version, gameVersion); if (Files.exists(jar) && GameVersionNumber.compare(gameVersion, "1.6") < 0 && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java index 4497ab72d7..5590f536bb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java @@ -387,8 +387,8 @@ public void execute() throws Exception { } vars.put("SIDE", "client"); - vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getVersionJar(version))); - vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getVersionJar(version))); + vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(version))); + vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(version))); vars.put("ROOT", FileUtils.getAbsolutePath(gameRepository.getBaseDirectory())); vars.put("INSTALLER", installer.toAbsolutePath().toString()); vars.put("LIBRARY_DIR", FileUtils.getAbsolutePath(gameRepository.getLibrariesDirectory(version))); 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 f422749add..8cf1eb7f40 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 @@ -147,7 +147,7 @@ public void execute() throws Exception { "-cp", dest.toString(), "optifine.Patcher", - gameRepository.getVersionJar(version).toAbsolutePath().normalize().toString(), + gameRepository.getInstanceJar(version).toAbsolutePath().normalize().toString(), dest.toString(), optiFineLibraryPath.toString() }; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index b1bede3b0e..0a61e842f1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -389,7 +389,7 @@ public boolean renameInstance(GameInstanceID from, GameInstanceID to) { } } - public boolean removeVersionFromDisk(String id) { + public boolean removeInstanceFromDisk(String id) { try { return removeInstanceFromDisk(new GameInstanceID(id)); } catch (IllegalArgumentException e) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index 6feed89655..efaeb1de1e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -51,7 +51,7 @@ public interface GameRepository { /// /// @param id the instance id string /// @return whether the instance exists - default boolean hasVersion(@Nullable String id) { + default boolean hasInstance(@Nullable String id) { if (id == null) { return false; } @@ -69,7 +69,7 @@ default boolean hasVersion(@Nullable String id) { /// /// @param id the instance id string /// @return the instance manifest - default GameInstanceManifest getVersion(String id) throws NoSuchGameInstanceException { + default GameInstanceManifest getInstanceManifest(String id) throws NoSuchGameInstanceException { return getInstanceManifest(new GameInstanceID(id)); } @@ -83,7 +83,7 @@ default GameInstanceManifest getVersion(String id) throws NoSuchGameInstanceExce /// /// @param id the instance id string /// @return the resolved manifest - default GameInstanceManifest getResolvedVersion(String id) throws NoSuchGameInstanceException { + default GameInstanceManifest getResolvedInstanceManifest(String id) throws NoSuchGameInstanceException { return getResolvedInstanceManifest(new GameInstanceID(id)).manifest(); } @@ -99,51 +99,27 @@ default GameInstanceManifest.Standalone getResolvedPreservingPatchesInstanceMani /// /// @param id the instance id string /// @return the standalone manifest - default GameInstanceManifest getResolvedPreservingPatchesVersion(String id) throws NoSuchGameInstanceException { + default GameInstanceManifest getResolvedPreservingPatchesManifest(String id) throws NoSuchGameInstanceException { return getResolvedPreservingPatchesInstanceManifest(new GameInstanceID(id)).manifest(); } int getInstanceCount(); - /// Returns the instance count. - /// - /// @return the instance count - default int getVersionCount() { - return getInstanceCount(); - } - Collection getInstanceManifests(); - /// Returns all instance manifests. - /// - /// @return all instance manifests - default Collection getVersions() { - return getInstanceManifests(); - } - void refresh(); - /// Refreshes the repository. - default void refreshVersions() { - refresh(); - } - default Task refreshAsync() { return Task.runAsync(this::refresh); } - /// Refreshes the repository asynchronously. - default Task refreshVersionsAsync() { - return refreshAsync(); - } - Path getInstanceRoot(GameInstanceID instanceId); /// Returns the instance root by string id. /// /// @param id the instance id string /// @return the instance root - default Path getVersionRoot(String id) { + default Path getInstanceRoot(String id) { return getInstanceRoot(new GameInstanceID(id)); } @@ -184,13 +160,8 @@ default Path getResourcePackDirectory(String id) { Path getInstanceJar(GameInstanceManifest manifest); - /// Returns the instance jar path. - default Path getVersionJar(GameInstanceManifest manifest) { - return getInstanceJar(manifest); - } - /// Returns the instance jar path by string id. - default Path getVersionJar(String id) { + default Path getInstanceJar(String id) { try { return getInstanceJar(new GameInstanceID(id)); } catch (NoSuchGameInstanceException e) { @@ -220,7 +191,7 @@ default Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstance boolean renameInstance(GameInstanceID from, GameInstanceID to); /// Renames an instance by string id. - default boolean renameVersion(String from, String to) { + default boolean renameInstance(String from, String to) { return renameInstance(new GameInstanceID(from), new GameInstanceID(to)); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 3c596e1ead..eb94aec154 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -158,7 +158,7 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { if (!options.isNoGeneratedJVMArgs()) { appendJvmArgs(res); - res.addDefault("-Dminecraft.client.jar=", FileUtils.getAbsolutePath(repository.getVersionJar(version))); + res.addDefault("-Dminecraft.client.jar=", FileUtils.getAbsolutePath(repository.getInstanceJar(version))); if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { res.addDefault("-Xdock:name=", "Minecraft " + version.getId()); @@ -287,7 +287,7 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { classpath.removeIf(c -> c.contains("2.9.4-nightly-20150209")); } - Path jar = repository.getVersionJar(version); + Path jar = repository.getInstanceJar(version); if (!Files.isRegularFile(jar)) throw new IOException("Minecraft jar does not exist"); classpath.add(FileUtils.getAbsolutePath(jar.toAbsolutePath())); @@ -494,7 +494,7 @@ private boolean isUsingLog4j() { } public Path getLog4jConfigurationFile() { - return repository.getVersionRoot(version.getId()).resolve("log4j2.xml"); + return repository.getInstanceRoot(version.getId()).resolve("log4j2.xml"); } public void extractLog4jConfigurationFile() throws IOException { @@ -539,7 +539,7 @@ protected Map getConfigurations() { pair("${resolution_height}", options.getHeight().toString()), pair("${library_directory}", FileUtils.getAbsolutePath(repository.getLibrariesDirectory(version))), pair("${classpath_separator}", File.pathSeparator), - pair("${primary_jar}", FileUtils.getAbsolutePath(repository.getVersionJar(version))), + pair("${primary_jar}", FileUtils.getAbsolutePath(repository.getInstanceJar(version))), pair("${language}", Locale.getDefault().toLanguageTag()), // defined by HMCL @@ -549,7 +549,7 @@ protected Map getConfigurations() { pair("${libraries_directory}", FileUtils.getAbsolutePath(repository.getLibrariesDirectory(version))), // file_separator is used in -DignoreList pair("${file_separator}", File.separator), - pair("${primary_jar_name}", FileUtils.getName(repository.getVersionJar(version))) + pair("${primary_jar_name}", FileUtils.getName(repository.getInstanceJar(version))) ); } @@ -622,7 +622,7 @@ private Map getEnvVars(Path nativeFolder) { Map env = new LinkedHashMap<>(); env.put("INST_NAME", versionName); env.put("INST_ID", versionName); - env.put("INST_DIR", FileUtils.getAbsolutePath(repository.getVersionRoot(version.getId()))); + env.put("INST_DIR", FileUtils.getAbsolutePath(repository.getInstanceRoot(version.getId()))); env.put("INST_MC_DIR", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId()))); env.put("INST_JAVA", options.getJava().getBinary().toString()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java index 9e101f5d4a..43ca3a9989 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java @@ -55,7 +55,7 @@ public Collection> getDependencies() { @Override public void execute() throws Exception { - FileUtils.copyDirectory(repository.getVersionRoot(id), backupFolder); + FileUtils.copyDirectory(repository.getInstanceRoot(id), backupFolder); } @Override @@ -69,11 +69,11 @@ public void postExecute() throws Exception { // Keep backup game version for further repair. } else { // Restore backup - repository.removeVersionFromDisk(id); + repository.removeInstanceFromDisk(id); - FileUtils.copyDirectory(backupFolder, repository.getVersionRoot(id)); + FileUtils.copyDirectory(backupFolder, repository.getInstanceRoot(id)); - repository.refreshVersionsAsync().start(); + repository.refreshAsync().start(); } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java index 5885be26fe..f8dcf01124 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java @@ -87,7 +87,7 @@ public CurseCompletionTask(DefaultDependencyManager dependencyManager, String ve if (manifest == null) try { - Path manifestFile = repository.getVersionRoot(version).resolve("manifest.json"); + Path manifestFile = repository.getInstanceRoot(version).resolve("manifest.json"); if (Files.exists(manifestFile)) this.manifest = JsonUtils.fromJsonFile(manifestFile, CurseManifest.class); } catch (Exception e) { @@ -112,7 +112,7 @@ public void execute() throws Exception { if (manifest == null) return; - Path root = repository.getVersionRoot(version); + Path root = repository.getInstanceRoot(version); // Because in China, Curse is too difficult to visit, // if failed, ignore it and retry next time. @@ -140,7 +140,7 @@ public void execute() throws Exception { .collect(Collectors.toList())); JsonUtils.writeToJsonFile(root.resolve("manifest.json"), newManifest); - Path versionRoot = repository.getVersionRoot(modManager.getInstanceId()); + Path versionRoot = repository.getInstanceRoot(modManager.getInstanceId()); Path resourcePacksRoot = versionRoot.resolve("resourcepacks"); Path shaderPacksRoot = versionRoot.resolve("shaderpacks"); finished.set(0); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java index b8cf50d8f2..eaeca83d2d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java @@ -78,7 +78,7 @@ public CurseInstallTask(DefaultDependencyManager dependencyManager, Path zipFile this.run = repository.getRunDirectory(name); Path json = repository.getModpackConfiguration(name); - if (repository.hasVersion(name) && Files.notExists(json)) + if (repository.hasInstance(name) && Files.notExists(json)) throw new IllegalArgumentException("Version " + name + " already exists."); GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.minecraft().gameVersion()); @@ -97,7 +97,7 @@ public CurseInstallTask(DefaultDependencyManager dependencyManager, Path zipFile Exception ex = event.getTask().getException(); if (event.isFailed()) { if (!(ex instanceof ModpackCompletionException)) { - repository.removeVersionFromDisk(name); + repository.removeInstanceFromDisk(name); } } }); @@ -171,7 +171,7 @@ public void execute() throws Exception { // CurseForge manifest where fileName is missing. CurseCompletionTask // resolves those file names and writes the enriched manifest to // manifest.json, so read from there when available. - Path oldManifestFile = repository.getVersionRoot(name).resolve("manifest.json"); + Path oldManifestFile = repository.getInstanceRoot(name).resolve("manifest.json"); List oldFiles = config.getManifest().files(); if (Files.exists(oldManifestFile)) { try { @@ -195,7 +195,7 @@ public void execute() throws Exception { } } - Path root = repository.getVersionRoot(name); + Path root = repository.getInstanceRoot(name); Files.createDirectories(root); JsonUtils.writeToJsonFile(root.resolve("manifest.json"), manifest); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java index 730acc2106..82682446a0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java @@ -109,7 +109,7 @@ public CompletableFuture getFuture(TaskCompletableFuture executor) { throw new IOException("Unable to parse server manifest.json from " + manifest.getFileApi(), e); } - Path rootPath = repository.getVersionRoot(version); + Path rootPath = repository.getInstanceRoot(version); Files.createDirectories(rootPath); Map localFiles = manifest.getFiles().stream().collect(Collectors.toMap(Function.identity(), Function.identity())); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java index 1d20256ffc..60396dc654 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java @@ -90,7 +90,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(version) .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(version), gameVersion); // Mcbbs manifest List addons = new ArrayList<>(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java index 3e179e893c..28bcd30f02 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java @@ -61,9 +61,9 @@ public McbbsModpackLocalInstallTask(DefaultDependencyManager dependencyManager, Path run = repository.getRunDirectory(name); Path json = repository.getModpackConfiguration(name); - if (repository.hasVersion(name) && Files.notExists(json)) + if (repository.hasInstance(name) && Files.notExists(json)) throw new IllegalArgumentException("Version " + name + " already exists."); - this.update = repository.hasVersion(name); + this.update = repository.hasInstance(name); GameBuilder builder = dependencyManager.gameBuilder().name(name); @@ -74,7 +74,7 @@ public McbbsModpackLocalInstallTask(DefaultDependencyManager dependencyManager, dependents.add(builder.buildAsync()); onDone().register(event -> { if (event.isFailed()) - repository.removeVersionFromDisk(name); + repository.removeInstanceFromDisk(name); }); ModpackConfiguration config = null; @@ -104,7 +104,7 @@ public List> getDependencies() { @Override public void execute() throws Exception { - GameInstanceManifest version = repository.getVersion(name); + GameInstanceManifest version = repository.getInstanceManifest(name); Optional mcbbsPatch = version.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst(); if (!update) { GameInstancePatch patch = new GameInstancePatch(PATCH_NAME).setLibraries(manifest.getLibraries()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java index bfd6ec23d9..d128fd80a4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java @@ -48,7 +48,7 @@ public McbbsModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, this.manifest = manifest; Path json = repository.getModpackConfiguration(name); - if (repository.hasVersion(name) && Files.notExists(json)) + if (repository.hasInstance(name) && Files.notExists(json)) throw new IllegalArgumentException("Version " + name + " already exists."); GameBuilder builder = dependencyManager.gameBuilder().name(name); @@ -59,7 +59,7 @@ public McbbsModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, dependents.add(builder.buildAsync()); onDone().register(event -> { if (event.isFailed()) - repository.removeVersionFromDisk(name); + repository.removeInstanceFromDisk(name); }); ModpackConfiguration config; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java index 6794a52cf3..0dd24e6636 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java @@ -77,7 +77,7 @@ public ModrinthCompletionTask(DefaultDependencyManager dependencyManager, String if (manifest == null) try { - Path manifestFile = repository.getVersionRoot(version).resolve("modrinth.index.json"); + Path manifestFile = repository.getInstanceRoot(version).resolve("modrinth.index.json"); if (Files.exists(manifestFile)) this.manifest = JsonUtils.fromJsonFile(manifestFile, ModrinthManifest.class); } catch (Exception e) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java index 00e9a0cc56..e09f418e32 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java @@ -64,7 +64,7 @@ public ModrinthInstallTask(DefaultDependencyManager dependencyManager, Path zipF this.run = repository.getRunDirectory(name); Path json = repository.getModpackConfiguration(name); - if (repository.hasVersion(name) && Files.notExists(json)) + if (repository.hasInstance(name) && Files.notExists(json)) throw new IllegalArgumentException("Version " + name + " already exists."); GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion()); @@ -96,7 +96,7 @@ public ModrinthInstallTask(DefaultDependencyManager dependencyManager, Path zipF Exception ex = event.getTask().getException(); if (event.isFailed()) { if (!(ex instanceof ModpackCompletionException)) { - repository.removeVersionFromDisk(name); + repository.removeInstanceFromDisk(name); } } }); @@ -152,7 +152,7 @@ public void execute() throws Exception { } } - Path root = repository.getVersionRoot(name); + Path root = repository.getInstanceRoot(name); Files.createDirectories(root); JsonUtils.writeToJsonFile(root.resolve("modrinth.index.json"), manifest); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java index c589efce48..f4341823e4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java @@ -172,7 +172,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(version) .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(version), gameVersion); Map dependencies = new HashMap<>(); dependencies.put("minecraft", gameVersion); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java index ca0daeebd7..ef47a7dc98 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java @@ -80,7 +80,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(versionId) .orElseThrow(() -> new IOException("Cannot parse the version of " + versionId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(versionId), gameVersion); List components = new ArrayList<>(); components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, MultiMCComponents.getComponent(MINECRAFT), gameVersion)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java index ec9971994b..5a3260fecb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java @@ -94,12 +94,12 @@ public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, Pat this.repository = dependencyManager.getGameRepository(); Path json = repository.getModpackConfiguration(name); - if (repository.hasVersion(name) && Files.notExists(json)) + if (repository.hasInstance(name) && Files.notExists(json)) throw new IllegalArgumentException("Version " + name + " already exists."); onDone().register(event -> { if (event.isFailed()) - repository.removeVersionFromDisk(name); + repository.removeInstanceFromDisk(name); }); } @@ -252,7 +252,7 @@ public void execute() throws Exception { Path libraries = root.resolve("libraries"); if (Files.exists(libraries)) - FileUtils.copyDirectory(libraries, repository.getVersionRoot(name).resolve("libraries")); + FileUtils.copyDirectory(libraries, repository.getInstanceRoot(name).resolve("libraries")); for (Library library : artifact.getVersion().getLibraries()) { if ("local".equals(library.getHint())) { @@ -278,7 +278,7 @@ public void execute() throws Exception { if (iconKey != null) { Path iconFile = root.resolve(iconKey + ".png"); if (Files.exists(iconFile)) { - FileUtils.copyFile(iconFile, repository.getVersionRoot(name).resolve("icon.png")); + FileUtils.copyFile(iconFile, repository.getInstanceRoot(name).resolve("icon.png")); } } } @@ -338,7 +338,7 @@ public void postExecute() throws Exception { Path root = getRootPath(fs).resolve("jarmods"); try (FileSystem mc = CompressingUtils.writable( - repository.getVersionRoot(name).resolve(name + ".jar") + repository.getInstanceRoot(name).resolve(name + ".jar") ).setAutoDetectEncoding(true).build()) { for (String fileName : files) { try (FileSystem jm = CompressingUtils.readonly(root.resolve(fileName)).setAutoDetectEncoding(true).build()) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java index ab06e48e38..ce16275012 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java @@ -120,7 +120,7 @@ public void execute() throws Exception { dependencies.add(builder.buildAsync()); } - Path rootPath = repository.getVersionRoot(version).toAbsolutePath().normalize(); + Path rootPath = repository.getInstanceRoot(version).toAbsolutePath().normalize(); Map files = manifest.getManifest().getFiles().stream() .collect(Collectors.toMap(ModpackConfiguration.FileInformation::getPath, Function.identity())); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java index e943675e0f..1231782f8d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java @@ -86,7 +86,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(versionId) .orElseThrow(() -> new IOException("Cannot parse the version of " + versionId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(versionId), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(versionId), gameVersion); List addons = new ArrayList<>(); addons.add(new ServerModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion)); analyzer.getVersion(FORGE).ifPresent(forgeVersion -> diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java index 5a1d382a68..918f4acfe6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java @@ -54,7 +54,7 @@ public ServerModpackLocalInstallTask(DefaultDependencyManager dependencyManager, Path run = repository.getRunDirectory(name); Path json = repository.getModpackConfiguration(name); - if (repository.hasVersion(name) && Files.notExists(json)) + if (repository.hasInstance(name) && Files.notExists(json)) throw new IllegalArgumentException("Version " + name + " already exists."); GameBuilder builder = dependencyManager.gameBuilder().name(name); @@ -65,7 +65,7 @@ public ServerModpackLocalInstallTask(DefaultDependencyManager dependencyManager, dependents.add(builder.buildAsync()); onDone().register(event -> { if (event.isFailed()) - repository.removeVersionFromDisk(name); + repository.removeInstanceFromDisk(name); }); ModpackConfiguration config = null; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java index ecff6c17ac..7f75a46019 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java @@ -48,7 +48,7 @@ public ServerModpackRemoteInstallTask(DefaultDependencyManager dependencyManager this.manifest = manifest; Path json = repository.getModpackConfiguration(name); - if (repository.hasVersion(name) && Files.notExists(json)) + if (repository.hasInstance(name) && Files.notExists(json)) throw new IllegalArgumentException("Version " + name + " already exists."); GameBuilder builder = dependencyManager.gameBuilder().name(name); @@ -59,7 +59,7 @@ public ServerModpackRemoteInstallTask(DefaultDependencyManager dependencyManager dependents.add(builder.buildAsync()); onDone().register(event -> { if (event.isFailed()) - repository.removeVersionFromDisk(name); + repository.removeInstanceFromDisk(name); }); ModpackConfiguration config; From 38f5861ee3e20d923a0b16b9ac37647bbd3986a4 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 12:08:30 +0800 Subject: [PATCH 26/85] refactor: add detailed Javadoc comments to GameRepository interface methods --- .../jackhuang/hmcl/game/GameRepository.java | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index efaeb1de1e..a4e1253d34 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -31,6 +31,10 @@ import java.util.Optional; import java.util.Set; +/// Provides indexed access to local game instances and the filesystem layout used by those instances. +/// +/// Implementations are responsible for loading instance manifests, resolving inheritance and patches, +/// locating instance-owned files, and exposing helper paths used by launch, download, and maintenance code. @NotNullByDefault public interface GameRepository { /// Resolves inheritance and pending patches into a launch-ready manifest view. @@ -45,6 +49,10 @@ public interface GameRepository { /// @return the standalone manifest view GameInstanceManifest.Standalone resolvePreservingPatches(GameInstanceManifest manifest) throws NoSuchGameInstanceException; + /// Returns whether the instance exists in the current repository index. + /// + /// @param instanceId the instance id + /// @return whether the instance exists boolean hasInstance(GameInstanceID instanceId); /// Returns whether the instance exists. @@ -63,6 +71,11 @@ default boolean hasInstance(@Nullable String id) { } } + /// Returns the stored manifest for an instance without resolving inheritance or patches. + /// + /// @param instanceId the instance id + /// @return the stored instance manifest + /// @throws NoSuchGameInstanceException if the instance is not loaded in this repository GameInstanceManifest getInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; /// Returns an instance manifest by string id. @@ -103,16 +116,30 @@ default GameInstanceManifest getResolvedPreservingPatchesManifest(String id) thr return getResolvedPreservingPatchesInstanceManifest(new GameInstanceID(id)).manifest(); } + /// Returns the number of loaded instances. + /// + /// @return the loaded instance count int getInstanceCount(); + /// Returns the stored manifests for all loaded instances. + /// + /// @return the loaded instance manifests Collection getInstanceManifests(); + /// Reloads repository state from the backing storage. void refresh(); + /// Creates a task that reloads repository state from the backing storage. + /// + /// @return a task that calls [#refresh()] default Task refreshAsync() { return Task.runAsync(this::refresh); } + /// Returns the directory that stores files belonging to an instance. + /// + /// @param instanceId the instance id + /// @return the instance root directory Path getInstanceRoot(GameInstanceID instanceId); /// Returns the instance root by string id. @@ -123,6 +150,10 @@ default Path getInstanceRoot(String id) { return getInstanceRoot(new GameInstanceID(id)); } + /// Returns the working directory used when launching an instance. + /// + /// @param instanceId the instance id + /// @return the run directory Path getRunDirectory(GameInstanceID instanceId); /// Returns the run directory by string id. @@ -133,34 +164,74 @@ default Path getRunDirectory(String id) { return getRunDirectory(new GameInstanceID(id)); } + /// Returns the base directory used to store shared libraries for a manifest. + /// + /// @param manifest the manifest whose libraries are being resolved + /// @return the libraries directory Path getLibrariesDirectory(GameInstanceManifest manifest); + /// Returns the expected filesystem path for a library. + /// + /// @param manifest the manifest that owns or references the library + /// @param lib the library descriptor + /// @return the library file path Path getLibraryFile(GameInstanceManifest manifest, Library lib); + /// Returns the directory used for extracted native libraries of an instance and platform. + /// + /// @param instanceId the instance id + /// @param platform the target platform + /// @return the native library directory Path getNativeDirectory(GameInstanceID instanceId, Platform platform); /// Returns the native directory by string id. + /// + /// @param id the instance id string + /// @param platform the target platform + /// @return the native library directory default Path getNativeDirectory(String id, Platform platform) { return getNativeDirectory(new GameInstanceID(id), platform); } + /// Returns the mods directory for an instance. + /// + /// @param instanceId the instance id + /// @return the mods directory Path getModsDirectory(GameInstanceID instanceId); /// Returns the mods directory by string id. + /// + /// @param id the instance id string + /// @return the mods directory default Path getModsDirectory(String id) { return getModsDirectory(new GameInstanceID(id)); } + /// Returns the resource pack directory for an instance. + /// + /// @param instanceId the instance id + /// @return the resource pack directory Path getResourcePackDirectory(GameInstanceID instanceId); /// Returns the resource pack directory by string id. + /// + /// @param id the instance id string + /// @return the resource pack directory default Path getResourcePackDirectory(String id) { return getResourcePackDirectory(new GameInstanceID(id)); } + /// Returns the primary client jar path for a manifest. + /// + /// @param manifest the manifest whose jar should be located + /// @return the primary client jar path Path getInstanceJar(GameInstanceManifest manifest); /// Returns the instance jar path by string id. + /// + /// @param id the instance id string + /// @return the primary client jar path + /// @throws IllegalArgumentException if the instance id is invalid or the instance is not loaded default Path getInstanceJar(String id) { try { return getInstanceJar(new GameInstanceID(id)); @@ -169,13 +240,25 @@ default Path getInstanceJar(String id) { } } + /// Detects the Minecraft game version associated with a manifest. + /// + /// @param manifest the manifest to inspect + /// @return the detected Minecraft game version, or empty if it cannot be determined Optional getGameVersion(GameInstanceManifest manifest); + /// Detects the Minecraft game version associated with an instance. + /// + /// @param instanceId the instance id + /// @return the detected Minecraft game version, or empty if it cannot be determined + /// @throws NoSuchGameInstanceException if the instance is not loaded in this repository default Optional getGameVersion(GameInstanceID instanceId) throws NoSuchGameInstanceException { return getGameVersion(getInstanceManifest(instanceId)); } /// Returns the detected game version by string id. + /// + /// @param id the instance id string + /// @return the detected Minecraft game version, or empty if the id is invalid, the instance is missing, or detection fails default Optional getGameVersion(String id) { try { return getGameVersion(new GameInstanceID(id)); @@ -184,10 +267,20 @@ default Optional getGameVersion(String id) { } } + /// Returns the primary client jar path for an instance. + /// + /// @param instanceId the instance id + /// @return the primary client jar path + /// @throws NoSuchGameInstanceException if the instance is not loaded in this repository default Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceException { return getInstanceJar(getResolvedInstanceManifest(instanceId).manifest()); } + /// Renames an instance and updates repository-managed references. + /// + /// @param from the current instance id + /// @param to the target instance id + /// @return whether the instance was renamed boolean renameInstance(GameInstanceID from, GameInstanceID to); /// Renames an instance by string id. @@ -195,55 +288,132 @@ default boolean renameInstance(String from, String to) { return renameInstance(new GameInstanceID(from), new GameInstanceID(to)); } + /// Returns the asset directory that should be used at launch time. + /// + /// @param instanceId the instance id + /// @param assetId the asset index id + /// @return the actual asset directory Path getActualAssetDirectory(GameInstanceID instanceId, String assetId); /// Returns the actual asset directory by string id. + /// + /// @param id the instance id string + /// @param assetId the asset index id + /// @return the actual asset directory default Path getActualAssetDirectory(String id, String assetId) { return getActualAssetDirectory(new GameInstanceID(id), assetId); } + /// Returns the base asset storage directory for an instance. + /// + /// @param instanceId the instance id + /// @param assetId the asset index id + /// @return the asset storage directory Path getAssetDirectory(GameInstanceID instanceId, String assetId); /// Returns the asset directory by string id. + /// + /// @param id the instance id string + /// @param assetId the asset index id + /// @return the asset storage directory default Path getAssetDirectory(String id, String assetId) { return getAssetDirectory(new GameInstanceID(id), assetId); } + /// Returns an existing asset object path by logical asset name. + /// + /// @param instanceId the instance id + /// @param assetId the asset index id + /// @param name the logical asset name + /// @return the asset object path, or empty if the object is not present in the asset index + /// @throws IOException if the asset index cannot be read Optional getAssetObject(GameInstanceID instanceId, String assetId, String name) throws IOException; /// Returns an asset object by string id. + /// + /// @param id the instance id string + /// @param assetId the asset index id + /// @param name the logical asset name + /// @return the asset object path, or empty if the object is not present in the asset index + /// @throws IOException if the asset index cannot be read default Optional getAssetObject(String id, String assetId, String name) throws IOException { return getAssetObject(new GameInstanceID(id), assetId, name); } + /// Returns the expected path for an asset object descriptor. + /// + /// @param instanceId the instance id + /// @param assetId the asset index id + /// @param obj the asset object descriptor + /// @return the asset object path Path getAssetObject(GameInstanceID instanceId, String assetId, AssetObject obj); /// Returns an asset object by string id. + /// + /// @param id the instance id string + /// @param assetId the asset index id + /// @param obj the asset object descriptor + /// @return the asset object path default Path getAssetObject(String id, String assetId, AssetObject obj) { return getAssetObject(new GameInstanceID(id), assetId, obj); } + /// Reads an asset index. + /// + /// @param instanceId the instance id + /// @param assetId the asset index id + /// @return the asset index + /// @throws IOException if the asset index cannot be read AssetIndex getAssetIndex(GameInstanceID instanceId, String assetId) throws IOException; /// Returns an asset index by string id. + /// + /// @param id the instance id string + /// @param assetId the asset index id + /// @return the asset index + /// @throws IOException if the asset index cannot be read default AssetIndex getAssetIndex(String id, String assetId) throws IOException { return getAssetIndex(new GameInstanceID(id), assetId); } + /// Returns the path of an asset index file. + /// + /// @param instanceId the instance id + /// @param assetId the asset index id + /// @return the asset index file path Path getIndexFile(GameInstanceID instanceId, String assetId); /// Returns an asset index path by string id. + /// + /// @param id the instance id string + /// @param assetId the asset index id + /// @return the asset index file path default Path getIndexFile(String id, String assetId) { return getIndexFile(new GameInstanceID(id), assetId); } + /// Returns the path of a logging configuration object. + /// + /// @param instanceId the instance id + /// @param assetId the asset index id used as the logging object namespace + /// @param loggingInfo the logging configuration descriptor + /// @return the logging object path Path getLoggingObject(GameInstanceID instanceId, String assetId, LoggingInfo loggingInfo); /// Returns a logging object path by string id. + /// + /// @param id the instance id string + /// @param assetId the asset index id used as the logging object namespace + /// @param loggingInfo the logging configuration descriptor + /// @return the logging object path default Path getLoggingObject(String id, String assetId, LoggingInfo loggingInfo) { return getLoggingObject(new GameInstanceID(id), assetId, loggingInfo); } + /// Returns the classpath entries whose library files are present on disk. + /// + /// @param manifest the manifest whose libraries should be mapped to classpath entries + /// @return absolute classpath entries for existing non-native libraries default Set getClasspath(GameInstanceManifest manifest) { Set classpath = new LinkedHashSet<>(); if (manifest.libraries() != null) { From b1bcd7686bf4a9063f4cb71c94dd92bb872447f8 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 12:10:14 +0800 Subject: [PATCH 27/85] refactor: rename version references to manifest in GameAssetDownloadTask and GameAssetIndexDownloadTask --- .../download/game/GameAssetDownloadTask.java | 16 ++++++++-------- .../game/GameAssetIndexDownloadTask.java | 12 ++++++------ .../jackhuang/hmcl/game/GameRepository.java | 19 ------------------- 3 files changed, 14 insertions(+), 33 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java index 3210634231..426f1973e8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java @@ -42,7 +42,7 @@ public final class GameAssetDownloadTask extends Task { private final AbstractDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final AssetIndexInfo assetIndexInfo; private final Path assetIndexFile; private final boolean integrityCheck; @@ -53,17 +53,17 @@ public final class GameAssetDownloadTask extends Task { * Constructor. * * @param dependencyManager the dependency manager that can provides {@link GameRepository} - * @param version the game version + * @param manifest the game version */ - public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean forceDownloadingIndex, boolean integrityCheck) { + public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, GameInstanceManifest manifest, boolean forceDownloadingIndex, boolean integrityCheck) { this.dependencyManager = dependencyManager; - this.version = version.resolve(dependencyManager.getGameRepository()); - this.assetIndexInfo = this.version.getAssetIndex(); - this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId()); + this.manifest = manifest.resolve(dependencyManager.getGameRepository()); + this.assetIndexInfo = this.manifest.getAssetIndex(); + this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(manifest.id(), assetIndexInfo.getId()); this.integrityCheck = integrityCheck; setStage("hmcl.install.assets"); - dependents.add(new GameAssetIndexDownloadTask(dependencyManager, this.version, forceDownloadingIndex)); + dependents.add(new GameAssetIndexDownloadTask(dependencyManager, this.manifest, forceDownloadingIndex)); } @Override @@ -90,7 +90,7 @@ public void execute() throws Exception { if (isCancelled()) throw new InterruptedException(); - Path file = dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject); + Path file = dependencyManager.getGameRepository().getAssetObject(manifest.getId(), assetIndexInfo.getId(), assetObject); boolean download = !Files.isRegularFile(file); try { if (!download && integrityCheck && !assetObject.validateChecksum(file, true)) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java index 572c0735cd..110e99b72e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java @@ -45,7 +45,7 @@ public final class GameAssetIndexDownloadTask extends Task { private final AbstractDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final boolean forceDownloading; private final List> dependencies = new ArrayList<>(1); @@ -53,11 +53,11 @@ public final class GameAssetIndexDownloadTask extends Task { * Constructor. * * @param dependencyManager the dependency manager that can provides {@link GameRepository} - * @param version the resolved version + * @param manifest the resolved version */ - public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean forceDownloading) { + public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, GameInstanceManifest manifest, boolean forceDownloading) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.forceDownloading = forceDownloading; setSignificance(TaskSignificance.MODERATE); } @@ -69,8 +69,8 @@ public List> getDependencies() { @Override public void execute() { - AssetIndexInfo assetIndexInfo = version.getAssetIndex(); - Path assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId()); + AssetIndexInfo assetIndexInfo = manifest.getAssetIndex(); + Path assetIndexFile = dependencyManager.getGameRepository().getIndexFile(manifest.id(), assetIndexInfo.getId()); boolean verifyHashCode = StringUtils.isNotBlank(assetIndexInfo.getSha1()) && assetIndexInfo.getUrl().contains(assetIndexInfo.getSha1()); if (Files.exists(assetIndexFile) && !forceDownloading) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index a4e1253d34..c2b497be34 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -383,15 +383,6 @@ default AssetIndex getAssetIndex(String id, String assetId) throws IOException { /// @return the asset index file path Path getIndexFile(GameInstanceID instanceId, String assetId); - /// Returns an asset index path by string id. - /// - /// @param id the instance id string - /// @param assetId the asset index id - /// @return the asset index file path - default Path getIndexFile(String id, String assetId) { - return getIndexFile(new GameInstanceID(id), assetId); - } - /// Returns the path of a logging configuration object. /// /// @param instanceId the instance id @@ -400,16 +391,6 @@ default Path getIndexFile(String id, String assetId) { /// @return the logging object path Path getLoggingObject(GameInstanceID instanceId, String assetId, LoggingInfo loggingInfo); - /// Returns a logging object path by string id. - /// - /// @param id the instance id string - /// @param assetId the asset index id used as the logging object namespace - /// @param loggingInfo the logging configuration descriptor - /// @return the logging object path - default Path getLoggingObject(String id, String assetId, LoggingInfo loggingInfo) { - return getLoggingObject(new GameInstanceID(id), assetId, loggingInfo); - } - /// Returns the classpath entries whose library files are present on disk. /// /// @param manifest the manifest whose libraries should be mapped to classpath entries From 64947ee3c1104f630b2fbb3329e2e6e4259e7840 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 12:11:26 +0800 Subject: [PATCH 28/85] refactor: replace version references with manifest in DefaultLauncher, GameAssetDownloadTask, GameRepository, HMCLGameLauncher, and Launcher --- .../jackhuang/hmcl/game/HMCLGameLauncher.java | 8 +-- .../download/game/GameAssetDownloadTask.java | 2 +- .../jackhuang/hmcl/game/GameRepository.java | 49 ------------- .../hmcl/launch/DefaultLauncher.java | 70 +++++++++---------- .../org/jackhuang/hmcl/launch/Launcher.java | 14 ++-- 5 files changed, 47 insertions(+), 96 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java index 5022678188..4bc4b24b96 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java @@ -66,7 +66,7 @@ private void generateOptionsTxt() { if (options.isDisableAutoGameOptions()) return; - Path runDir = repository.getRunDirectory(version.getId()); + Path runDir = repository.getRunDirectory(manifest.getId()); Path optionsFile = runDir.resolve("options.txt"); Path configFolder = runDir.resolve("config"); @@ -91,7 +91,7 @@ private void generateOptionsTxt() { * 1.11 ~ 1.12 : zh_cn works fine, zh_CN will display Chinese but the language setting will incorrectly show English as selected * 1.13+ : zh_cn works fine, zh_CN will automatically switch to English */ - GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(repository.getGameVersion(version)); + GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(repository.getGameVersion(manifest)); if (gameVersion.compareTo("1.1") < 0) return; @@ -161,7 +161,7 @@ protected void appendJvmArgs(CommandBuilder result) { if (options.isAllowAutoAgent() && !options.isNoGeneratedJVMArgs() && !options.isNoGeneratedOptimizingJVMArgs() - && NativePatcher.needPatchMemoryUtil(version, options.getJava().getParsedVersion())) { + && NativePatcher.needPatchMemoryUtil(manifest, options.getJava().getParsedVersion())) { LOG.info("Attempting to patch game with lwjgl-unsafe-agent"); try { result.add("-javaagent:" + extractLwjglUnsafeAgent()); @@ -180,7 +180,7 @@ private Path extractLwjglUnsafeAgent() throws IOException { Library library = new Library(new Artifact("org.glavo", "lwjgl-unsafe-agent", agentVersion)); String fileName = library.getArtifact().getFileName(); - Path agentPath = repository.getLibraryFile(version, library).toAbsolutePath().normalize(); + Path agentPath = repository.getLibraryFile(manifest, library).toAbsolutePath().normalize(); if (agentPath.toString().contains("=")) { throw new IOException("Invalid library path: " + agentPath); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java index 426f1973e8..8d87159a23 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java @@ -90,7 +90,7 @@ public void execute() throws Exception { if (isCancelled()) throw new InterruptedException(); - Path file = dependencyManager.getGameRepository().getAssetObject(manifest.getId(), assetIndexInfo.getId(), assetObject); + Path file = dependencyManager.getGameRepository().getAssetObject(manifest.id(), assetIndexInfo.getId(), assetObject); boolean download = !Files.isRegularFile(file); try { if (!download && integrityCheck && !assetObject.validateChecksum(file, true)) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index c2b497be34..a4670e38ad 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -295,15 +295,6 @@ default boolean renameInstance(String from, String to) { /// @return the actual asset directory Path getActualAssetDirectory(GameInstanceID instanceId, String assetId); - /// Returns the actual asset directory by string id. - /// - /// @param id the instance id string - /// @param assetId the asset index id - /// @return the actual asset directory - default Path getActualAssetDirectory(String id, String assetId) { - return getActualAssetDirectory(new GameInstanceID(id), assetId); - } - /// Returns the base asset storage directory for an instance. /// /// @param instanceId the instance id @@ -311,15 +302,6 @@ default Path getActualAssetDirectory(String id, String assetId) { /// @return the asset storage directory Path getAssetDirectory(GameInstanceID instanceId, String assetId); - /// Returns the asset directory by string id. - /// - /// @param id the instance id string - /// @param assetId the asset index id - /// @return the asset storage directory - default Path getAssetDirectory(String id, String assetId) { - return getAssetDirectory(new GameInstanceID(id), assetId); - } - /// Returns an existing asset object path by logical asset name. /// /// @param instanceId the instance id @@ -329,17 +311,6 @@ default Path getAssetDirectory(String id, String assetId) { /// @throws IOException if the asset index cannot be read Optional getAssetObject(GameInstanceID instanceId, String assetId, String name) throws IOException; - /// Returns an asset object by string id. - /// - /// @param id the instance id string - /// @param assetId the asset index id - /// @param name the logical asset name - /// @return the asset object path, or empty if the object is not present in the asset index - /// @throws IOException if the asset index cannot be read - default Optional getAssetObject(String id, String assetId, String name) throws IOException { - return getAssetObject(new GameInstanceID(id), assetId, name); - } - /// Returns the expected path for an asset object descriptor. /// /// @param instanceId the instance id @@ -348,16 +319,6 @@ default Optional getAssetObject(String id, String assetId, String name) th /// @return the asset object path Path getAssetObject(GameInstanceID instanceId, String assetId, AssetObject obj); - /// Returns an asset object by string id. - /// - /// @param id the instance id string - /// @param assetId the asset index id - /// @param obj the asset object descriptor - /// @return the asset object path - default Path getAssetObject(String id, String assetId, AssetObject obj) { - return getAssetObject(new GameInstanceID(id), assetId, obj); - } - /// Reads an asset index. /// /// @param instanceId the instance id @@ -366,16 +327,6 @@ default Path getAssetObject(String id, String assetId, AssetObject obj) { /// @throws IOException if the asset index cannot be read AssetIndex getAssetIndex(GameInstanceID instanceId, String assetId) throws IOException; - /// Returns an asset index by string id. - /// - /// @param id the instance id string - /// @param assetId the asset index id - /// @return the asset index - /// @throws IOException if the asset index cannot be read - default AssetIndex getAssetIndex(String id, String assetId) throws IOException { - return getAssetIndex(new GameInstanceID(id), assetId); - } - /// Returns the path of an asset index file. /// /// @param instanceId the instance id diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index eb94aec154..af61cedd28 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -158,11 +158,11 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { if (!options.isNoGeneratedJVMArgs()) { appendJvmArgs(res); - res.addDefault("-Dminecraft.client.jar=", FileUtils.getAbsolutePath(repository.getInstanceJar(version))); + res.addDefault("-Dminecraft.client.jar=", FileUtils.getAbsolutePath(repository.getInstanceJar(manifest))); if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { - res.addDefault("-Xdock:name=", "Minecraft " + version.getId()); - repository.getAssetObject(version.getId(), version.getAssetIndex().getId(), "icons/minecraft.icns") + res.addDefault("-Xdock:name=", "Minecraft " + manifest.id()); + repository.getAssetObject(manifest.id(), manifest.getAssetIndex().getId(), "icons/minecraft.icns") .ifPresent(minecraftIcns -> { res.addDefault("-Xdock:icon=", FileUtils.getAbsolutePath(minecraftIcns)); }); @@ -281,25 +281,25 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { } } - Set classpath = repository.getClasspath(version); + Set classpath = repository.getClasspath(manifest); if (analyzer.has(LibraryAnalyzer.LibraryType.CLEANROOM)) { classpath.removeIf(c -> c.contains("2.9.4-nightly-20150209")); } - Path jar = repository.getInstanceJar(version); + Path jar = repository.getInstanceJar(manifest); if (!Files.isRegularFile(jar)) throw new IOException("Minecraft jar does not exist"); classpath.add(FileUtils.getAbsolutePath(jar.toAbsolutePath())); // Provided Minecraft arguments - Path gameAssets = repository.getActualAssetDirectory(version.getId(), version.getAssetIndex().getId()); + Path gameAssets = repository.getActualAssetDirectory(manifest.id(), manifest.getAssetIndex().getId()); Map configuration = getConfigurations(); configuration.put("${classpath}", String.join(File.pathSeparator, classpath)); configuration.put("${game_assets}", FileUtils.getAbsolutePath(gameAssets)); configuration.put("${assets_root}", FileUtils.getAbsolutePath(gameAssets)); - Optional gameVersion = repository.getGameVersion(version); + Optional gameVersion = repository.getGameVersion(manifest); // lwjgl assumes path to native libraries encoded by ASCII. // Here is a workaround for this issue: https://github.com/HMCL-dev/HMCL/issues/1141. @@ -314,7 +314,7 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { configuration.put("${natives_directory}", nativeFolderPath); Path javaNativeFolder = FileUtils.toAbsolute(nativeFolder); - @Nullable List jvmArguments = version.getArguments().map(Arguments::jvm).orElse(null); + @Nullable List jvmArguments = manifest.getArguments().map(Arguments::jvm).orElse(null); if (jvmArguments != null) { for (Argument jvmArgument : jvmArguments) { @@ -351,17 +351,17 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { res.add("-javaagent:" + javaAgent); } - if (version.getMainClass() == null) { - throw new IllegalStateException("Main class is null for instance " + version.getId()); + if (manifest.getMainClass() == null) { + throw new IllegalStateException("Main class is null for instance " + manifest.getId()); } - res.add(version.getMainClass()); + res.add(manifest.getMainClass()); - res.addAll(Arguments.parseStringArguments(version.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(ArrayList::new), configuration)); + res.addAll(Arguments.parseStringArguments(manifest.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(ArrayList::new), configuration)); Map features = getFeatures(); - version.getArguments().map(Arguments::game).ifPresent(arguments -> res.addAll(Arguments.parseArguments(arguments, configuration, features))); - if (version.getMinecraftArguments().isPresent()) { + manifest.getArguments().map(Arguments::game).ifPresent(arguments -> res.addAll(Arguments.parseArguments(arguments, configuration, features))); + if (manifest.getMinecraftArguments().isPresent()) { res.addAll(Arguments.parseArguments(this.getDefaultGameArguments(), configuration, features)); } if (argumentsFromAuthInfo != null && argumentsFromAuthInfo.game() != null && !argumentsFromAuthInfo.game().isEmpty()) @@ -461,9 +461,9 @@ public void decompressNatives(Path destination) throws NotDecompressingNativesEx try { FileUtils.cleanDirectoryQuietly(destination); - for (Library library : version.getLibraries()) + for (Library library : manifest.getLibraries()) if (library.isNative()) - new Unzipper(repository.getLibraryFile(version, library), destination) + new Unzipper(repository.getLibraryFile(manifest, library), destination) .setFilter((zipEntry, destFile, relativePath) -> { if (!zipEntry.isDirectory() && !zipEntry.isUnixSymlink() && Files.isRegularFile(destFile) @@ -490,11 +490,11 @@ public void decompressNatives(Path destination) throws NotDecompressingNativesEx } private boolean isUsingLog4j() { - return GameVersionNumber.compare(repository.getGameVersion(version).orElse("1.7"), "1.7") >= 0; + return GameVersionNumber.compare(repository.getGameVersion(manifest).orElse("1.7"), "1.7") >= 0; } public Path getLog4jConfigurationFile() { - return repository.getInstanceRoot(version.getId()).resolve("log4j2.xml"); + return repository.getInstanceRoot(manifest.getId()).resolve("log4j2.xml"); } public void extractLog4jConfigurationFile() throws IOException { @@ -502,7 +502,7 @@ public void extractLog4jConfigurationFile() throws IOException { String sourcePath; - if (GameVersionNumber.asGameVersion(repository.getGameVersion(version)).compareTo("1.12") < 0) { + if (GameVersionNumber.asGameVersion(repository.getGameVersion(manifest)).compareTo("1.12") < 0) { if (options.isEnableDebugLogOutput()) { sourcePath = "/assets/game/log4j2-1.7-debug.xml"; } else { @@ -528,35 +528,35 @@ protected Map getConfigurations() { pair("${auth_session}", authInfo.getAccessToken()), pair("${auth_access_token}", authInfo.getAccessToken()), pair("${auth_uuid}", UUIDs.toCompactString(authInfo.getUUID())), - pair("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(version.getId())), + pair("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(manifest.getId())), pair("${profile_name}", Optional.ofNullable(options.getProfileName()).orElse("Minecraft")), - pair("${version_type}", Optional.ofNullable(options.getVersionType()).orElse(version.getType().getId())), - pair("${game_directory}", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId()))), + pair("${version_type}", Optional.ofNullable(options.getVersionType()).orElse(manifest.getType().getId())), + pair("${game_directory}", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId()))), pair("${user_type}", authInfo.getUserType()), - pair("${assets_index_name}", version.getAssetIndex().getId()), + pair("${assets_index_name}", manifest.getAssetIndex().getId()), pair("${user_properties}", authInfo.getUserProperties()), pair("${resolution_width}", options.getWidth().toString()), pair("${resolution_height}", options.getHeight().toString()), - pair("${library_directory}", FileUtils.getAbsolutePath(repository.getLibrariesDirectory(version))), + pair("${library_directory}", FileUtils.getAbsolutePath(repository.getLibrariesDirectory(manifest))), pair("${classpath_separator}", File.pathSeparator), - pair("${primary_jar}", FileUtils.getAbsolutePath(repository.getInstanceJar(version))), + pair("${primary_jar}", FileUtils.getAbsolutePath(repository.getInstanceJar(manifest))), pair("${language}", Locale.getDefault().toLanguageTag()), // defined by HMCL // libraries_directory stands for historical reasons here. We don't know the official launcher // had already defined "library_directory" as the placeholder for path to ".minecraft/libraries" // when we propose this placeholder. - pair("${libraries_directory}", FileUtils.getAbsolutePath(repository.getLibrariesDirectory(version))), + pair("${libraries_directory}", FileUtils.getAbsolutePath(repository.getLibrariesDirectory(manifest))), // file_separator is used in -DignoreList pair("${file_separator}", File.separator), - pair("${primary_jar_name}", FileUtils.getName(repository.getInstanceJar(version))) + pair("${primary_jar_name}", FileUtils.getName(repository.getInstanceJar(manifest))) ); } /// Returns the native library directory selected by the launch options. private Path getNativeFolder() { if (StringUtils.isBlank(options.getNativesDir())) { - return repository.getNativeDirectory(version.getId(), options.getJava().getPlatform()); + return repository.getNativeDirectory(manifest.getId(), options.getJava().getPlatform()); } return Path.of(options.getNativesDir()); @@ -587,7 +587,7 @@ public ManagedProcess launch() throws IOException, InterruptedException { if (isUsingLog4j()) extractLog4jConfigurationFile(); - Path runDirectory = repository.getRunDirectory(version.getId()); + Path runDirectory = repository.getRunDirectory(manifest.getId()); if (StringUtils.isNotBlank(options.getPreLaunchCommand())) { ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPreLaunchCommand(), getEnvVars(nativeFolder))).directory(runDirectory.toFile()); @@ -617,13 +617,13 @@ public ManagedProcess launch() throws IOException, InterruptedException { } private Map getEnvVars(Path nativeFolder) { - String versionName = Optional.ofNullable(options.getVersionName()).orElse(version.getId()); + String versionName = Optional.ofNullable(options.getVersionName()).orElse(manifest.getId()); Map env = new LinkedHashMap<>(); env.put("INST_NAME", versionName); env.put("INST_ID", versionName); - env.put("INST_DIR", FileUtils.getAbsolutePath(repository.getInstanceRoot(version.getId()))); - env.put("INST_MC_DIR", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId()))); + env.put("INST_DIR", FileUtils.getAbsolutePath(repository.getInstanceRoot(manifest.getId()))); + env.put("INST_MC_DIR", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId()))); env.put("INST_JAVA", options.getJava().getBinary().toString()); if (options.getRenderer() instanceof Renderer.Driver driver) { @@ -782,7 +782,7 @@ else if (!isWindows && !(scriptExtension.equalsIgnoreCase("sh") || scriptExtensi writer.newLine(); } writer.write("Set-Location -LiteralPath "); - writer.write(CommandBuilder.pwshString(FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId())))); + writer.write(CommandBuilder.pwshString(FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId())))); writer.newLine(); @@ -826,7 +826,7 @@ else if (!isWindows && !(scriptExtension.equalsIgnoreCase("sh") || scriptExtensi writer.newLine(); } writer.newLine(); - writer.write(new CommandBuilder().addAll("cd", "/D", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId()))).toString()); + writer.write(new CommandBuilder().addAll("cd", "/D", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId()))).toString()); } else { writer.write("#!/usr/bin/env bash"); writer.newLine(); @@ -838,7 +838,7 @@ else if (!isWindows && !(scriptExtension.equalsIgnoreCase("sh") || scriptExtensi writer.write(new CommandBuilder().addAll("ln", "-s", FileUtils.getAbsolutePath(nativeFolder), commandLine.tempNativeFolder.toString()).toString()); writer.newLine(); } - writer.write(new CommandBuilder().addAll("cd", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId()))).toString()); + writer.write(new CommandBuilder().addAll("cd", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId()))).toString()); } writer.newLine(); if (StringUtils.isNotBlank(options.getPreLaunchCommand())) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java index 98c2d48d02..c20c05a899 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/Launcher.java @@ -33,23 +33,23 @@ public abstract class Launcher { protected final GameRepository repository; - protected final GameInstanceManifest version; + protected final GameInstanceManifest manifest; protected final AuthInfo authInfo; protected final LaunchOptions options; protected final ProcessListener listener; protected final boolean daemon; - public Launcher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { - this(repository, version, authInfo, options, null); + public Launcher(GameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions options) { + this(repository, manifest, authInfo, options, null); } - public Launcher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { - this(repository, version, authInfo, options, listener, true); + public Launcher(GameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { + this(repository, manifest, authInfo, options, listener, true); } - public Launcher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { + public Launcher(GameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { this.repository = repository; - this.version = version; + this.manifest = manifest; this.authInfo = authInfo; this.options = options; this.listener = listener; From 4513d5c1776dde5900328c2050ef412e6c873840 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 12:12:23 +0800 Subject: [PATCH 29/85] refactor: rename Versions to Instances and update references across the codebase --- .../org/jackhuang/hmcl/ui/Controllers.java | 4 ++-- .../hmcl/ui/download/DownloadPage.java | 6 +++--- .../org/jackhuang/hmcl/ui/main/MainPage.java | 4 ++-- .../org/jackhuang/hmcl/ui/main/RootPage.java | 6 +++--- .../terracotta/TerracottaControllerPage.java | 4 ++-- .../hmcl/ui/terracotta/TerracottaPage.java | 4 ++-- .../hmcl/ui/versions/GameListItem.java | 20 +++++++++---------- .../hmcl/ui/versions/GameListPage.java | 8 ++++---- .../{Versions.java => Instances.java} | 4 ++-- .../hmcl/ui/versions/VersionPage.java | 18 ++++++++--------- .../hmcl/ui/versions/WorldListPage.java | 4 ++-- .../hmcl/ui/versions/WorldManagePage.java | 4 ++-- 12 files changed, 43 insertions(+), 43 deletions(-) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/{Versions.java => Instances.java} (99%) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index 0c70fed237..6bad25b1c3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -61,7 +61,7 @@ import org.jackhuang.hmcl.ui.terracotta.TerracottaPage; import org.jackhuang.hmcl.ui.versions.GameListPage; import org.jackhuang.hmcl.ui.versions.VersionPage; -import org.jackhuang.hmcl.ui.versions.Versions; +import org.jackhuang.hmcl.ui.versions.Instances; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.i18n.I18n; @@ -703,7 +703,7 @@ public static void onHyperlinkAction(String href) { break; case "hmcl://game/launch": var repository = GameDirectoryManager.getSelectedRepository(); - Versions.launch(repository, repository.getSelectedInstance(), LauncherHelper::setKeep); + Instances.launch(repository, repository.getSelectedInstance(), LauncherHelper::setKeep); break; } } else { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index 342049ba4c..d091998e82 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -45,7 +45,7 @@ import org.jackhuang.hmcl.ui.versions.DownloadListPage; import org.jackhuang.hmcl.ui.versions.HMCLLocalizedDownloadListPage; import org.jackhuang.hmcl.ui.versions.VersionPage; -import org.jackhuang.hmcl.ui.versions.Versions; +import org.jackhuang.hmcl.ui.versions.Instances; import org.jackhuang.hmcl.ui.wizard.Navigation; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardProvider; @@ -97,11 +97,11 @@ public DownloadPage(String uploadVersion) { "game", versionPageNavigator::onGameSelected))); modpackTab.setNodeSupplier(loadVersionFor(() -> { DownloadListPage page = HMCLLocalizedDownloadListPage.ofModPack((downloadProvider, repository, __, modpack, file) -> { - Versions.downloadModpackImpl(downloadProvider, repository, uploadVersion, modpack, file); + Instances.downloadModpackImpl(downloadProvider, repository, uploadVersion, modpack, file); }, false); JFXButton installLocalModpackButton = FXUtils.newRaisedButton(i18n("install.modpack")); - installLocalModpackButton.setOnAction(e -> Versions.importModpack()); + installLocalModpackButton.setOnAction(e -> Instances.importModpack()); page.getActions().add(installLocalModpackButton); return page; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 3b512b353d..25db012686 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -62,7 +62,7 @@ import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.versions.GameListPopupMenu; -import org.jackhuang.hmcl.ui.versions.Versions; +import org.jackhuang.hmcl.ui.versions.Instances; import org.jackhuang.hmcl.upgrade.RemoteVersion; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.upgrade.UpdateHandler; @@ -317,7 +317,7 @@ private void doAnimation(boolean show) { private void launch() { HMCLGameRepository repository = GameDirectoryManager.getSelectedRepository(); - Versions.launch(repository, repository.getSelectedInstance()); + Instances.launch(repository, repository.getSelectedInstance()); } private void launchNoGame() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 7a561dc477..2b5cfc9e57 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -48,7 +48,7 @@ import org.jackhuang.hmcl.ui.nbt.NBTFileType; import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem; import org.jackhuang.hmcl.ui.versions.GameListPopupMenu; -import org.jackhuang.hmcl.ui.versions.Versions; +import org.jackhuang.hmcl.ui.versions.Instances; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; @@ -115,7 +115,7 @@ public MainPage getMainPage() { i18n("message.error"), MessageDialogPane.MessageType.ERROR); } } else if ("json".equalsIgnoreCase(FileUtils.getExtension(file))) { - Versions.installFromJson(GameDirectoryManager.getSelectedRepository(), file); + Instances.installFromJson(GameDirectoryManager.getSelectedRepository(), file); } }); @@ -158,7 +158,7 @@ protected Skin(RootPage control) { if (version == null) { Controllers.navigate(Controllers.getGameListPage()); } else { - Versions.modifyGameSettings(GameDirectoryManager.getSelectedRepository(), version); + Instances.modifyGameSettings(GameDirectoryManager.getSelectedRepository(), version); } }); FXUtils.onScroll(gameListItem, getSkinnable().getMainPage().getVersions(), list -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java index c2a015eb2e..2b558e8dba 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java @@ -51,7 +51,7 @@ import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.TransitionPane; import org.jackhuang.hmcl.ui.construct.*; -import org.jackhuang.hmcl.ui.versions.Versions; +import org.jackhuang.hmcl.ui.versions.Instances; import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.i18n.LocaleUtils; import org.jackhuang.hmcl.util.io.FileUtils; @@ -212,7 +212,7 @@ public TerracottaControllerPage() { MessageDialogPane.MessageType.QUESTION ).addAction(i18n("version.launch"), () -> { var repository = GameDirectoryManager.getSelectedRepository(); - Versions.launch(repository, repository.getSelectedInstance(), launcherHelper -> { + Instances.launch(repository, repository.getSelectedInstance(), launcherHelper -> { launcherHelper.setKeep(); launcherHelper.setDisableOfflineSkin(); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java index 4f5f15f384..8e607b234b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java @@ -38,7 +38,7 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.main.MainPage; import org.jackhuang.hmcl.ui.versions.GameListPopupMenu; -import org.jackhuang.hmcl.ui.versions.Versions; +import org.jackhuang.hmcl.ui.versions.Instances; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; @@ -79,7 +79,7 @@ public TerracottaPage() { .add(accountListItem) .addNavigationDrawerItem(i18n("version.launch"), SVG.ROCKET_LAUNCH, () -> { var repository = GameDirectoryManager.getSelectedRepository(); - Versions.launch(repository, repository.getSelectedInstance(), launcherHelper -> { + Instances.launch(repository, repository.getSelectedInstance(), launcherHelper -> { launcherHelper.setKeep(); launcherHelper.setDisableOfflineSkin(); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java index 70e4c62052..77a0706609 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java @@ -44,39 +44,39 @@ public ReadOnlyBooleanProperty selectedProperty() { } public void rename() { - Versions.renameVersion(repository, id); + Instances.renameVersion(repository, id); } public void duplicate() { - Versions.duplicateInstance(repository, id); + Instances.duplicateInstance(repository, id); } public void remove() { - Versions.deleteVersion(repository, id); + Instances.deleteVersion(repository, id); } public void export() { - Versions.exportVersion(repository, id); + Instances.exportVersion(repository, id); } public void browse() { - Versions.openFolder(repository, id); + Instances.openFolder(repository, id); } public void testGame() { - Versions.testGame(repository, id); + Instances.testGame(repository, id); } public void launch() { - Versions.launch(repository, id); + Instances.launch(repository, id); } public void modifyGameSettings() { - Versions.modifyGameSettings(repository, id); + Instances.modifyGameSettings(repository, id); } public void generateLaunchScript() { - Versions.generateLaunchScript(repository, id); + Instances.generateLaunchScript(repository, id); } public boolean canUpdate() { @@ -84,6 +84,6 @@ public boolean canUpdate() { } public void update() { - Versions.updateVersion(repository, id); + Instances.updateVersion(repository, id); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java index 4510234211..4ddbaeff7b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java @@ -103,8 +103,8 @@ public GameListPage() { } AdvancedListBox bottomLeftCornerList = new AdvancedListBox() - .addNavigationDrawerItem(i18n("install.new_game"), SVG.ADD_CIRCLE, Versions::addNewGame) - .addNavigationDrawerItem(i18n("install.modpack"), SVG.PACKAGE2, Versions::importModpack) + .addNavigationDrawerItem(i18n("install.new_game"), SVG.ADD_CIRCLE, Instances::addNewGame) + .addNavigationDrawerItem(i18n("install.modpack"), SVG.PACKAGE2, Instances::importModpack) .addNavigationDrawerItem(i18n("settings.type.global.manage"), SVG.SETTINGS, this::modifyGlobalGameSettings); FXUtils.setLimitHeight(bottomLeftCornerList, 40 * 3 + 12 * 2); setLeft(pane, bottomLeftCornerList); @@ -118,13 +118,13 @@ public GameListPage() { if (ModpackHelper.isFileModpackByExtension(file)) { Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(GameDirectoryManager.getSelectedRepository(), file), i18n("install.modpack")); } else if ("json".equalsIgnoreCase(FileUtils.getExtension(file))) { - Versions.installFromJson(GameDirectoryManager.getSelectedRepository(), file); + Instances.installFromJson(GameDirectoryManager.getSelectedRepository(), file); } }); } public void modifyGlobalGameSettings() { - Versions.modifyGlobalSettings(GameDirectoryManager.getSelectedRepository()); + Instances.modifyGlobalSettings(GameDirectoryManager.getSelectedRepository()); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java index cd659cf104..de8e203a55 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java @@ -63,8 +63,8 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.logging.Logger.LOG; -public final class Versions { - private Versions() { +public final class Instances { + private Instances() { } public static void addNewGame() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java index 0ce5def1f1..921d52e502 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java @@ -189,7 +189,7 @@ private void onBrowse(String sub) { } private void redownloadAssetIndex() { - Versions.updateGameAssets(getRepository(), getVersion()); + Instances.updateGameAssets(getRepository(), getVersion()); } private void clearLibraries() { @@ -224,36 +224,36 @@ private void clearAssets() { } private void clearJunkFiles() { - Versions.cleanVersion(getRepository(), getVersion()); + Instances.cleanVersion(getRepository(), getVersion()); } private void testGame() { - Versions.testGame(getRepository(), getVersion()); + Instances.testGame(getRepository(), getVersion()); } private void updateGame() { - Versions.updateVersion(getRepository(), getVersion()); + Instances.updateVersion(getRepository(), getVersion()); } private void generateLaunchScript() { - Versions.generateLaunchScript(getRepository(), getVersion()); + Instances.generateLaunchScript(getRepository(), getVersion()); } private void export() { - Versions.exportVersion(getRepository(), getVersion()); + Instances.exportVersion(getRepository(), getVersion()); } private void rename() { - Versions.renameVersion(getRepository(), getVersion()) + Instances.renameVersion(getRepository(), getVersion()) .thenApply(newVersionName -> this.preferredVersionName = newVersionName); } private void remove() { - Versions.deleteVersion(getRepository(), getVersion()); + Instances.deleteVersion(getRepository(), getVersion()); } private void duplicate() { - Versions.duplicateInstance(getRepository(), getVersion()); + Instances.duplicateInstance(getRepository(), getVersion()); } public HMCLGameRepository getRepository() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java index 66c0660919..dbbc4b472e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java @@ -197,11 +197,11 @@ public void reveal(World world) { } public void launch(World world) { - Versions.launchAndEnterWorld(repository, instanceId, world.getFileName()); + Instances.launchAndEnterWorld(repository, instanceId, world.getFileName()); } public void generateLaunchScript(World world) { - Versions.generateLaunchScriptForQuickEnterWorld(repository, instanceId, world.getFileName()); + Instances.generateLaunchScriptForQuickEnterWorld(repository, instanceId, world.getFileName()); } public BooleanProperty showAllProperty() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java index 68399a745c..b04c0cce21 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java @@ -150,11 +150,11 @@ public void onExited(Navigator.NavigationEvent event) { public void launch() { fireEvent(new PageCloseEvent()); - Versions.launchAndEnterWorld(repository, instanceId, world.getFileName()); + Instances.launchAndEnterWorld(repository, instanceId, world.getFileName()); } public void generateLaunchScript() { - Versions.generateLaunchScriptForQuickEnterWorld(repository, instanceId, world.getFileName()); + Instances.generateLaunchScriptForQuickEnterWorld(repository, instanceId, world.getFileName()); } @Override From c890fca07d33553959fcc58eb016f7bd9f79d545 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 12:33:02 +0800 Subject: [PATCH 30/85] refactor: replace String instanceId references with GameInstanceID across multiple classes --- .../hmcl/game/HMCLGameRepository.java | 252 +++++++++--------- .../hmcl/game/HMCLModpackInstallTask.java | 12 +- .../jackhuang/hmcl/game/LauncherHelper.java | 16 +- .../jackhuang/hmcl/game/ModpackHelper.java | 23 +- .../hmcl/setting/GameDirectoryManager.java | 3 +- .../setting/LegacyGameSettingsMigrator.java | 2 +- .../hmcl/ui/download/DownloadPage.java | 19 +- .../ModpackInstallWizardProvider.java | 10 +- .../VanillaInstallWizardProvider.java | 5 +- .../hmcl/ui/export/ExportWizardProvider.java | 5 +- .../hmcl/ui/export/ModpackInfoPage.java | 5 +- .../hmcl/ui/game/GameSettingsPage.java | 34 ++- .../org/jackhuang/hmcl/ui/main/MainPage.java | 5 +- .../org/jackhuang/hmcl/ui/main/RootPage.java | 3 +- .../hmcl/ui/terracotta/TerracottaPage.java | 3 +- .../hmcl/ui/versions/DownloadListPage.java | 14 +- .../hmcl/ui/versions/DownloadPage.java | 5 +- .../ui/versions/GameAdvancedListItem.java | 24 +- .../jackhuang/hmcl/ui/versions/GameItem.java | 11 +- .../hmcl/ui/versions/GameListCell.java | 3 +- .../hmcl/ui/versions/GameListPopupMenu.java | 5 +- .../jackhuang/hmcl/ui/versions/Instances.java | 45 ++-- .../hmcl/ui/versions/ModListPageSkin.java | 3 +- .../ui/versions/ResourcePackListPage.java | 3 +- .../hmcl/ui/versions/VersionIconDialog.java | 9 +- .../hmcl/ui/versions/VersionPage.java | 22 +- .../hmcl/setting/GameDirectoriesTest.java | 19 +- 27 files changed, 320 insertions(+), 240 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 5c47009353..849be8ba10 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -81,7 +81,7 @@ public final class HMCLGameRepository extends DefaultGameRepository { /// @param repository the owning game repository /// @param instanceId the game instance ID, or `null` when only repository context is available @NotNullByDefault - public record InstanceReference(HMCLGameRepository repository, @Nullable String instanceId) { + public record InstanceReference(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { } /// Directory under the version root that stores HMCL-managed instance metadata. @@ -103,11 +103,11 @@ public record InstanceReference(HMCLGameRepository repository, @Nullable String private final StringBinding selectedInstance; // instance game settings - private final Map instanceGameSettings = new HashMap<>(); + private final Map instanceGameSettings = new HashMap<>(); /// Instance IDs whose local game settings file has already been checked. - private final Set loadedInstanceGameSettings = new HashSet<>(); - private final Set readOnlyInstanceGameSettings = new HashSet<>(); - private final Set beingModpackInstances = new HashSet<>(); + private final Set loadedInstanceGameSettings = new HashSet<>(); + private final Set readOnlyInstanceGameSettings = new HashSet<>(); + private final Set beingModpackInstances = new HashSet<>(); public final EventManager onInstanceIconChanged = new EventManager<>(); @@ -135,8 +135,8 @@ public StringBinding selectedInstanceProperty() { } /// Sets the selected instance ID for this repository's game directory. - public void setSelectedInstance(@Nullable String instance) { - settings().setSelectedInstance(gameDirectory.getId(), instance); + public void setSelectedInstance(@Nullable GameInstanceID instanceId) { + settings().setSelectedInstance(gameDirectory.getId(), instanceId == null ? null : instanceId.id()); } /// Refreshes the selected instance ID after versions are loaded. @@ -147,7 +147,7 @@ public void refreshSelectedInstance() { refreshedInstance = getInstanceManifests().isEmpty() ? null : getInstanceManifests().iterator().next().getId(); } if (!Objects.equals(selectedInstance, refreshedInstance)) { - setSelectedInstance(refreshedInstance); + setSelectedInstance(refreshedInstance == null ? null : new GameInstanceID(refreshedInstance)); } } @@ -162,24 +162,24 @@ public DefaultDependencyManager getDependency(DownloadProvider downloadProvider) } @Override - public Path getRunDirectory(String id) { - if (beingModpackInstances.contains(id) || isModpack(id)) { - return getInstanceRoot(id); + public Path getRunDirectory(GameInstanceID instanceId) { + if (beingModpackInstances.contains(instanceId) || isModpack(instanceId)) { + return getInstanceRoot(instanceId); } - GameSettings.Instance localSetting = getInstanceGameSettings(id); + GameSettings.Instance localSetting = getInstanceGameSettings(instanceId); boolean useInstanceRunningDirectory = localSetting != null && localSetting.getOverrideProperties().contains(GameSettings.PROPERTY_RUNNING_DIRECTORY); String runningDirectory = getSelectedRunningDirectory(localSetting, useInstanceRunningDirectory); if (StringUtils.isBlank(runningDirectory)) { - return useInstanceRunningDirectory ? getInstanceRoot(id) : super.getRunDirectory(id); + return useInstanceRunningDirectory ? getInstanceRoot(instanceId) : super.getRunDirectory(instanceId); } try { return Path.of(runningDirectory); } catch (InvalidPathException ignored) { - return getInstanceRoot(id); + return getInstanceRoot(instanceId); } } @@ -214,7 +214,7 @@ protected void refreshImpl() { loadedInstanceGameSettings.clear(); readOnlyInstanceGameSettings.clear(); super.refreshImpl(); - getInstanceManifests().stream().map(GameInstanceManifest::getId).forEach(this::loadInstanceGameSettings); + getInstanceManifests().stream().map(GameInstanceManifest::id).forEach(this::loadInstanceGameSettings); try { Path file = getBaseDirectory().resolve("launcher_profiles.json"); @@ -237,20 +237,20 @@ private void clean(Path directory) throws IOException { FileUtils.deleteDirectory(directory.resolve("logs")); } - public void clean(String id) throws IOException { + public void clean(GameInstanceID instanceId) throws IOException { clean(getBaseDirectory()); - clean(getRunDirectory(id)); + clean(getRunDirectory(instanceId)); } - public void duplicateInstance(String srcId, String dstId, boolean copySaves) throws IOException { + public void duplicateInstance(GameInstanceID srcId, GameInstanceID dstId, boolean copySaves) throws IOException { Path srcDir = getInstanceRoot(srcId); Path dstDir = getInstanceRoot(dstId); GameInstanceManifest fromVersion = getInstanceManifest(srcId); List blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); - blackList.add(srcId + ".jar"); - blackList.add(srcId + ".json"); + blackList.add(srcId.id() + ".jar"); + blackList.add(srcId.id() + ".json"); if (!copySaves) blackList.add("saves"); @@ -259,17 +259,17 @@ public void duplicateInstance(String srcId, String dstId, boolean copySaves) thr Files.createDirectories(dstDir); FileUtils.copyDirectory(srcDir, dstDir, path -> Modpack.acceptFile(path, blackList, null)); - Path fromJson = srcDir.resolve(srcId + ".json"); - Path fromJar = srcDir.resolve(srcId + ".jar"); - Path toJson = dstDir.resolve(dstId + ".json"); - Path toJar = dstDir.resolve(dstId + ".jar"); + Path fromJson = srcDir.resolve(srcId.id() + ".json"); + Path fromJar = srcDir.resolve(srcId.id() + ".jar"); + Path toJson = dstDir.resolve(dstId.id() + ".json"); + Path toJar = dstDir.resolve(dstId.id() + ".jar"); if (Files.exists(fromJar)) { Files.copy(fromJar, toJar); } Files.copy(fromJson, toJson); - JsonUtils.writeToJsonFile(toJson, fromVersion.setId(dstId).setJar(dstId)); + JsonUtils.writeToJsonFile(toJson, fromVersion.withId(dstId).withJar(dstId)); boolean copyOriginalGameDir; try { @@ -292,48 +292,48 @@ public void duplicateInstance(String srcId, String dstId, boolean copySaves) thr FileUtils.copyDirectory(srcGameDir, dstGameDir, path -> Modpack.acceptFile(path, blackList, null)); } - private GameSettings.Instance copyInstanceGameSettings(String id) { - GameSettings.Instance setting = getInstanceGameSettings(id); + private GameSettings.Instance copyInstanceGameSettings(GameInstanceID instanceId) { + GameSettings.Instance setting = getInstanceGameSettings(instanceId); if (setting != null) { return JsonUtils.clone(LauncherSettings.SETTINGS_GSON, setting, TypeToken.get(GameSettings.Instance.class)); } GameSettings.Instance copied = new GameSettings.Instance(); - copied.parentProperty().setValue(getEffectiveGameSettings(id).getPreset().idProperty().getValue()); + copied.parentProperty().setValue(getEffectiveGameSettings(instanceId).getPreset().idProperty().getValue()); return copied; } /// Returns the HMCL-managed metadata directory under the version root. /// /// This directory stores instance-scoped files owned by HMCL. - public Path getInstanceMetadataDirectory(String id) { - return getInstanceRoot(id).resolve(INSTANCE_METADATA_DIRECTORY); + public Path getInstanceMetadataDirectory(GameInstanceID instanceId) { + return getInstanceRoot(instanceId).resolve(INSTANCE_METADATA_DIRECTORY); } /// Returns the HMCL-managed configuration directory under the instance metadata directory. - public Path getInstanceConfigDirectory(String id) { - return getInstanceMetadataDirectory(id).resolve(INSTANCE_CONFIG_DIRECTORY); + public Path getInstanceConfigDirectory(GameInstanceID instanceId) { + return getInstanceMetadataDirectory(instanceId).resolve(INSTANCE_CONFIG_DIRECTORY); } /// Returns the HMCL-managed state directory under the instance metadata directory. - public Path getInstanceStateDirectory(String id) { - return getInstanceMetadataDirectory(id).resolve(INSTANCE_STATE_DIRECTORY); + public Path getInstanceStateDirectory(GameInstanceID instanceId) { + return getInstanceMetadataDirectory(instanceId).resolve(INSTANCE_STATE_DIRECTORY); } /// Returns the current local game settings path under the instance configuration directory. - private Path getInstanceGameSettingsFile(String id) { - return getInstanceConfigDirectory(id).resolve(INSTANCE_GAME_SETTINGS_FILENAME); + private Path getInstanceGameSettingsFile(GameInstanceID instanceId) { + return getInstanceConfigDirectory(instanceId).resolve(INSTANCE_GAME_SETTINGS_FILENAME); } - private void loadInstanceGameSettings(String id) { - loadedInstanceGameSettings.add(id); - InstanceGameSettingsLoadResult result = loadGameSettingsFile(getInstanceGameSettingsFile(id)); + private void loadInstanceGameSettings(GameInstanceID instanceId) { + loadedInstanceGameSettings.add(instanceId); + InstanceGameSettingsLoadResult result = loadGameSettingsFile(getInstanceGameSettingsFile(instanceId)); if (result.setting() != null) { - initInstanceGameSettings(id, result.setting(), result.allowSave()); + initInstanceGameSettings(instanceId, result.setting(), result.allowSave()); return; } if (!result.allowSave()) { - readOnlyInstanceGameSettings.add(id); + readOnlyInstanceGameSettings.add(instanceId); return; } @@ -344,15 +344,15 @@ private void loadInstanceGameSettings(String id) { LegacyGameSettingsMigrator.InstanceMigrationResult migrationResult = LegacyGameSettingsMigrator.migrateInstanceGameSettings( - this, id, + this, instanceId, legacyParent); if (migrationResult != null) { - initInstanceGameSettings(id, migrationResult.setting()); + initInstanceGameSettings(instanceId, migrationResult.setting()); try { - saveGameSettingsSync(id); + saveGameSettingsSync(instanceId); migrationResult.saveReceipt(); } catch (IOException e) { - LOG.warning("Failed to save migrated instance game settings for " + id, e); + LOG.warning("Failed to save migrated instance game settings for " + instanceId, e); } return; } @@ -416,35 +416,35 @@ private InstanceGameSettingsLoadResult loadGameSettingsFile(Path file) { } } - public @Nullable GameSettings.Instance createInstanceGameSettings(String id) { - if (!hasInstance(id)) { + public @Nullable GameSettings.Instance createInstanceGameSettings(GameInstanceID instanceId) { + if (!hasInstance(instanceId)) { return null; } - if (readOnlyInstanceGameSettings.contains(id)) { + if (readOnlyInstanceGameSettings.contains(instanceId)) { return null; } - if (instanceGameSettings.containsKey(id)) { - return getInstanceGameSettings(id); + if (instanceGameSettings.containsKey(instanceId)) { + return getInstanceGameSettings(instanceId); } GameSettings.Instance setting = new GameSettings.Instance(); - return initInstanceGameSettings(id, setting); + return initInstanceGameSettings(instanceId, setting); } - private GameSettings.Instance initInstanceGameSettings(String id, GameSettings.Instance setting) { - return initInstanceGameSettings(id, setting, true); + private GameSettings.Instance initInstanceGameSettings(GameInstanceID instanceId, GameSettings.Instance setting) { + return initInstanceGameSettings(instanceId, setting, true); } - private GameSettings.Instance initInstanceGameSettings(String id, GameSettings.Instance setting, boolean allowSave) { + private GameSettings.Instance initInstanceGameSettings(GameInstanceID instanceId, GameSettings.Instance setting, boolean allowSave) { normalizeRunningDirectoryOverride(setting); setting.setSavable(allowSave); - loadedInstanceGameSettings.add(id); - instanceGameSettings.put(id, setting); + loadedInstanceGameSettings.add(instanceId); + instanceGameSettings.put(instanceId, setting); if (allowSave) { - readOnlyInstanceGameSettings.remove(id); - setting.addListener(a -> saveGameSettings(id)); + readOnlyInstanceGameSettings.remove(instanceId); + setting.addListener(a -> saveGameSettings(instanceId)); } else { - readOnlyInstanceGameSettings.add(id); + readOnlyInstanceGameSettings.add(instanceId); } return setting; } @@ -457,59 +457,59 @@ private void normalizeRunningDirectoryOverride(GameSettings.Instance setting) { } @Nullable - public GameSettings.Instance getInstanceGameSettings(String id) { - if (!loadedInstanceGameSettings.contains(id)) { - loadInstanceGameSettings(id); + public GameSettings.Instance getInstanceGameSettings(GameInstanceID instanceId) { + if (!loadedInstanceGameSettings.contains(instanceId)) { + loadInstanceGameSettings(instanceId); } - return instanceGameSettings.get(id); + return instanceGameSettings.get(instanceId); } @Nullable - public GameSettings.Instance getInstanceGameSettingsOrCreate(String id) { - GameSettings.Instance setting = getInstanceGameSettings(id); + public GameSettings.Instance getInstanceGameSettingsOrCreate(GameInstanceID instanceId) { + GameSettings.Instance setting = getInstanceGameSettings(instanceId); if (setting == null) { - setting = createInstanceGameSettings(id); + setting = createInstanceGameSettings(instanceId); } return setting; } /// Returns whether the instance-specific game settings file cannot be overwritten safely. /// - /// @param id the instance ID + /// @param instanceId the instance ID /// @return whether the instance settings are loaded in read-only mode - public boolean isInstanceGameSettingsReadOnly(String id) { - if (!loadedInstanceGameSettings.contains(id)) { - loadInstanceGameSettings(id); + public boolean isInstanceGameSettingsReadOnly(GameInstanceID instanceId) { + if (!loadedInstanceGameSettings.contains(instanceId)) { + loadInstanceGameSettings(instanceId); } - return readOnlyInstanceGameSettings.contains(id); + return readOnlyInstanceGameSettings.contains(instanceId); } /// Backs up and overwrites the instance-specific game settings file with the currently loaded settings. /// - /// @param id the instance ID - public void forceOverwriteInstanceGameSettings(String id) { - if (!loadedInstanceGameSettings.contains(id)) { - loadInstanceGameSettings(id); + /// @param instanceId the instance ID + public void forceOverwriteInstanceGameSettings(GameInstanceID instanceId) { + if (!loadedInstanceGameSettings.contains(instanceId)) { + loadInstanceGameSettings(instanceId); } - GameSettings.Instance setting = instanceGameSettings.get(id); + GameSettings.Instance setting = instanceGameSettings.get(instanceId); if (setting == null) { setting = new GameSettings.Instance(); - instanceGameSettings.put(id, setting); - loadedInstanceGameSettings.add(id); + instanceGameSettings.put(instanceId, setting); + loadedInstanceGameSettings.add(instanceId); } boolean installAutoSave = !setting.isSavable(); - Path file = getInstanceGameSettingsFile(id).toAbsolutePath().normalize(); + Path file = getInstanceGameSettingsFile(instanceId).toAbsolutePath().normalize(); SettingFileUtils.backupInvalidConfig(file); setting.setSchema(GameSettings.Instance.CURRENT_SCHEMA); setting.setSavable(true); setting.setBackupOnNextSave(false); - readOnlyInstanceGameSettings.remove(id); - saveGameSettings(id); + readOnlyInstanceGameSettings.remove(instanceId); + saveGameSettings(instanceId); if (installAutoSave) { - setting.addListener(a -> saveGameSettings(id)); + setting.addListener(a -> saveGameSettings(instanceId)); } } @@ -520,35 +520,35 @@ public GameSettings.Preset getParentGameSettings(@Nullable GameSettings.Instance return parentSetting != null ? parentSetting : SettingsManager.getDefaultGameSettingsPresetOrCreate(); } - public GameSettings.Effective getEffectiveGameSettings(String id) { - GameSettings.Instance instance = getInstanceGameSettings(id); + public GameSettings.Effective getEffectiveGameSettings(GameInstanceID instanceId) { + GameSettings.Instance instance = getInstanceGameSettings(instanceId); return GameSettings.resolve(getParentGameSettings(instance), instance); } - public void applyDefaultIsolationSetting(String id) { - if (!hasInstance(id)) { + public void applyDefaultIsolationSetting(GameInstanceID instanceId) { + if (!hasInstance(instanceId)) { return; } - GameSettings.Instance instanceSetting = getInstanceGameSettings(id); + GameSettings.Instance instanceSetting = getInstanceGameSettings(instanceId); GameSettings.Preset preset = getParentGameSettings(instanceSetting); DefaultIsolationType type = Lang.requireNonNullElse(preset.defaultIsolationTypeProperty().getValue(), DefaultIsolationType.MODDED); boolean isolated = switch (type) { case NEVER -> false; case ALWAYS -> true; - case MODDED -> LibraryAnalyzer.isModded(this, getInstanceManifest(id).resolve(this)); + case MODDED -> LibraryAnalyzer.isModded(this, getInstanceManifest(instanceId).resolve(this)); }; if (isolated) { - GameSettings.Instance setting = instanceSetting != null ? instanceSetting : getInstanceGameSettingsOrCreate(id); + GameSettings.Instance setting = instanceSetting != null ? instanceSetting : getInstanceGameSettingsOrCreate(instanceId); if (setting != null) { setting.getOverrideProperties().add(GameSettings.PROPERTY_RUNNING_DIRECTORY); } } } - public Optional getInstanceIconFile(String id) { - Path root = getInstanceRoot(id); + public Optional getInstanceIconFile(GameInstanceID instanceId) { + Path root = getInstanceRoot(instanceId); for (String extension : FXUtils.IMAGE_EXTENSIONS) { Path file = root.resolve("icon." + extension); @@ -560,19 +560,19 @@ public Optional getInstanceIconFile(String id) { return Optional.empty(); } - public void setInstanceIconFile(String id, Path iconFile) throws IOException { + public void setInstanceIconFile(GameInstanceID instanceId, Path iconFile) throws IOException { String ext = FileUtils.getExtension(iconFile).toLowerCase(Locale.ROOT); if (!FXUtils.IMAGE_EXTENSIONS.contains(ext)) { throw new IllegalArgumentException("Unsupported icon file: " + ext); } - deleteIconFile(id); + deleteIconFile(instanceId); - FileUtils.copyFile(iconFile, getInstanceRoot(id).resolve("icon." + ext)); + FileUtils.copyFile(iconFile, getInstanceRoot(instanceId).resolve("icon." + ext)); } - public void deleteIconFile(String id) { - Path root = getInstanceRoot(id); + public void deleteIconFile(GameInstanceID instanceId) { + Path root = getInstanceRoot(instanceId); for (String extension : FXUtils.IMAGE_EXTENSIONS) { Path file = root.resolve("icon." + extension); try { @@ -583,21 +583,21 @@ public void deleteIconFile(String id) { } } - public Image getInstanceIconImage(@Nullable String id) { - if (id == null || !isLoaded()) + public Image getInstanceIconImage(@Nullable GameInstanceID instanceId) { + if (instanceId == null || !isLoaded()) return VersionIconType.DEFAULT.getIcon(); - GameSettings.Instance setting = getInstanceGameSettings(id); + GameSettings.Instance setting = getInstanceGameSettings(instanceId); VersionIconType iconType = setting != null ? Lang.requireNonNullElse(setting.iconProperty().getValue(), VersionIconType.DEFAULT) : VersionIconType.DEFAULT; if (iconType == VersionIconType.DEFAULT) { - GameInstanceManifest version = getInstanceManifest(id).resolve(this); - Optional iconFile = getInstanceIconFile(id); + GameInstanceManifest version = getInstanceManifest(instanceId).resolve(this); + Optional iconFile = getInstanceIconFile(instanceId); if (iconFile.isPresent()) { try { return FXUtils.loadImage(iconFile.get(), 64, 64, true, true); } catch (Exception e) { - LOG.warning("Failed to load version icon of " + id, e); + LOG.warning("Failed to load version icon of " + instanceId, e); } } @@ -638,14 +638,14 @@ else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.OPTIFINE)) } } - public void saveGameSettings(String id) { - if (!instanceGameSettings.containsKey(id) || readOnlyInstanceGameSettings.contains(id)) + public void saveGameSettings(GameInstanceID instanceId) { + if (!instanceGameSettings.containsKey(instanceId) || readOnlyInstanceGameSettings.contains(instanceId)) return; - GameSettings.Instance setting = instanceGameSettings.get(id); + GameSettings.Instance setting = instanceGameSettings.get(instanceId); if (setting == null) { return; } - Path file = getInstanceGameSettingsFile(id).toAbsolutePath().normalize(); + Path file = getInstanceGameSettingsFile(instanceId).toAbsolutePath().normalize(); try { Files.createDirectories(file.getParent()); } catch (IOException e) { @@ -661,19 +661,19 @@ public void saveGameSettings(String id) { /// Saves instance-specific game settings synchronously. /// - /// @param id the instance ID + /// @param instanceId the instance ID /// @throws IOException if saving the file fails - private void saveGameSettingsSync(String id) throws IOException { - if (!instanceGameSettings.containsKey(id) || readOnlyInstanceGameSettings.contains(id)) { + private void saveGameSettingsSync(GameInstanceID instanceId) throws IOException { + if (!instanceGameSettings.containsKey(instanceId) || readOnlyInstanceGameSettings.contains(instanceId)) { return; } - GameSettings.Instance setting = instanceGameSettings.get(id); + GameSettings.Instance setting = instanceGameSettings.get(instanceId); if (setting == null) { return; } - Path file = getInstanceGameSettingsFile(id).toAbsolutePath().normalize(); + Path file = getInstanceGameSettingsFile(instanceId).toAbsolutePath().normalize(); Files.createDirectories(file.getParent()); if (setting.isBackupOnNextSave()) { setting.setBackupOnNextSave(false); @@ -691,17 +691,17 @@ private record InstanceGameSettingsLoadResult( boolean allowSave) { } - public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVersion, Path gameDir, List javaAgents, List javaArguments, boolean makeLaunchScript) { - GameSettings.Effective vs = getEffectiveGameSettings(version); + public LaunchOptions.Builder getLaunchOptions(GameInstanceID instanceId, JavaRuntime javaVersion, Path gameDir, List javaAgents, List javaArguments, boolean makeLaunchScript) { + GameSettings.Effective vs = getEffectiveGameSettings(instanceId); boolean noJVMOptions = vs.getInheritable(GameSettings::noJVMOptionsProperty); boolean autoMemory = vs.get(GameSettings::autoMemoryProperty); - GameVersionNumber gameVersionNumber = GameVersionNumber.asGameVersion(getGameVersion(version)); + GameVersionNumber gameVersionNumber = GameVersionNumber.asGameVersion(getGameVersion(instanceId)); LaunchOptions.Builder builder = new LaunchOptions.Builder() .setGameDir(gameDir) .setJava(javaVersion) .setVersionType(Metadata.TITLE) - .setVersionName(version) + .setVersionName(instanceId.id()) .setProfileName(Metadata.TITLE) .setGameArguments(StringUtils.tokenize(vs.get(GameSettings::gameArgumentsProperty))) .setOverrideJavaArguments(StringUtils.tokenize(vs.get(GameSettings::jvmOptionsProperty))) @@ -750,7 +750,7 @@ public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVe builder.setQuickPlayOption(quickPlayOption); } - Path json = getModpackConfiguration(version); + Path json = getModpackConfiguration(instanceId); if (Files.exists(json)) { try { String jsonText = Files.readString(json); @@ -769,27 +769,27 @@ public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVe } @Override - public Path getModpackConfiguration(String version) { - return getInstanceRoot(version).resolve("modpack.cfg"); + public Path getModpackConfiguration(GameInstanceID instanceId) { + return getInstanceRoot(instanceId).resolve("modpack.cfg"); } - public void markInstanceAsModpack(String id) { - beingModpackInstances.add(id); + public void markInstanceAsModpack(GameInstanceID instanceId) { + beingModpackInstances.add(instanceId); } - public void undoMark(String id) { - beingModpackInstances.remove(id); + public void undoMark(GameInstanceID instanceId) { + beingModpackInstances.remove(instanceId); } - public void markInstanceLaunchedAbnormally(String id) { + public void markInstanceLaunchedAbnormally(GameInstanceID instanceId) { try { - Files.createFile(getInstanceRoot(id).resolve(".abnormal")); + Files.createFile(getInstanceRoot(instanceId).resolve(".abnormal")); } catch (IOException ignored) { } } - public boolean unmarkInstanceLaunchedAbnormally(String id) { - Path file = getInstanceRoot(id).resolve(".abnormal"); + public boolean unmarkInstanceLaunchedAbnormally(GameInstanceID instanceId) { + Path file = getInstanceRoot(instanceId).resolve(".abnormal"); if (Files.isRegularFile(file)) { try { Files.delete(file); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java index 715dbd27e2..8fb7a8332a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java @@ -38,6 +38,7 @@ public final class HMCLModpackInstallTask extends Task { private final Path zipFile; private final String name; + private final GameInstanceID instanceId; private final HMCLGameRepository repository; private final DefaultDependencyManager dependency; private final Modpack modpack; @@ -49,17 +50,18 @@ public HMCLModpackInstallTask(HMCLGameRepository repository, Path zipFile, Modpa this.dependency = repository.getDependency(); this.zipFile = zipFile; this.name = name; + this.instanceId = new GameInstanceID(name); this.modpack = modpack; - Path run = repository.getRunDirectory(name); - Path json = repository.getModpackConfiguration(name); - if (repository.hasInstance(name) && Files.notExists(json)) + Path run = repository.getRunDirectory(instanceId); + Path json = repository.getModpackConfiguration(instanceId); + if (repository.hasInstance(instanceId) && Files.notExists(json)) throw new IllegalArgumentException("Version " + name + " already exists"); dependents.add(dependency.gameBuilder().name(name).gameVersion(modpack.getGameVersion()).buildAsync()); onDone().register(event -> { - if (event.isFailed()) repository.removeInstanceFromDisk(name); + if (event.isFailed()) repository.removeInstanceFromDisk(instanceId); }); ModpackConfiguration config = null; @@ -73,7 +75,7 @@ public HMCLModpackInstallTask(HMCLGameRepository repository, Path zipFile, Modpa } catch (JsonParseException | IOException ignore) { } dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList("/minecraft"), it -> !"pack.json".equals(it), config)); - dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/minecraft"), modpack, HMCLModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack")); + dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/minecraft"), modpack, HMCLModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(instanceId)).withStage("hmcl.modpack")); } @Override 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 e46bfc07af..007eba52f6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -85,6 +85,7 @@ public final class LauncherHelper { private final HMCLGameRepository repository; private Account account; private final String selectedVersion; + private final GameInstanceID selectedInstanceId; private Path scriptFile; private final GameSettings.Effective setting; private LauncherVisibility launcherVisibility; @@ -96,7 +97,8 @@ public LauncherHelper(HMCLGameRepository repository, Account account, String sel this.repository = Objects.requireNonNull(repository); this.account = Objects.requireNonNull(account); this.selectedVersion = Objects.requireNonNull(selectedVersion); - this.setting = repository.getEffectiveGameSettings(selectedVersion); + this.selectedInstanceId = new GameInstanceID(selectedVersion); + this.setting = repository.getEffectiveGameSettings(selectedInstanceId); this.launcherVisibility = setting.getInheritable(GameSettings::launcherVisibilityProperty); this.showLogs = setting.getInheritable(GameSettings::showLogsProperty); this.launchingStepsPane.setTitle(i18n("version.launch")); @@ -148,9 +150,9 @@ private void launch0() { PROCESSES.removeIf(it -> it.get() == null); DefaultDependencyManager dependencyManager = repository.getDependency(); - AtomicReference version = new AtomicReference<>(MaintainTask.maintain(repository, repository.getResolvedInstanceManifest(selectedVersion))); + AtomicReference version = new AtomicReference<>(MaintainTask.maintain(repository, repository.getResolvedInstanceManifest(selectedInstanceId).manifest())); Optional gameVersion = repository.getGameVersion(version.get()); - boolean integrityCheck = repository.unmarkInstanceLaunchedAbnormally(selectedVersion); + boolean integrityCheck = repository.unmarkInstanceLaunchedAbnormally(selectedInstanceId); CountDownLatch launchingLatch = new CountDownLatch(1); List javaAgents = new ArrayList<>(0); List javaArguments = new ArrayList<>(0); @@ -167,7 +169,7 @@ private void launch0() { dependencyManager.checkGameCompletionAsync(version.get(), integrityCheck), Task.composeAsync(() -> { try { - ModpackConfiguration configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion)); + ModpackConfiguration configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedInstanceId)); ModpackProvider provider = ModpackHelper.getProviderByType(configuration.getType()); if (provider == null) return null; else return provider.createCompletionTask(dependencyManager, selectedVersion); @@ -233,7 +235,7 @@ private void launch0() { .thenComposeAsync(() -> logIn(account).withStage("launch.state.logging_in")) .thenComposeAsync(authInfo -> Task.supplyAsync(() -> { LaunchOptions.Builder launchOptionsBuilder = repository.getLaunchOptions( - selectedVersion, javaVersionRef.get(), repository.getBaseDirectory(), javaAgents, javaArguments, scriptFile != null); + selectedInstanceId, javaVersionRef.get(), repository.getBaseDirectory(), javaAgents, javaArguments, scriptFile != null); if (disableOfflineSkin) { launchOptionsBuilder.setDaemon(false); } @@ -243,7 +245,7 @@ private void launch0() { LaunchOptions launchOptions = launchOptionsBuilder.create(); - LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModsDirectory(selectedVersion), 10)); + LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModsDirectory(selectedInstanceId), 10)); return new HMCLGameLauncher( repository, @@ -1000,7 +1002,7 @@ public void onExit(int exitCode, ExitType exitType) { } if (exitType != ExitType.NORMAL) { - repository.markInstanceLaunchedAbnormally(version.getId()); + repository.markInstanceLaunchedAbnormally(version.id()); runLater(() -> new GameCrashWindow(process, exitType, repository, version, launchOptions, logs).show()); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 93b67ac9df..51bf0db9d8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -157,12 +157,13 @@ public static ModpackConfiguration readModpackConfiguration(Path file) throws } public static Task getInstallTask(HMCLGameRepository repository, ServerModpackManifest manifest, String name, Modpack modpack) { - repository.markInstanceAsModpack(name); + GameInstanceID instanceId = new GameInstanceID(name); + repository.markInstanceAsModpack(instanceId); ExceptionalRunnable success = () -> { repository.refresh(); - GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(name); - repository.undoMark(name); + GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(instanceId); + repository.undoMark(instanceId); if (setting != null) { setting.getOverrideProperties().add(GameSettings.PROPERTY_RUNNING_DIRECTORY); } @@ -201,12 +202,13 @@ public static Task getInstallManuallyCreatedModpackTask(Path zipFile, String } public static Task getInstallTask(HMCLGameRepository repository, Path zipFile, String name, Modpack modpack, String iconUrl) { - repository.markInstanceAsModpack(name); + GameInstanceID instanceId = new GameInstanceID(name); + repository.markInstanceAsModpack(instanceId); ExceptionalRunnable success = () -> { repository.refresh(); - GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(name); - repository.undoMark(name); + GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(instanceId); + repository.undoMark(instanceId); if (setting != null) { setting.getOverrideProperties().add(GameSettings.PROPERTY_RUNNING_DIRECTORY); } @@ -335,23 +337,24 @@ private static void applyCommandAndJvmSettings(MultiMCInstanceConfiguration c, G private static Task createMultiMCPostUpdateTask(HMCLGameRepository repository, MultiMCInstanceConfiguration manifest, String version) { return Task.runAsync(Schedulers.javafx(), () -> { - GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(version)); + GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(new GameInstanceID(version))); ModpackHelper.applyCommandAndJvmSettings(manifest, setting); }); } private static Task createMultiMCPostInstallTask(HMCLGameRepository repository, MultiMCInstanceConfiguration manifest, String version) { return Task.runAsync(Schedulers.javafx(), () -> { - GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(version)); + GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(new GameInstanceID(version))); ModpackHelper.toGameSettings(manifest, setting); }); } private static Task createMcbbsPostInstallTask(HMCLGameRepository repository, McbbsModpackManifest manifest, String version) { return Task.runAsync(Schedulers.javafx(), () -> { - GameSettings.Effective effective = repository.getEffectiveGameSettings(version); + GameInstanceID instanceId = new GameInstanceID(version); + GameSettings.Effective effective = repository.getEffectiveGameSettings(instanceId); if (manifest.getLaunchInfo().getMinMemory() > effective.getMaxMemory()) { - GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(version)); + GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(instanceId)); setting.getOverrideProperties().addAll(List.of( GameSettings.PROPERTY_AUTO_MEMORY, GameSettings.PROPERTY_MIN_MEMORY, diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java index f093cbe076..21a60cab29 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java @@ -24,6 +24,7 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.RefreshedVersionsEvent; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.util.PortablePath; import org.jackhuang.hmcl.util.i18n.I18n; @@ -477,7 +478,7 @@ public static ReadOnlyStringProperty selectedInstanceProperty() { /// Sets the selected instance ID for the selected repository. public static void setSelectedInstance(@Nullable String instance) { - getSelectedRepository().setSelectedInstance(instance); + getSelectedRepository().setSelectedInstance(instance == null ? null : new GameInstanceID(instance)); } /// Listeners notified after the selected repository has loaded versions. diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java index 3704b5f8f6..430cbd97f0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java @@ -130,7 +130,7 @@ public static GameSettings.Preset toPreset(GameSettingsPresetID id, int autoName /// @return the migrated instance setting, or `null` when no legacy file can be migrated public static @Nullable InstanceMigrationResult migrateInstanceGameSettings( HMCLGameRepository repository, - String instanceId, + GameInstanceID instanceId, @Nullable GameSettingsPresetID parent) { Path instanceRoot = repository.getInstanceRoot(instanceId); Path file = instanceRoot.resolve(LEGACY_INSTANCE_SETTINGS_FILENAME); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index d091998e82..94e9eebb83 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -25,6 +25,7 @@ import org.jackhuang.hmcl.addon.repository.CurseForgeRemoteAddonRepository; import org.jackhuang.hmcl.download.*; import org.jackhuang.hmcl.download.game.GameRemoteVersion; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.setting.GameDirectoryManager; @@ -141,10 +142,18 @@ private static Supplier loadVersionFor(Supplier nodeSuppl }; } - public static void download(DownloadProvider downloadProvider, HMCLGameRepository repository, @Nullable String version, RemoteAddon.Version file, String subdirectoryName) { - if (version == null) version = repository.getSelectedInstance(); + public static void download(DownloadProvider downloadProvider, HMCLGameRepository repository, @Nullable GameInstanceID instanceId, RemoteAddon.Version file, String subdirectoryName) { + if (instanceId == null) { + @Nullable String selectedInstance = repository.getSelectedInstance(); + if (selectedInstance != null) { + try { + instanceId = new GameInstanceID(selectedInstance); + } catch (IllegalArgumentException ignored) { + } + } + } - Path runDirectory = repository.hasInstance(version) ? repository.getRunDirectory(version) : repository.getBaseDirectory(); + Path runDirectory = instanceId != null && repository.hasInstance(instanceId) ? repository.getRunDirectory(instanceId) : repository.getBaseDirectory(); Set existingFiles; @@ -320,8 +329,8 @@ private Task finishVersionDownloadingAsync(SettingsMap settings) { return builder.buildAsync().whenComplete(any -> { repository.refresh(); - repository.applyDefaultIsolationSetting(name); - }).thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(name)); + repository.applyDefaultIsolationSetting(new GameInstanceID(name)); + }).thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(new GameInstanceID(name))); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index 21f0e512b9..a059d66916 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.ui.download; import javafx.scene.Node; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ManuallyCreatedModpackException; import org.jackhuang.hmcl.game.ModpackHelper; @@ -105,10 +106,11 @@ private Task finishModpackInstallingAsync(SettingsMap settings) { return null; } try { + GameInstanceID instanceId = new GameInstanceID(name); if (serverModpackManifest != null) { - return ModpackHelper.getUpdateTask(repository, serverModpackManifest, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(name))); + return ModpackHelper.getUpdateTask(repository, serverModpackManifest, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(instanceId))); } else { - return ModpackHelper.getUpdateTask(repository, selected, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(name))); + return ModpackHelper.getUpdateTask(repository, selected, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(instanceId))); } } catch (UnsupportedModpackException | ManuallyCreatedModpackException e) { Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageType.ERROR); @@ -121,10 +123,10 @@ private Task finishModpackInstallingAsync(SettingsMap settings) { } else { if (serverModpackManifest != null) { return ModpackHelper.getInstallTask(repository, serverModpackManifest, name, modpack) - .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(name)); + .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(new GameInstanceID(name))); } else { return ModpackHelper.getInstallTask(repository, selected, name, modpack, iconUrl) - .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(name)); + .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(new GameInstanceID(name))); } } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java index 2c7b5bed7d..f3e2ae15cb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java @@ -22,6 +22,7 @@ import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.download.RemoteVersion; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.task.Schedulers; @@ -63,9 +64,9 @@ private Task finishVersionDownloadingAsync(SettingsMap settings) { return builder.buildAsync().whenComplete(any -> { repository.refresh(); - repository.applyDefaultIsolationSetting(name); + repository.applyDefaultIsolationSetting(new GameInstanceID(name)); }) - .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(name)); + .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(new GameInstanceID(name))); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java index cc66712876..8172ce9570 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java @@ -19,6 +19,7 @@ import javafx.scene.Node; import org.jackhuang.hmcl.Metadata; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.ModpackExportInfo; @@ -48,10 +49,12 @@ public final class ExportWizardProvider implements WizardProvider { private final HMCLGameRepository repository; private final String version; + private final GameInstanceID instanceId; public ExportWizardProvider(HMCLGameRepository repository, String version) { this.repository = repository; this.version = version; + this.instanceId = new GameInstanceID(version); } @Override @@ -184,7 +187,7 @@ private Task exportAsMultiMC(ModpackExportInfo exportInfo, Path modpackFile) @Override public void execute() { - GameSettings.Effective setting = repository.getEffectiveGameSettings(version); + GameSettings.Effective setting = repository.getEffectiveGameSettings(instanceId); dependency = new MultiMCModpackExportTask(repository, version, exportInfo.getWhitelist(), new MultiMCInstanceConfiguration( "OneSix", diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java index 402f0fb50d..df0f83df4c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java @@ -35,6 +35,7 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.modpack.ModpackExportInfo; import org.jackhuang.hmcl.modpack.mcbbs.McbbsModpackManifest; @@ -68,6 +69,7 @@ public final class ModpackInfoPage extends Control implements WizardPage { private final HMCLGameRepository gameRepository; private final ModpackExportInfo.Options options; private final String versionName; + private final GameInstanceID instanceId; private final boolean canIncludeLauncher; private final ModpackExportInfo exportInfo = new ModpackExportInfo(); @@ -93,6 +95,7 @@ public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepos this.gameRepository = gameRepository; this.options = controller.getSettings().get(MODPACK_INFO_OPTION); this.versionName = version; + this.instanceId = new GameInstanceID(version); if (this.options == null) throw new IllegalArgumentException("Settings.MODPACK_INFO_OPTION is required"); @@ -100,7 +103,7 @@ public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepos name.set(version); author.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getProfileName).orElse("")); - GameSettings.Effective versionSetting = gameRepository.getEffectiveGameSettings(versionName); + GameSettings.Effective versionSetting = gameRepository.getEffectiveGameSettings(instanceId); minMemory.set(Optional.ofNullable(versionSetting.get(GameSettings::minMemoryProperty)).orElse(0)); launchArguments.set(versionSetting.get(GameSettings::gameArgumentsProperty)); javaArguments.set(versionSetting.get(GameSettings::jvmOptionsProperty)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java index 617fea6c58..86137876b0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java @@ -2448,12 +2448,13 @@ public void loadInstance(HMCLGameRepository repository, @Nullable String instanc assert isPresetSetting == (instanceId == null); if (instanceId != null) { - this.currentGameVersionNumber.set(GameVersionNumber.asGameVersion(repository.getGameVersion(instanceId))); + GameInstanceID loadedInstanceId = new GameInstanceID(instanceId); + this.currentGameVersionNumber.set(GameVersionNumber.asGameVersion(repository.getGameVersion(loadedInstanceId))); - @Nullable GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(instanceId); + @Nullable GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(loadedInstanceId); this.currentSetting.set((S) setting); setSettingsReadOnly( - setting == null || repository.isInstanceGameSettingsReadOnly(instanceId), + setting == null || repository.isInstanceGameSettingsReadOnly(loadedInstanceId), i18n("settings.game.instance_settings.unsupported"), setting != null ? this::forceOverwriteInstanceGameSettings : null); loadIcon(); @@ -2467,6 +2468,11 @@ public void loadInstance(HMCLGameRepository repository, @Nullable String instanc } } + /// Returns the loaded instance ID, or `null` when this page edits preset settings. + private @Nullable GameInstanceID getLoadedInstanceId() { + return instanceId == null ? null : new GameInstanceID(instanceId); + } + /// Updates the page read-only state used when settings cannot be saved safely. /// /// @param readOnly whether the current settings should be displayed read-only @@ -2506,12 +2512,13 @@ private void setSettingsReadOnly(boolean readOnly, String message, @Nullable Run /// Backs up and overwrites the current instance's `instance-game-settings.json`. private void forceOverwriteInstanceGameSettings() { - if (repository == null || instanceId == null) { + @Nullable GameInstanceID loadedInstanceId = getLoadedInstanceId(); + if (repository == null || loadedInstanceId == null) { return; } Controllers.confirmBackupAndOverwrite(i18n("settings.game.instance_settings.unsupported"), () -> { - repository.forceOverwriteInstanceGameSettings(instanceId); + repository.forceOverwriteInstanceGameSettings(loadedInstanceId); setSettingsReadOnly(false, ""); }); } @@ -2525,10 +2532,11 @@ private void forceOverwriteGameSettings() { } private void loadIcon() { - if (repository == null || instanceId == null) + @Nullable GameInstanceID loadedInstanceId = getLoadedInstanceId(); + if (repository == null || loadedInstanceId == null) return; - iconPickerItem.setImage(repository.getInstanceIconImage(instanceId)); + iconPickerItem.setImage(repository.getInstanceIconImage(loadedInstanceId)); } /// Refreshes Java selection controls and keeps inherited parent Java properties observed. @@ -2597,7 +2605,8 @@ private void initJavaSubtitle() { initializeSelectedJava(); JavaVersionType javaVersionType = setting.javaTypeProperty().getValue(); - GameSettings.Effective effectiveSetting = this.instanceId != null && repository != null ? repository.getEffectiveGameSettings(this.instanceId) : null; + @Nullable GameInstanceID loadedInstanceId = getLoadedInstanceId(); + GameSettings.Effective effectiveSetting = loadedInstanceId != null && repository != null ? repository.getEffectiveGameSettings(loadedInstanceId) : null; JavaVersionType effectiveJavaVersionType = effectiveSetting != null ? effectiveSetting.getInheritable(GameSettings::javaTypeProperty) : javaVersionType; boolean autoSelected = effectiveJavaVersionType == JavaVersionType.AUTO || effectiveJavaVersionType == JavaVersionType.VERSION; @@ -2618,7 +2627,7 @@ private void initJavaSubtitle() { if (this.instanceId == null) { version = null; } else { - version = repository != null ? repository.getResolvedInstanceManifest(this.instanceId) : null; + version = repository != null && loadedInstanceId != null ? repository.getResolvedInstanceManifest(loadedInstanceId).manifest() : null; } try { @@ -2646,11 +2655,12 @@ private void onExploreIcon() { } private void onDeleteIcon() { - if (repository == null || instanceId == null) + @Nullable GameInstanceID loadedInstanceId = getLoadedInstanceId(); + if (repository == null || loadedInstanceId == null) return; - repository.deleteIconFile(instanceId); - GameSettings.Instance localGameSettings = repository.getInstanceGameSettingsOrCreate(instanceId); + repository.deleteIconFile(loadedInstanceId); + GameSettings.Instance localGameSettings = repository.getInstanceGameSettingsOrCreate(loadedInstanceId); if (localGameSettings != null) { localGameSettings.iconProperty().setValue(VersionIconType.DEFAULT); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 25db012686..c4cf75dc8e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -44,6 +44,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.VersionList; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.DownloadProviders; @@ -210,7 +211,7 @@ public final class MainPage extends StackPane implements DecoratorPage { FXUtils.onScroll(launchPane, versions, list -> { String currentId = getCurrentGame(); return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); - }, it -> repository.setSelectedInstance(it.getId())); + }, it -> repository.setSelectedInstance(new GameInstanceID(it.getId()))); StackPane.setAlignment(launchPane, Pos.BOTTOM_RIGHT); { @@ -345,7 +346,7 @@ private void launchNoGame() { .whenComplete(any -> GameDirectoryManager.getSelectedRepository().refresh()) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { - GameDirectoryManager.getSelectedRepository().setSelectedInstance(gameVersionHolder.value); + GameDirectoryManager.getSelectedRepository().setSelectedInstance(new GameInstanceID(gameVersionHolder.value)); launch(); } else if (exception instanceof CancellationException) { Controllers.showToast(i18n("message.cancelled")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 2b5cfc9e57..df66965810 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -23,6 +23,7 @@ import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.RefreshedVersionsEvent; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ModpackHelper; @@ -164,7 +165,7 @@ protected Skin(RootPage control) { FXUtils.onScroll(gameListItem, getSkinnable().getMainPage().getVersions(), list -> { String currentId = getSkinnable().getMainPage().getCurrentGame(); return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); - }, it -> getSkinnable().getMainPage().getRepository().setSelectedInstance(it.getId())); + }, it -> getSkinnable().getMainPage().getRepository().setSelectedInstance(new GameInstanceID(it.getId()))); if (AnimationUtils.isAnimationEnabled()) { FXUtils.prepareOnMouseEnter(gameListItem, Controllers::prepareVersionPage); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java index 8e607b234b..0f497608a6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java @@ -25,6 +25,7 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.setting.*; import org.jackhuang.hmcl.terracotta.TerracottaMetadata; import org.jackhuang.hmcl.ui.Controllers; @@ -92,7 +93,7 @@ public TerracottaPage() { FXUtils.onScroll(item, mainPage.getVersions(), list -> { String currentId = mainPage.getCurrentGame(); return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); - }, it -> mainPage.getRepository().setSelectedInstance(it.getId())); + }, it -> mainPage.getRepository().setSelectedInstance(new GameInstanceID(it.getId()))); FXUtils.onSecondaryButtonClicked(item, () -> GameListPopupMenu.show(item, JFXPopup.PopupVPosition.BOTTOM, diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index e14cfa5eea..8e3589ac6a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -38,6 +38,7 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.*; import org.jackhuang.hmcl.download.DownloadProvider; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.addon.RemoteAddon; @@ -112,7 +113,7 @@ public ObservableList getActions() { @Override public void loadInstance(HMCLGameRepository repository, @Nullable String instanceId) { - this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, instanceId)); + this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, instanceId == null ? null : new GameInstanceID(instanceId))); setLoading(false); setFailed(false); @@ -166,12 +167,10 @@ private void search(String userGameVersion, RemoteAddonRepository.Category categ int currentSearchID = searchID = searchID + 1; Task.supplyAsync(() -> { HMCLGameRepository.InstanceReference instanceReference = this.instanceReference.get(); - if (StringUtils.isBlank(instanceReference.instanceId())) { + if (instanceReference.instanceId() == null) { return userGameVersion; } else { - return StringUtils.isNotBlank(instanceReference.instanceId()) - ? instanceReference.repository().getGameVersion(instanceReference.instanceId()).orElse("") - : ""; + return instanceReference.repository().getGameVersion(instanceReference.instanceId()).orElse(""); } }).thenApplyAsync( gameVersion -> repository.search(downloadProvider, gameVersion, category, pageOffset, 50, searchFilter, sort, RemoteAddonRepository.SortOrder.DESC) @@ -220,7 +219,8 @@ protected String getLocalizedOfficialPage() { protected HMCLGameRepository.InstanceReference getInstanceReference() { if (versionSelection) { - return new HMCLGameRepository.InstanceReference(instanceReference.get().repository(), selectedVersion.get()); + @Nullable String instanceId = selectedVersion.get(); + return new HMCLGameRepository.InstanceReference(instanceReference.get().repository(), instanceId == null ? null : new GameInstanceID(instanceId)); } else { return instanceReference.get(); } @@ -329,7 +329,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) { FXUtils.installFastTooltip(gameVersionField, i18n("search.enter")); FXUtils.onChangeAndOperate(getSkinnable().instanceReference, instanceReference -> { - if (StringUtils.isNotBlank(instanceReference.instanceId())) { + if (instanceReference.instanceId() != null) { GridPane.setColumnSpan(nameField, 3); } else { GridPane.setColumnSpan(nameField, 1); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index 03a27a3a6d..136d286357 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -31,6 +31,7 @@ import javafx.stage.FileChooser; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.addon.mod.ModLoaderType; @@ -271,7 +272,7 @@ protected DownloadPageSkin(DownloadPage control) { if (control.instanceReference.repository() != null && control.instanceReference.instanceId() != null) { HMCLGameRepository repository = control.instanceReference.repository(); - GameInstanceManifest game = repository.getResolvedPreservingPatchesManifest(control.instanceReference.instanceId()); + GameInstanceManifest game = repository.getResolvedPreservingPatchesInstanceManifest(control.instanceReference.instanceId()).manifest(); String gameVersion = repository.getGameVersion(game).orElse(null); if (gameVersion != null && control.versions.containsKey(gameVersion)) { @@ -615,6 +616,6 @@ private void loadDependencies(RemoteAddon.Version version, DownloadPage selfPage @FunctionalInterface public interface DownloadCallback { - void download(DownloadProvider downloadProvider, HMCLGameRepository repository, @Nullable String version, RemoteAddon addon, RemoteAddon.Version file); + void download(DownloadProvider downloadProvider, HMCLGameRepository repository, @Nullable GameInstanceID instanceId, RemoteAddon addon, RemoteAddon.Version file); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java index 9e691d68bb..4de9d3129c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java @@ -19,6 +19,7 @@ import javafx.geometry.Pos; import org.jackhuang.hmcl.event.Event; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.GameDirectoryManager; import org.jackhuang.hmcl.setting.VersionIconType; @@ -56,14 +57,21 @@ private void loadVersion(String version) { }); } } - if (version != null && repository != null && repository.hasInstance(version)) { - setTitle(i18n("version.manage.manage")); - setSubtitle(version); - imageContainer.setImage(repository.getInstanceIconImage(version)); - } else { - setTitle(i18n("version.empty")); - setSubtitle(i18n("version.empty.add")); - imageContainer.setImage(VersionIconType.DEFAULT.getIcon()); + if (version != null && repository != null) { + try { + GameInstanceID instanceId = new GameInstanceID(version); + if (repository.hasInstance(instanceId)) { + setTitle(i18n("version.manage.manage")); + setSubtitle(version); + imageContainer.setImage(repository.getInstanceIconImage(instanceId)); + return; + } + } catch (IllegalArgumentException ignored) { + } } + + setTitle(i18n("version.empty")); + setSubtitle(i18n("version.empty.add")); + imageContainer.setImage(VersionIconType.DEFAULT.getIcon()); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java index 7e3ef33a4a..1eaa211ff2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java @@ -20,6 +20,7 @@ import javafx.beans.property.*; import javafx.scene.image.Image; import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.setting.GameDirectory; @@ -44,6 +45,7 @@ public class GameItem { protected final HMCLGameRepository repository; protected final String id; + protected final GameInstanceID instanceId; private boolean initialized = false; private StringProperty title; @@ -54,6 +56,7 @@ public class GameItem { public GameItem(HMCLGameRepository repository, String id) { this.repository = repository; this.id = id; + this.instanceId = new GameInstanceID(id); } public GameDirectory getGameDirectory() { @@ -83,10 +86,10 @@ record Result(@Nullable String gameVersion, @Nullable String tag) { CompletableFuture.supplyAsync(() -> { // GameVersion.minecraftVersion() is a time-costing job (up to ~200 ms) - Optional gameVersion = repository.getGameVersion(id); + Optional gameVersion = repository.getGameVersion(instanceId); String modPackVersion = null; try { - ModpackConfiguration config = repository.readModpackConfiguration(id); + ModpackConfiguration config = repository.readModpackConfiguration(instanceId); modPackVersion = config != null ? config.getVersion() : null; } catch (IOException e) { LOG.warning("Failed to read modpack configuration from " + id, e); @@ -99,7 +102,7 @@ record Result(@Nullable String gameVersion, @Nullable String tag) { } StringBuilder libraries = new StringBuilder(Objects.requireNonNullElse(result.gameVersion, i18n("message.unknown"))); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(id), result.gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesInstanceManifest(instanceId).manifest(), result.gameVersion); for (LibraryAnalyzer.LibraryMark mark : analyzer) { String libraryId = mark.getLibraryId(); String libraryVersion = mark.getLibraryVersion(); @@ -118,7 +121,7 @@ record Result(@Nullable String gameVersion, @Nullable String tag) { }, Schedulers.javafx()); title.set(id); - image.set(repository.getInstanceIconImage(id)); + image.set(repository.getInstanceIconImage(instanceId)); } public ReadOnlyStringProperty titleProperty() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListCell.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListCell.java index 5cd185870a..98afdaf595 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListCell.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListCell.java @@ -29,6 +29,7 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.*; @@ -69,7 +70,7 @@ public void fire() { fireEvent(new ActionEvent()); GameListItem item = GameListCell.this.getItem(); if (item != null) { - item.getRepository().setSelectedInstance(item.getId()); + item.getRepository().setSelectedInstance(new GameInstanceID(item.getId())); } } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java index 19d35e6024..e674a1a788 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java @@ -33,6 +33,7 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.ui.FXUtils; @@ -53,7 +54,7 @@ public static void show(Node owner, JFXPopup.PopupVPosition vAlign, JFXPopup.Pop HMCLGameRepository repository, List versions) { GameListPopupMenu menu = new GameListPopupMenu(); menu.getItems().setAll(versions.stream() - .filter(it -> repository.hasInstance(it.getId())) + .filter(it -> repository.hasInstance(it.id())) .map(it -> new GameItem(repository, it.getId())) .toList()); JFXPopup popup = new JFXPopup(menu); @@ -127,7 +128,7 @@ public Cell(ListView listView) { FXUtils.onClicked(rootPane, () -> { GameItem item = getItem(); if (item != null) { - item.getRepository().setSelectedInstance(item.getId()); + item.getRepository().setSelectedInstance(new GameInstanceID(item.getId())); if (getScene().getWindow() instanceof JFXPopup popup) popup.hide(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java index de8e203a55..6078838c73 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java @@ -117,15 +117,16 @@ public static void downloadModpackImpl(DownloadProvider downloadProvider, HMCLGa } public static void deleteVersion(HMCLGameRepository repository, String version) { - boolean isIndependent = repository.getRunDirectory(version).toAbsolutePath().normalize() - .equals(repository.getInstanceRoot(version).toAbsolutePath().normalize()); + GameInstanceID instanceId = new GameInstanceID(version); + boolean isIndependent = repository.getRunDirectory(instanceId).toAbsolutePath().normalize() + .equals(repository.getInstanceRoot(instanceId).toAbsolutePath().normalize()); String message = isIndependent ? i18n("version.manage.remove.confirm.independent", version) : i18n("version.manage.remove.confirm.trash", version, version + "_removed"); JFXButton deleteButton = new JFXButton(i18n("button.delete")); deleteButton.getStyleClass().add("dialog-error"); deleteButton.setOnAction(e -> { - Task.supplyAsync(Schedulers.io(), () -> repository.removeInstanceFromDisk(version)) + Task.supplyAsync(Schedulers.io(), () -> repository.removeInstanceFromDisk(instanceId)) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception != null || !Boolean.TRUE.equals(result)) { Controllers.dialog(i18n("version.manage.remove.failed"), i18n("message.error"), MessageDialogPane.MessageType.ERROR); @@ -142,12 +143,14 @@ public static CompletableFuture renameVersion(HMCLGameRepository reposit handler.resolve(); return; } - if (repository.renameInstance(version, newName)) { + GameInstanceID oldInstanceId = new GameInstanceID(version); + GameInstanceID newInstanceId = new GameInstanceID(newName); + if (repository.renameInstance(oldInstanceId, newInstanceId)) { handler.resolve(); repository.refreshAsync() .thenRunAsync(Schedulers.javafx(), () -> { - if (repository.hasInstance(newName)) { - repository.setSelectedInstance(newName); + if (repository.hasInstance(newInstanceId)) { + repository.setSelectedInstance(newInstanceId); } }).start(); } else { @@ -163,7 +166,7 @@ public static void exportVersion(HMCLGameRepository repository, String version) } public static void openFolder(HMCLGameRepository repository, String version) { - FXUtils.openFolder(repository.getRunDirectory(version)); + FXUtils.openFolder(repository.getRunDirectory(new GameInstanceID(version))); } public static void installFromJson(HMCLGameRepository repository, Path file) { @@ -196,7 +199,7 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { .thenRunAsync(repository::refresh) .whenComplete(Schedulers.javafx(), (exception) -> { if (exception == null) { - repository.setSelectedInstance(result); + repository.setSelectedInstance(new GameInstanceID(result)); } else { Controllers.dialog( DownloadProviders.localizeErrorMessage(exception), i18n("install.failed"), MessageDialogPane.MessageType.ERROR); @@ -210,7 +213,7 @@ public static void duplicateInstance(HMCLGameRepository repository, String versi new PromptDialogPane.Builder(i18n("version.manage.duplicate.prompt"), (res, handler) -> { String newVersionName = ((PromptDialogPane.Builder.StringQuestion) res.get(1)).getValue(); boolean copySaves = ((PromptDialogPane.Builder.BooleanQuestion) res.get(2)).getValue(); - Task.runAsync(() -> repository.duplicateInstance(version, newVersionName, copySaves)) + Task.runAsync(() -> repository.duplicateInstance(new GameInstanceID(version), new GameInstanceID(newVersionName), copySaves)) .thenComposeAsync(repository.refreshAsync()) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { @@ -218,7 +221,7 @@ public static void duplicateInstance(HMCLGameRepository repository, String versi } else { handler.reject(StringUtils.getStackTrace(exception)); if (!repository.instanceIdConflicts(newVersionName)) { - repository.removeInstanceFromDisk(newVersionName); + repository.removeInstanceFromDisk(new GameInstanceID(newVersionName)); } } }).start(); @@ -235,7 +238,7 @@ public static void updateVersion(HMCLGameRepository repository, String version) } public static void updateGameAssets(HMCLGameRepository repository, String version) { - TaskExecutor executor = new GameAssetDownloadTask(repository.getDependency(), repository.getInstanceManifest(version), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true) + TaskExecutor executor = new GameAssetDownloadTask(repository.getDependency(), repository.getInstanceManifest(new GameInstanceID(version)), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true) .executor(); Controllers.taskDialog(executor, i18n("version.manage.redownload_assets_index"), TaskCancellationAction.NO_CANCEL); executor.start(); @@ -243,7 +246,7 @@ public static void updateGameAssets(HMCLGameRepository repository, String versio public static void cleanVersion(HMCLGameRepository repository, String id) { try { - repository.clean(id); + repository.clean(new GameInstanceID(id)); } catch (IOException e) { LOG.warning("Unable to clean game directory", e); } @@ -255,8 +258,9 @@ public static void generateLaunchScript(HMCLGameRepository repository, String id return; ensureSelectedAccount(account -> { FileChooser chooser = new FileChooser(); - if (Files.isDirectory(repository.getRunDirectory(id))) - chooser.setInitialDirectory(repository.getRunDirectory(id).toFile()); + GameInstanceID instanceId = new GameInstanceID(id); + if (Files.isDirectory(repository.getRunDirectory(instanceId))) + chooser.setInitialDirectory(repository.getRunDirectory(instanceId).toFile()); chooser.setTitle(i18n("version.launch_script.save")); if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { chooser.getExtensionFilters().add( @@ -326,7 +330,18 @@ public static void generateLaunchScriptForQuickEnterWorld(HMCLGameRepository rep } private static boolean checkVersionForLaunching(HMCLGameRepository repository, String id) { - if (id == null || !repository.isLoaded() || !repository.hasInstance(id)) { + boolean unavailable; + if (id == null || !repository.isLoaded()) { + unavailable = true; + } else { + try { + unavailable = !repository.hasInstance(new GameInstanceID(id)); + } catch (IllegalArgumentException e) { + unavailable = true; + } + } + + if (unavailable) { JFXButton gotoDownload = new JFXButton(i18n("version.empty.launch.goto_download")); gotoDownload.getStyleClass().add("dialog-accept"); gotoDownload.setOnAction(e -> Controllers.navigate(Controllers.getDownloadPage())); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java index 10ac770a36..04c4873a62 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java @@ -42,6 +42,7 @@ import org.jackhuang.hmcl.addon.mod.LocalModFile; import org.jackhuang.hmcl.addon.mod.ModLoaderType; import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.DownloadProviders; import org.jackhuang.hmcl.setting.VersionIconType; @@ -462,7 +463,7 @@ final class ModInfoDialog extends JFXDialogLayout { Controllers.navigate(new DownloadPage( repository instanceof CurseForgeRemoteAddonRepository ? HMCLLocalizedDownloadListPage.ofCurseForgeMod(null, false) : HMCLLocalizedDownloadListPage.ofModrinthMod(null, false), remoteAddon, - new HMCLGameRepository.InstanceReference(ModListPageSkin.this.getSkinnable().getRepository(), ModListPageSkin.this.getSkinnable().getInstanceId()), + new HMCLGameRepository.InstanceReference(ModListPageSkin.this.getSkinnable().getRepository(), new GameInstanceID(ModListPageSkin.this.getSkinnable().getInstanceId())), org.jackhuang.hmcl.ui.download.DownloadPage.FOR_MOD )); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java index 504cc0bfce..be8fc25dbc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java @@ -42,6 +42,7 @@ import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.addon.resourcepack.ResourcePackFile; import org.jackhuang.hmcl.addon.resourcepack.ResourcePackManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.SettingsManager; import org.jackhuang.hmcl.setting.DownloadProviders; @@ -619,7 +620,7 @@ private static final class ResourcePackInfoDialog extends JFXDialogLayout { ? HMCLLocalizedDownloadListPage.ofCurseForgeResourcePack(null, false) : HMCLLocalizedDownloadListPage.ofModrinthResourcePack(null, false), remoteAddon, - new HMCLGameRepository.InstanceReference(page.repository, page.instanceId), + new HMCLGameRepository.InstanceReference(page.repository, new GameInstanceID(page.instanceId)), org.jackhuang.hmcl.ui.download.DownloadPage.FOR_RESOURCE_PACK )); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index aaa7a3cff9..4f9ae01a9b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -22,6 +22,7 @@ import javafx.scene.layout.FlowPane; import javafx.stage.FileChooser; import org.jackhuang.hmcl.event.Event; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.GameSettings; import org.jackhuang.hmcl.setting.VersionIconType; @@ -40,15 +41,15 @@ public class VersionIconDialog extends DialogPane { private final HMCLGameRepository repository; - private final String versionId; + private final GameInstanceID instanceId; private final Runnable onFinish; private final GameSettings.Instance setting; public VersionIconDialog(HMCLGameRepository repository, String versionId, Runnable onFinish) { this.repository = repository; - this.versionId = versionId; + this.instanceId = new GameInstanceID(versionId); this.onFinish = onFinish; - this.setting = repository.getInstanceGameSettingsOrCreate(versionId); + this.setting = repository.getInstanceGameSettingsOrCreate(instanceId); setTitle(i18n("settings.icon")); FlowPane pane = new FlowPane(); @@ -79,7 +80,7 @@ private void exploreIcon() { Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (selectedFile != null) { try { - repository.setInstanceIconFile(versionId, selectedFile); + repository.setInstanceIconFile(instanceId, selectedFile); if (setting != null) { setting.iconProperty().setValue(VersionIconType.DEFAULT); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java index 921d52e502..b6cd199bc8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java @@ -29,6 +29,7 @@ import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.EventPriority; import org.jackhuang.hmcl.event.RefreshedVersionsEvent; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.GameSettings; import org.jackhuang.hmcl.task.Schedulers; @@ -112,7 +113,8 @@ private void checkSelectedVersion() { runInFX(() -> { if (this.instanceReference.get() == null) return; HMCLGameRepository repository = this.instanceReference.get().repository(); - if (!repository.hasInstance(this.instanceReference.get().instanceId())) { + @Nullable GameInstanceID instanceId = this.instanceReference.get().instanceId(); + if (instanceId == null || !repository.hasInstance(instanceId)) { if (preferredVersionName != null) { loadVersion(preferredVersionName, repository); } else { @@ -127,7 +129,8 @@ private Supplier loadVersionFor(Supplier nodeSupplier) { T node = nodeSupplier.get(); if (instanceReference.get() != null) { if (node instanceof VersionPage.GameInstanceLoadable) { - ((GameInstanceLoadable) node).loadInstance(instanceReference.get().repository(), instanceReference.get().instanceId()); + @Nullable GameInstanceID instanceId = instanceReference.get().instanceId(); + ((GameInstanceLoadable) node).loadInstance(instanceReference.get().repository(), instanceId == null ? null : instanceId.id()); } } return node; @@ -139,14 +142,14 @@ public void showInstanceSettings() { } public void setVersion(String version, HMCLGameRepository repository) { - this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, version)); + this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, new GameInstanceID(version))); } public void loadVersion(String version, HMCLGameRepository repository) { // If we jumped to game list page and deleted this version // and back to this page, we should return to main page. if (this.instanceReference.get() != null && (!getRepository().isLoaded() || - !getRepository().hasInstance(version))) { + !getRepository().hasInstance(new GameInstanceID(version)))) { Platform.runLater(() -> fireEvent(new PageCloseEvent())); return; } @@ -166,7 +169,7 @@ public void loadVersion(String version, HMCLGameRepository repository) { worldListTab.getNode().loadInstance(repository, version); if (schematicsTab.isInitialized()) schematicsTab.getNode().loadInstance(repository, version); - currentVersionUpgradable.set(repository.isModpack(version)); + currentVersionUpgradable.set(repository.isModpack(new GameInstanceID(version))); } private void onNavigated(Navigator.NavigationEvent event) { @@ -176,7 +179,7 @@ private void onNavigated(Navigator.NavigationEvent event) { // If we jumped to game list page and deleted this version // and back to this page, we should return to main page. if (!getRepository().isLoaded() || - !getRepository().hasInstance(getVersion())) { + !getRepository().hasInstance(new GameInstanceID(getVersion()))) { Platform.runLater(() -> fireEvent(new PageCloseEvent())); return; } @@ -185,7 +188,7 @@ private void onNavigated(Navigator.NavigationEvent event) { } private void onBrowse(String sub) { - FXUtils.openFolder(getRepository().getRunDirectory(getVersion()).resolve(sub)); + FXUtils.openFolder(getRepository().getRunDirectory(new GameInstanceID(getVersion())).resolve(sub)); } private void redownloadAssetIndex() { @@ -261,7 +264,10 @@ public HMCLGameRepository getRepository() { } public String getVersion() { - return Optional.ofNullable(instanceReference.get()).map(HMCLGameRepository.InstanceReference::instanceId).orElse(null); + return Optional.ofNullable(instanceReference.get()) + .map(HMCLGameRepository.InstanceReference::instanceId) + .map(GameInstanceID::id) + .orElse(null); } @Override diff --git a/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java b/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java index 115e43d235..8a8983fa69 100644 --- a/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java +++ b/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java @@ -25,6 +25,7 @@ import javafx.beans.property.ObjectProperty; import javafx.collections.ObservableList; import org.jackhuang.hmcl.Metadata; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.util.FileSaver; import org.jackhuang.hmcl.util.PortablePath; @@ -465,7 +466,8 @@ public void legacyInstanceSettingsMigrationStoresLegacyGameDirectoryPresetAsPare new GameDirectoryEnvironment(localDirectories, userDirectories, presets)) { settings().defaultGameSettingsPresetProperty().set(defaultPresetId); HMCLGameRepository repository = new HMCLGameRepository(gameDirectory); - Path versionRoot = repository.getInstanceRoot("1.20.1"); + GameInstanceID instanceId = new GameInstanceID("1.20.1"); + Path versionRoot = repository.getInstanceRoot(instanceId); Files.createDirectories(versionRoot); Files.writeString(versionRoot.resolve("hmclversion.cfg"), """ { @@ -473,7 +475,7 @@ public void legacyInstanceSettingsMigrationStoresLegacyGameDirectoryPresetAsPare } """); - GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettings("1.20.1")); + GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettings(instanceId)); assertEquals(legacyPresetId, setting.parentProperty().getValue()); } @@ -506,7 +508,8 @@ public void startupMigrationSkipsLegacyInstanceSettingsFile(@TempDir Path tempDi settings().defaultGameSettingsPresetProperty().set(defaultPresetId); HMCLGameRepository repository = new HMCLGameRepository(gameDirectory); writeVersionJson(repository, "1.20.1"); - Path versionRoot = repository.getInstanceRoot("1.20.1"); + GameInstanceID instanceId = new GameInstanceID("1.20.1"); + Path versionRoot = repository.getInstanceRoot(instanceId); Files.writeString(versionRoot.resolve(LegacyGameSettingsMigrator.LEGACY_INSTANCE_SETTINGS_FILENAME), """ { "usesGlobal": true @@ -515,9 +518,9 @@ public void startupMigrationSkipsLegacyInstanceSettingsFile(@TempDir Path tempDi LegacyConfigMigrator.migrateLegacyInstanceGameSettings(localDirectories, presets); - assertFalse(Files.exists(repository.getInstanceConfigDirectory("1.20.1") + assertFalse(Files.exists(repository.getInstanceConfigDirectory(instanceId) .resolve(LegacyGameSettingsMigrator.INSTANCE_GAME_SETTINGS_FILENAME))); - GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettings("1.20.1")); + GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettings(instanceId)); assertEquals(legacyPresetId, setting.parentProperty().getValue()); } } @@ -553,7 +556,7 @@ public void absentInstanceSettingsStoreLegacyGameDirectoryPresetAsParent(@TempDi writeVersionJson(repository, "1.20.1"); LegacyConfigMigrator.migrateLegacyInstanceGameSettings(localDirectories, presets); - GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettings("1.20.1")); + GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettings(new GameInstanceID("1.20.1"))); assertEquals(legacyPresetId, setting.parentProperty().getValue()); assertTrue(setting.getOverrideProperties().contains(GameSettings.PROPERTY_RUNNING_DIRECTORY)); @@ -591,7 +594,7 @@ public void newInstanceAfterMigrationDoesNotUseLegacyGameDirectoryParent(@TempDi LegacyConfigMigrator.migrateLegacyInstanceGameSettings(localDirectories, presets); writeVersionJson(repository, "1.20.1"); - assertNull(repository.getInstanceGameSettings("1.20.1")); + assertNull(repository.getInstanceGameSettings(new GameInstanceID("1.20.1"))); } } @@ -774,7 +777,7 @@ public void close() throws ReflectiveOperationException { /// Writes a minimal valid version json for repository refresh tests. private static void writeVersionJson(HMCLGameRepository repository, String id) throws IOException { - Path versionRoot = repository.getInstanceRoot(id); + Path versionRoot = repository.getInstanceRoot(new GameInstanceID(id)); Files.createDirectories(versionRoot); Files.writeString(versionRoot.resolve(id + ".json"), """ { From 7e6da03d6194950d0253497f9a2b473e0b461986 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 12:38:37 +0800 Subject: [PATCH 31/85] refactor: update version references to use GameInstanceID across multiple classes --- .../org/jackhuang/hmcl/game/HMCLGameLauncher.java | 2 +- .../org/jackhuang/hmcl/game/HMCLGameRepository.java | 12 ++++++------ .../org/jackhuang/hmcl/setting/LauncherSettings.java | 5 +++++ .../java/org/jackhuang/hmcl/ui/GameCrashWindow.java | 4 ++-- .../hmcl/ui/download/AdditionalInstallersPage.java | 2 +- .../java/org/jackhuang/hmcl/ui/main/MainPage.java | 10 +++++----- .../java/org/jackhuang/hmcl/ui/main/RootPage.java | 10 +++++----- .../jackhuang/hmcl/ui/terracotta/TerracottaPage.java | 6 +++--- 8 files changed, 28 insertions(+), 23 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java index 4bc4b24b96..e7023e852e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java @@ -66,7 +66,7 @@ private void generateOptionsTxt() { if (options.isDisableAutoGameOptions()) return; - Path runDir = repository.getRunDirectory(manifest.getId()); + Path runDir = repository.getRunDirectory(manifest.id()); Path optionsFile = runDir.resolve("options.txt"); Path configFolder = runDir.resolve("config"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 849be8ba10..2bc99ad0e1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -141,13 +141,13 @@ public void setSelectedInstance(@Nullable GameInstanceID instanceId) { /// Refreshes the selected instance ID after versions are loaded. public void refreshSelectedInstance() { - @Nullable String selectedInstance = settings().getSelectedInstance(gameDirectory.getId()); - @Nullable String refreshedInstance = selectedInstance; + @Nullable GameInstanceID selectedInstance = settings().getSelectedInstance2(gameDirectory.getId()); + @Nullable GameInstanceID refreshedInstance = selectedInstance; if (!hasInstance(refreshedInstance)) { - refreshedInstance = getInstanceManifests().isEmpty() ? null : getInstanceManifests().iterator().next().getId(); + refreshedInstance = getInstanceManifests().isEmpty() ? null : getInstanceManifests().iterator().next().id(); } if (!Objects.equals(selectedInstance, refreshedInstance)) { - setSelectedInstance(refreshedInstance == null ? null : new GameInstanceID(refreshedInstance)); + setSelectedInstance(refreshedInstance); } } @@ -205,7 +205,7 @@ public Stream getDisplayInstanceManifests() { return getInstanceManifests().stream() .filter(v -> !v.isHidden()) .sorted(Comparator.comparing((GameInstanceManifest v) -> Lang.requireNonNullElse(v.getReleaseTime(), Instant.EPOCH)) - .thenComparing(v -> VersionNumber.asVersion(v.getId()))); + .thenComparing(v -> VersionNumber.asVersion(v.id().id()))); } @Override @@ -828,7 +828,7 @@ public boolean instanceIdConflicts(String id) { if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { // on Windows, filenames are case-insensitive for (GameInstanceManifest manifest : getInstanceManifests()) { - String existingId = manifest.getId(); + String existingId = manifest.id().toString(); if (existingId.equalsIgnoreCase(id)) { return true; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java index 81028e6936..84f9ac8d7a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java @@ -34,6 +34,7 @@ import org.hildan.fxgson.factories.JavaFxPropertyTypeAdapterFactory; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.auth.AccountID; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.java.JavaRuntime; import org.jackhuang.hmcl.theme.BackgroundLoadPolicy; import org.jackhuang.hmcl.theme.BuiltinBackground; @@ -625,6 +626,10 @@ public ObservableMap getSelectedInstance() { return gameDirectoryId != null ? selectedInstance.get(gameDirectoryId) : null; } + public @Nullable GameInstanceID getSelectedInstance2(@Nullable GameDirectoryID gameDirectoryId) { + return gameDirectoryId != null ? new GameInstanceID(selectedInstance.get(gameDirectoryId)) : null; + } + /// Sets the selected instance ID for the given game directory ID. /// /// Blank values remove the persisted selected instance entry. diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java index 2e8d44308e..c968172ad3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java @@ -142,7 +142,7 @@ private void analyzeCrashReport() { return pair(CrashReportAnalyzer.analyze(rawLog), crashReport != null ? CrashReportAnalyzer.findKeywordsFromCrashReport(crashReport) : new HashSet<>()); }), Task.supplyAsync(() -> { - Path latestLog = repository.getRunDirectory(version.getId()).resolve("logs/latest.log"); + Path latestLog = repository.getRunDirectory(version.id()).resolve("logs/latest.log"); if (!Files.isReadable(latestLog)) { return pair(new HashSet(), new HashSet()); } @@ -345,7 +345,7 @@ private final class View extends VBox { TwoLineListItem version = new TwoLineListItem(); version.getStyleClass().setAll("two-line-item-second-large"); version.setTitle(i18n("game.version")); - version.setSubtitle(GameCrashWindow.this.version.getId()); + version.setSubtitle(GameCrashWindow.this.version.id().toString()); TwoLineListItem total_memory = new TwoLineListItem(); total_memory.getStyleClass().setAll("two-line-item-second-large"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java index c23a81bb2f..a394ea5a67 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java @@ -47,7 +47,7 @@ public AdditionalInstallersPage(String gameVersion, GameInstanceManifest version this.version = version; this.repository = repository; - txtName.setText(version.getId()); + txtName.setText(version.id().toString()); txtName.setEditable(false); for (InstallerItem library : group.getLibraries()) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index c4cf75dc8e..1044d50553 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -209,9 +209,9 @@ public final class MainPage extends StackPane implements DecoratorPage { HBox launchPane = new HBox(); launchPane.getStyleClass().add("launch-pane"); FXUtils.onScroll(launchPane, versions, list -> { - String currentId = getCurrentGame(); - return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); - }, it -> repository.setSelectedInstance(new GameInstanceID(it.getId()))); + GameInstanceID currentId = getCurrentGame(); + return Lang.indexWhere(list, instance -> instance.id().equals(currentId)); + }, it -> repository.setSelectedInstance(it.id())); StackPane.setAlignment(launchPane, Pos.BOTTOM_RIGHT); { @@ -386,8 +386,8 @@ public HMCLGameRepository getRepository() { return repository; } - public String getCurrentGame() { - return currentGame.get(); + public GameInstanceID getCurrentGame() { + return new GameInstanceID(currentGame.get()); } public StringProperty currentGameProperty() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index df66965810..85131721aa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -128,8 +128,8 @@ public MainPage getMainPage() { List children = repository.getInstanceManifests().parallelStream() .filter(version -> !version.isHidden()) .sorted(Comparator - .comparing((GameInstanceManifest version) -> Lang.requireNonNullElse(version.getReleaseTime(), Instant.EPOCH)) - .thenComparing(version -> VersionNumber.asVersion(repository.getGameVersion(version).orElse(version.getId())))) + .comparing((GameInstanceManifest manifest) -> Lang.requireNonNullElse(manifest.getReleaseTime(), Instant.EPOCH)) + .thenComparing(manifest -> VersionNumber.asVersion(repository.getGameVersion(manifest).orElse(manifest.id().toString())))) .collect(Collectors.toList()); runInFX(() -> { if (gameDirectory == GameDirectoryManager.getSelectedGameDirectory()) @@ -163,9 +163,9 @@ protected Skin(RootPage control) { } }); FXUtils.onScroll(gameListItem, getSkinnable().getMainPage().getVersions(), list -> { - String currentId = getSkinnable().getMainPage().getCurrentGame(); - return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); - }, it -> getSkinnable().getMainPage().getRepository().setSelectedInstance(new GameInstanceID(it.getId()))); + GameInstanceID currentId = getSkinnable().getMainPage().getCurrentGame(); + return Lang.indexWhere(list, instance -> instance.id().equals(currentId)); + }, it -> getSkinnable().getMainPage().getRepository().setSelectedInstance(it.id())); if (AnimationUtils.isAnimationEnabled()) { FXUtils.prepareOnMouseEnter(gameListItem, Controllers::prepareVersionPage); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java index 0f497608a6..4c304b53f7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java @@ -91,9 +91,9 @@ public TerracottaPage() { MainPage mainPage = Controllers.getRootPage().getMainPage(); FXUtils.onScroll(item, mainPage.getVersions(), list -> { - String currentId = mainPage.getCurrentGame(); - return Lang.indexWhere(list, instance -> instance.getId().equals(currentId)); - }, it -> mainPage.getRepository().setSelectedInstance(new GameInstanceID(it.getId()))); + GameInstanceID currentId = mainPage.getCurrentGame(); + return Lang.indexWhere(list, instance -> instance.id().equals(currentId)); + }, it -> mainPage.getRepository().setSelectedInstance(it.id())); FXUtils.onSecondaryButtonClicked(item, () -> GameListPopupMenu.show(item, JFXPopup.PopupVPosition.BOTTOM, From 480e2f88261cadac4c3bca4bfc41317ae508e368 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 12:41:07 +0800 Subject: [PATCH 32/85] refactor: update GameItem and GameListItem constructors to use GameInstanceID --- .../java/org/jackhuang/hmcl/game/HMCLGameRepository.java | 5 +++++ .../java/org/jackhuang/hmcl/ui/versions/GameItem.java | 6 +++--- .../org/jackhuang/hmcl/ui/versions/GameListItem.java | 9 +++++---- .../org/jackhuang/hmcl/ui/versions/GameListPage.java | 2 +- .../jackhuang/hmcl/ui/versions/GameListPopupMenu.java | 2 +- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 2bc99ad0e1..2c5a61cb48 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -134,6 +134,11 @@ public StringBinding selectedInstanceProperty() { return selectedInstance.get(); } + public @Nullable GameInstanceID getSelectedInstance2() { + return selectedInstance.get() == null ? null : new GameInstanceID(selectedInstance.get()); + } + + /// Sets the selected instance ID for this repository's game directory. public void setSelectedInstance(@Nullable GameInstanceID instanceId) { settings().setSelectedInstance(gameDirectory.getId(), instanceId == null ? null : instanceId.id()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java index 1eaa211ff2..1ca0caabf8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java @@ -53,10 +53,10 @@ public class GameItem { private StringProperty subtitle; private ObjectProperty image; - public GameItem(HMCLGameRepository repository, String id) { + public GameItem(HMCLGameRepository repository, GameInstanceID instanceId) { this.repository = repository; - this.id = id; - this.instanceId = new GameInstanceID(id); + this.id = instanceId.toString(); + this.instanceId = instanceId; } public GameDirectory getGameDirectory() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java index 77a0706609..4a68c62cab 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java @@ -21,6 +21,7 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.SimpleBooleanProperty; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.GameDirectoryManager; @@ -30,11 +31,11 @@ public class GameListItem extends GameItem { private final boolean isModpack; private final BooleanProperty selected = new SimpleBooleanProperty(this, "selected"); - public GameListItem(HMCLGameRepository repository, String id) { - super(repository, id); - this.isModpack = repository.isModpack(id); + public GameListItem(HMCLGameRepository repository, GameInstanceID instanceId) { + super(repository, instanceId); + this.isModpack = repository.isModpack(instanceId); selected.bind(Bindings.createBooleanBinding( - () -> repository.getGameDirectory() == GameDirectoryManager.getSelectedGameDirectory() && Objects.equals(repository.getSelectedInstance(), id), + () -> repository.getGameDirectory() == GameDirectoryManager.getSelectedGameDirectory() && Objects.equals(repository.getSelectedInstance2(), instanceId), GameDirectoryManager.selectedGameDirectoryProperty(), GameDirectoryManager.selectedInstanceProperty())); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java index 4ddbaeff7b..624cba4c7c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java @@ -152,7 +152,7 @@ private void loadVersions(HMCLGameRepository repository) { setLoading(true); setFailedReason(null); - List versionItems = repository.getDisplayInstanceManifests().map(instance -> new GameListItem(repository, instance.getId())).toList(); + List versionItems = repository.getDisplayInstanceManifests().map(instance -> new GameListItem(repository, instance.id())).toList(); sourceList.setAll(versionItems); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java index e674a1a788..cce882fd10 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java @@ -55,7 +55,7 @@ public static void show(Node owner, JFXPopup.PopupVPosition vAlign, JFXPopup.Pop GameListPopupMenu menu = new GameListPopupMenu(); menu.getItems().setAll(versions.stream() .filter(it -> repository.hasInstance(it.id())) - .map(it -> new GameItem(repository, it.getId())) + .map(it -> new GameItem(repository, it.id())) .toList()); JFXPopup popup = new JFXPopup(menu); popup.show(owner, vAlign, hAlign, initOffsetX, initOffsetY); From 08b3cea9413e51441ea1712559328c1797e5151d Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 13:02:37 +0800 Subject: [PATCH 33/85] refactor: update instance handling to use GameInstanceID across multiple classes --- .../jackhuang/hmcl/game/LauncherHelper.java | 6 +- .../org/jackhuang/hmcl/ui/Controllers.java | 2 +- .../ModpackInstallWizardProvider.java | 8 +- .../hmcl/ui/game/GameSettingsPage.java | 17 ++-- .../org/jackhuang/hmcl/ui/main/RootPage.java | 6 +- .../hmcl/ui/versions/DownloadListPage.java | 8 +- ...ialog.java => GameInstanceIconDialog.java} | 8 +- .../hmcl/ui/versions/GameListItem.java | 18 ++-- .../hmcl/ui/versions/InstallerListPage.java | 4 +- .../jackhuang/hmcl/ui/versions/Instances.java | 84 +++++++++---------- .../hmcl/ui/versions/ModListPage.java | 12 +-- .../hmcl/ui/versions/ModListPageSkin.java | 2 +- .../ui/versions/ResourcePackListPage.java | 2 +- .../hmcl/ui/versions/SchematicsPage.java | 3 +- .../hmcl/ui/versions/VersionPage.java | 79 +++++++++-------- .../hmcl/ui/versions/WorldListPage.java | 6 +- .../hmcl/ui/versions/WorldManagePage.java | 5 +- .../jackhuang/hmcl/util/NativePatcher.java | 2 +- .../hmcl/addon/LocalAddonManager.java | 11 +-- .../jackhuang/hmcl/addon/mod/ModManager.java | 7 +- .../resourcepack/ResourcePackManager.java | 13 +-- .../hmcl/game/DefaultGameRepository.java | 4 + 22 files changed, 159 insertions(+), 148 deletions(-) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/{VersionIconDialog.java => GameInstanceIconDialog.java} (95%) 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 007eba52f6..222d223ca7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -93,11 +93,11 @@ public final class LauncherHelper { private QuickPlayOption quickPlayOption; private boolean disableOfflineSkin = false; - public LauncherHelper(HMCLGameRepository repository, Account account, String selectedVersion) { + public LauncherHelper(HMCLGameRepository repository, Account account, GameInstanceID selectedInstanceId) { this.repository = Objects.requireNonNull(repository); this.account = Objects.requireNonNull(account); - this.selectedVersion = Objects.requireNonNull(selectedVersion); - this.selectedInstanceId = new GameInstanceID(selectedVersion); + this.selectedVersion = selectedInstanceId.toString(); + this.selectedInstanceId = selectedInstanceId; this.setting = repository.getEffectiveGameSettings(selectedInstanceId); this.launcherVisibility = setting.getInheritable(GameSettings::launcherVisibilityProperty); this.showLogs = setting.getInheritable(GameSettings::showLogsProperty); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index 6bad25b1c3..139b9581a4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -703,7 +703,7 @@ public static void onHyperlinkAction(String href) { break; case "hmcl://game/launch": var repository = GameDirectoryManager.getSelectedRepository(); - Instances.launch(repository, repository.getSelectedInstance(), LauncherHelper::setKeep); + Instances.launch(repository, repository.getSelectedInstance2(), LauncherHelper::setKeep); break; } } else { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index a059d66916..422f3a24a2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -58,14 +58,14 @@ public ModpackInstallWizardProvider(HMCLGameRepository repository, Path modpackF this(repository, modpackFile, null); } - public ModpackInstallWizardProvider(HMCLGameRepository repository, String updateVersion) { - this(repository, null, updateVersion); + public ModpackInstallWizardProvider(HMCLGameRepository repository, GameInstanceID updateInstanceId) { + this(repository, null, updateInstanceId); } - public ModpackInstallWizardProvider(HMCLGameRepository repository, Path modpackFile, String updateVersion) { + public ModpackInstallWizardProvider(HMCLGameRepository repository, Path modpackFile, GameInstanceID updateInstanceId) { this.repository = repository; this.file = modpackFile; - this.updateVersion = updateVersion; + this.updateVersion = updateInstanceId != null ? updateInstanceId.toString() : null; } public void setIconUrl(String iconUrl) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java index 86137876b0..e2071e9941 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java @@ -50,7 +50,7 @@ import org.jackhuang.hmcl.ui.*; import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.ui.versions.VersionIconDialog; +import org.jackhuang.hmcl.ui.versions.GameInstanceIconDialog; import org.jackhuang.hmcl.ui.versions.VersionPage; import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.Pair; @@ -97,7 +97,7 @@ public final class GameSettingsPage extends StackPane private @Nullable HMCLGameRepository repository; /// The current instance ID. - private @Nullable String instanceId; + private @Nullable GameInstanceID instanceId; /// The current setting. private final ObjectProperty<@Nullable S> currentSetting = new SimpleObjectProperty<>(this, "setting"); @@ -2440,7 +2440,7 @@ public ReadOnlyObjectProperty stateProperty() { @SuppressWarnings("unchecked") @Override - public void loadInstance(HMCLGameRepository repository, @Nullable String instanceId) { + public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { this.gameDirectory = repository.getGameDirectory(); this.repository = repository; this.instanceId = instanceId; @@ -2448,13 +2448,12 @@ public void loadInstance(HMCLGameRepository repository, @Nullable String instanc assert isPresetSetting == (instanceId == null); if (instanceId != null) { - GameInstanceID loadedInstanceId = new GameInstanceID(instanceId); - this.currentGameVersionNumber.set(GameVersionNumber.asGameVersion(repository.getGameVersion(loadedInstanceId))); + this.currentGameVersionNumber.set(GameVersionNumber.asGameVersion(repository.getGameVersion(instanceId))); - @Nullable GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(loadedInstanceId); + @Nullable GameSettings.Instance setting = repository.getInstanceGameSettingsOrCreate(instanceId); this.currentSetting.set((S) setting); setSettingsReadOnly( - setting == null || repository.isInstanceGameSettingsReadOnly(loadedInstanceId), + setting == null || repository.isInstanceGameSettingsReadOnly(instanceId), i18n("settings.game.instance_settings.unsupported"), setting != null ? this::forceOverwriteInstanceGameSettings : null); loadIcon(); @@ -2470,7 +2469,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable String instanc /// Returns the loaded instance ID, or `null` when this page edits preset settings. private @Nullable GameInstanceID getLoadedInstanceId() { - return instanceId == null ? null : new GameInstanceID(instanceId); + return instanceId == null ? null : instanceId; } /// Updates the page read-only state used when settings cannot be saved safely. @@ -2651,7 +2650,7 @@ private void onExploreIcon() { if (repository == null || instanceId == null) return; - Controllers.dialog(new VersionIconDialog(repository, instanceId, this::loadIcon)); + Controllers.dialog(new GameInstanceIconDialog(repository, instanceId, this::loadIcon)); } private void onDeleteIcon() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 85131721aa..c1359cc207 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -155,11 +155,11 @@ protected Skin(RootPage control) { // second item in left sidebar GameAdvancedListItem gameListItem = new GameAdvancedListItem(); gameListItem.setOnAction(e -> { - String version = GameDirectoryManager.getSelectedRepository().getSelectedInstance(); - if (version == null) { + GameInstanceID instanceId = GameDirectoryManager.getSelectedRepository().getSelectedInstance2(); + if (instanceId == null) { Controllers.navigate(Controllers.getGameListPage()); } else { - Instances.modifyGameSettings(GameDirectoryManager.getSelectedRepository(), version); + Instances.modifyGameSettings(GameDirectoryManager.getSelectedRepository(), instanceId); } }); FXUtils.onScroll(gameListItem, getSkinnable().getMainPage().getVersions(), list -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index 8e3589ac6a..960a07e85f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -112,8 +112,8 @@ public ObservableList getActions() { } @Override - public void loadInstance(HMCLGameRepository repository, @Nullable String instanceId) { - this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, instanceId == null ? null : new GameInstanceID(instanceId))); + public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { + this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, instanceId)); setLoading(false); setFailed(false); @@ -155,6 +155,10 @@ public void setLoading(boolean loading) { this.loading.set(loading); } + public void selectInstance(GameInstanceID instanceId) { + FXUtils.runInFX(() -> selectedVersion.set(instanceId.toString())); + } + public void selectVersion(String versionID) { FXUtils.runInFX(() -> selectedVersion.set(versionID)); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameInstanceIconDialog.java similarity index 95% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameInstanceIconDialog.java index 4f9ae01a9b..c5613c0e03 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameInstanceIconDialog.java @@ -39,17 +39,17 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class VersionIconDialog extends DialogPane { +public class GameInstanceIconDialog extends DialogPane { private final HMCLGameRepository repository; private final GameInstanceID instanceId; private final Runnable onFinish; private final GameSettings.Instance setting; - public VersionIconDialog(HMCLGameRepository repository, String versionId, Runnable onFinish) { + public GameInstanceIconDialog(HMCLGameRepository repository, GameInstanceID instanceId, Runnable onFinish) { this.repository = repository; - this.instanceId = new GameInstanceID(versionId); + this.instanceId = instanceId; this.onFinish = onFinish; - this.setting = repository.getInstanceGameSettingsOrCreate(instanceId); + this.setting = repository.getInstanceGameSettingsOrCreate(this.instanceId); setTitle(i18n("settings.icon")); FlowPane pane = new FlowPane(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java index 4a68c62cab..c0f6de1e0d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java @@ -45,19 +45,19 @@ public ReadOnlyBooleanProperty selectedProperty() { } public void rename() { - Instances.renameVersion(repository, id); + Instances.renameInstance(repository, instanceId); } public void duplicate() { - Instances.duplicateInstance(repository, id); + Instances.duplicateInstance(repository, instanceId); } public void remove() { - Instances.deleteVersion(repository, id); + Instances.deleteInstance(repository, id); } public void export() { - Instances.exportVersion(repository, id); + Instances.exportInstance(repository, id); } public void browse() { @@ -65,19 +65,19 @@ public void browse() { } public void testGame() { - Instances.testGame(repository, id); + Instances.testGame(repository, instanceId); } public void launch() { - Instances.launch(repository, id); + Instances.launch(repository, instanceId); } public void modifyGameSettings() { - Instances.modifyGameSettings(repository, id); + Instances.modifyGameSettings(repository, instanceId); } public void generateLaunchScript() { - Instances.generateLaunchScript(repository, id); + Instances.generateLaunchScript(repository, instanceId); } public boolean canUpdate() { @@ -85,6 +85,6 @@ public boolean canUpdate() { } public void update() { - Instances.updateVersion(repository, id); + Instances.updateInstance(repository, instanceId); } } 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 f5cb77df32..86ccf5f082 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 @@ -22,6 +22,7 @@ import javafx.scene.control.Skin; import javafx.stage.FileChooser; import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.task.Schedulers; @@ -32,6 +33,7 @@ import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider; import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jetbrains.annotations.Nullable; import java.nio.file.Path; import java.util.Arrays; @@ -61,7 +63,7 @@ protected Skin createDefaultSkin() { } @Override - public void loadInstance(HMCLGameRepository repository, String instanceId) { + public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { this.repository = repository; this.versionId = instanceId; this.version = repository.getInstanceManifest(instanceId); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java index 6078838c73..2176ebca9a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java @@ -79,7 +79,7 @@ public static void importModpack() { } } - public static void downloadModpackImpl(DownloadProvider downloadProvider, HMCLGameRepository repository, String version, RemoteAddon mod, RemoteAddon.Version file) { + public static void downloadModpackImpl(DownloadProvider downloadProvider, HMCLGameRepository repository, GameInstanceID instanceId, RemoteAddon mod, RemoteAddon.Version file) { Path modpack; List downloadURLs; try { @@ -96,8 +96,8 @@ public static void downloadModpackImpl(DownloadProvider downloadProvider, HMCLGa .whenComplete(Schedulers.javafx(), e -> { if (e == null) { ModpackInstallWizardProvider installWizardProvider; - if (version != null) - installWizardProvider = new ModpackInstallWizardProvider(repository, modpack, version); + if (instanceId != null) + installWizardProvider = new ModpackInstallWizardProvider(repository, modpack, instanceId); else installWizardProvider = new ModpackInstallWizardProvider(repository, modpack); if (StringUtils.isNotBlank(mod.iconUrl())) @@ -116,12 +116,11 @@ public static void downloadModpackImpl(DownloadProvider downloadProvider, HMCLGa ); } - public static void deleteVersion(HMCLGameRepository repository, String version) { - GameInstanceID instanceId = new GameInstanceID(version); + public static void deleteInstance(HMCLGameRepository repository, GameInstanceID instanceId) { boolean isIndependent = repository.getRunDirectory(instanceId).toAbsolutePath().normalize() .equals(repository.getInstanceRoot(instanceId).toAbsolutePath().normalize()); - String message = isIndependent ? i18n("version.manage.remove.confirm.independent", version) : - i18n("version.manage.remove.confirm.trash", version, version + "_removed"); + String message = isIndependent ? i18n("version.manage.remove.confirm.independent", instanceId) : + i18n("version.manage.remove.confirm.trash", instanceId, instanceId + "_removed"); JFXButton deleteButton = new JFXButton(i18n("button.delete")); deleteButton.getStyleClass().add("dialog-error"); @@ -137,13 +136,13 @@ public static void deleteVersion(HMCLGameRepository repository, String version) Controllers.confirmAction(message, i18n("message.warning"), MessageDialogPane.MessageType.WARNING, deleteButton); } - public static CompletableFuture renameVersion(HMCLGameRepository repository, String version) { + public static CompletableFuture renameInstance(HMCLGameRepository repository, GameInstanceID instanceId) { return Controllers.prompt(i18n("version.manage.rename.message"), (newName, handler) -> { - if (newName.equals(version)) { + if (newName.equals(instanceId.toString())) { handler.resolve(); return; } - GameInstanceID oldInstanceId = new GameInstanceID(version); + GameInstanceID oldInstanceId = instanceId; GameInstanceID newInstanceId = new GameInstanceID(newName); if (repository.renameInstance(oldInstanceId, newInstanceId)) { handler.resolve(); @@ -156,13 +155,13 @@ public static CompletableFuture renameVersion(HMCLGameRepository reposit } else { handler.reject(i18n("version.manage.rename.fail")); } - }, version, + }, instanceId.toString(), new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId), - new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName) || newVersionName.equals(version))); + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName) || newVersionName.equals(instanceId))); } - public static void exportVersion(HMCLGameRepository repository, String version) { - Controllers.getDecorator().startWizard(new ExportWizardProvider(repository, version), i18n("modpack.wizard")); + public static void exportInstance(HMCLGameRepository repository, GameInstanceID instanceId) { + Controllers.getDecorator().startWizard(new ExportWizardProvider(repository, instanceId.toString()), i18n("modpack.wizard")); // TODO } public static void openFolder(HMCLGameRepository repository, String version) { @@ -208,12 +207,12 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { }, FileUtils.getNameWithoutExtension(file), new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId), new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName))); } - public static void duplicateInstance(HMCLGameRepository repository, String version) { + public static void duplicateInstance(HMCLGameRepository repository, GameInstanceID instanceId) { Controllers.prompt( new PromptDialogPane.Builder(i18n("version.manage.duplicate.prompt"), (res, handler) -> { String newVersionName = ((PromptDialogPane.Builder.StringQuestion) res.get(1)).getValue(); boolean copySaves = ((PromptDialogPane.Builder.BooleanQuestion) res.get(2)).getValue(); - Task.runAsync(() -> repository.duplicateInstance(new GameInstanceID(version), new GameInstanceID(newVersionName), copySaves)) + Task.runAsync(() -> repository.duplicateInstance(instanceId, new GameInstanceID(newVersionName), copySaves)) .thenComposeAsync(repository.refreshAsync()) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { @@ -227,38 +226,37 @@ public static void duplicateInstance(HMCLGameRepository repository, String versi }).start(); }) .addQuestion(new PromptDialogPane.Builder.HintQuestion(i18n("version.manage.duplicate.confirm"))) - .addQuestion(new PromptDialogPane.Builder.StringQuestion(null, version, + .addQuestion(new PromptDialogPane.Builder.StringQuestion(null, instanceId.toString(), new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId), new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName)))) .addQuestion(new PromptDialogPane.Builder.BooleanQuestion(i18n("version.manage.duplicate.duplicate_save"), false))); } - public static void updateVersion(HMCLGameRepository repository, String version) { - Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(repository, version)); + public static void updateInstance(HMCLGameRepository repository, GameInstanceID instanceId) { + Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(repository, instanceId)); } - public static void updateGameAssets(HMCLGameRepository repository, String version) { - TaskExecutor executor = new GameAssetDownloadTask(repository.getDependency(), repository.getInstanceManifest(new GameInstanceID(version)), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true) + public static void updateGameAssets(HMCLGameRepository repository, GameInstanceID instanceId) { + TaskExecutor executor = new GameAssetDownloadTask(repository.getDependency(), repository.getInstanceManifest(instanceId), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true) .executor(); Controllers.taskDialog(executor, i18n("version.manage.redownload_assets_index"), TaskCancellationAction.NO_CANCEL); executor.start(); } - public static void cleanVersion(HMCLGameRepository repository, String id) { + public static void cleanInstance(HMCLGameRepository repository, GameInstanceID instanceId) { try { - repository.clean(new GameInstanceID(id)); + repository.clean(instanceId); } catch (IOException e) { LOG.warning("Unable to clean game directory", e); } } @SafeVarargs - public static void generateLaunchScript(HMCLGameRepository repository, String id, Consumer... injecters) { - if (!checkVersionForLaunching(repository, id)) + public static void generateLaunchScript(HMCLGameRepository repository, GameInstanceID instanceId, Consumer... injecters) { + if (!checkVersionForLaunching(repository, instanceId)) return; ensureSelectedAccount(account -> { FileChooser chooser = new FileChooser(); - GameInstanceID instanceId = new GameInstanceID(id); if (Files.isDirectory(repository.getRunDirectory(instanceId))) chooser.setInitialDirectory(repository.getRunDirectory(instanceId).toFile()); chooser.setTitle(i18n("version.launch_script.save")); @@ -278,7 +276,7 @@ public static void generateLaunchScript(HMCLGameRepository repository, String id file = file.resolveSibling(file.getFileName().toString() + "." + defaultExt); } - LauncherHelper launcherHelper = new LauncherHelper(repository, account, id); + LauncherHelper launcherHelper = new LauncherHelper(repository, account, instanceId); for (Consumer injecter : injecters) { injecter.accept(launcherHelper); } @@ -303,11 +301,11 @@ private static String getDefaultScriptExtension() { } @SafeVarargs - public static void launch(HMCLGameRepository repository, String id, Consumer... injecters) { - if (!checkVersionForLaunching(repository, id)) + public static void launch(HMCLGameRepository repository, GameInstanceID instanceId, Consumer... injecters) { + if (!checkVersionForLaunching(repository, instanceId)) return; ensureSelectedAccount(account -> { - LauncherHelper launcherHelper = new LauncherHelper(repository, account, id); + LauncherHelper launcherHelper = new LauncherHelper(repository, account, instanceId); for (Consumer injecter : injecters) { injecter.accept(launcherHelper); } @@ -315,30 +313,26 @@ public static void launch(HMCLGameRepository repository, String id, Consumer + public static void launchAndEnterWorld(HMCLGameRepository repository, GameInstanceID instanceId, String worldFolderName) { + launch(repository, instanceId, launcherHelper -> launcherHelper.setQuickPlayOption(new QuickPlayOption.SinglePlayer(worldFolderName))); } - public static void generateLaunchScriptForQuickEnterWorld(HMCLGameRepository repository, String id, String worldFolderName) { - generateLaunchScript(repository, id, launcherHelper -> + public static void generateLaunchScriptForQuickEnterWorld(HMCLGameRepository repository, GameInstanceID instanceId, String worldFolderName) { + generateLaunchScript(repository, instanceId, launcherHelper -> launcherHelper.setQuickPlayOption(new QuickPlayOption.SinglePlayer(worldFolderName))); } - private static boolean checkVersionForLaunching(HMCLGameRepository repository, String id) { + private static boolean checkVersionForLaunching(HMCLGameRepository repository, GameInstanceID instanceId) { boolean unavailable; - if (id == null || !repository.isLoaded()) { + if (instanceId == null || !repository.isLoaded()) { unavailable = true; } else { - try { - unavailable = !repository.hasInstance(new GameInstanceID(id)); - } catch (IllegalArgumentException e) { - unavailable = true; - } + unavailable = !repository.hasInstance(instanceId); } if (unavailable) { @@ -391,8 +385,8 @@ public static void modifyGlobalSettings(HMCLGameRepository repository) { Controllers.navigate(Controllers.getSettingsPage()); } - public static void modifyGameSettings(HMCLGameRepository repository, String version) { - Controllers.getVersionPage().setVersion(version, repository); + public static void modifyGameSettings(HMCLGameRepository repository, GameInstanceID instanceId) { + Controllers.getVersionPage().setInstance(instanceId, repository); // TODO Controllers.getVersionPage().showInstanceSettings(); // VersionPage.loadVersion will be invoked after navigation Controllers.navigate(Controllers.getVersionPage()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java index 8a31744082..89ef00528e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java @@ -23,6 +23,7 @@ import javafx.scene.control.Skin; import javafx.stage.FileChooser; import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.addon.mod.LocalModFile; @@ -39,6 +40,7 @@ import org.jackhuang.hmcl.ui.construct.PageAware; import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.UncheckedIOException; @@ -58,7 +60,7 @@ public final class ModListPage extends ListPageBase supportedLoaders = EnumSet.noneOf(ModLoaderType.class); @@ -86,11 +88,11 @@ public void refresh() { } @Override - public void loadInstance(HMCLGameRepository repository, String instanceId) { + public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { this.repository = repository; this.instanceId = instanceId; - GameInstanceManifest resolved = repository.getResolvedPreservingPatchesManifest(instanceId); + GameInstanceManifest resolved = repository.getResolvedPreservingPatchesInstanceManifest(instanceId).manifest(); this.gameVersion = repository.getGameVersion(resolved).orElse(null); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion); modded.set(analyzer.hasModLoader()); @@ -265,7 +267,7 @@ public void checkUpdates(Collection mods) { } public void download() { - Controllers.getDownloadPage().showModDownloads().selectVersion(instanceId); + Controllers.getDownloadPage().showModDownloads().selectInstance(instanceId); Controllers.navigate(Controllers.getDownloadPage()); } @@ -298,7 +300,7 @@ public HMCLGameRepository getRepository() { return this.repository; } - public String getInstanceId() { + public GameInstanceID getInstanceId() { return this.instanceId; } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java index 04c4873a62..603ecbfdcd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java @@ -463,7 +463,7 @@ final class ModInfoDialog extends JFXDialogLayout { Controllers.navigate(new DownloadPage( repository instanceof CurseForgeRemoteAddonRepository ? HMCLLocalizedDownloadListPage.ofCurseForgeMod(null, false) : HMCLLocalizedDownloadListPage.ofModrinthMod(null, false), remoteAddon, - new HMCLGameRepository.InstanceReference(ModListPageSkin.this.getSkinnable().getRepository(), new GameInstanceID(ModListPageSkin.this.getSkinnable().getInstanceId())), + new HMCLGameRepository.InstanceReference(ModListPageSkin.this.getSkinnable().getRepository(), ModListPageSkin.this.getSkinnable().getInstanceId()), org.jackhuang.hmcl.ui.download.DownloadPage.FOR_MOD )); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java index be8fc25dbc..cca720fe0b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java @@ -107,7 +107,7 @@ protected Skin createDefaultSkin() { } @Override - public void loadInstance(HMCLGameRepository repository, String instanceId) { + public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { this.repository = repository; this.instanceId = instanceId; this.resourcePackManager = new ResourcePackManager(repository, instanceId); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java index 241c47a435..04a38507a3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java @@ -34,6 +34,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.stage.FileChooser; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.schematic.LitematicFile; import org.jackhuang.hmcl.task.Schedulers; @@ -87,7 +88,7 @@ protected Skin createDefaultSkin() { } @Override - public void loadInstance(HMCLGameRepository repository, String instanceId) { + public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { this.schematicsDirectory = repository.getSchematicsDirectory(instanceId); refresh(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java index b6cd199bc8..64e61e05c6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java @@ -68,7 +68,7 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage private final ObjectProperty instanceReference = new SimpleObjectProperty<>(); private final WeakListenerHolder listenerHolder = new WeakListenerHolder(); - private String preferredVersionName = null; + private GameInstanceID preferredInstanceId = null; public static class WorkingDirChangedEvent extends Event { public static final EventType EVENT_TYPE = new EventType<>(Event.ANY, "WORKING_DIR_CHANGED"); @@ -94,15 +94,15 @@ public VersionPage() { addEventHandler(WorkingDirChangedEvent.EVENT_TYPE, event -> { if (this.instanceReference.get() != null) { if (installerListTab.isInitialized()) - installerListTab.getNode().loadInstance(getRepository(), getVersion()); + installerListTab.getNode().loadInstance(getRepository(), getInstanceId()); if (modListTab.isInitialized()) - modListTab.getNode().loadInstance(getRepository(), getVersion()); + modListTab.getNode().loadInstance(getRepository(), getInstanceId()); if (resourcePackTab.isInitialized()) - resourcePackTab.getNode().loadInstance(getRepository(), getVersion()); + resourcePackTab.getNode().loadInstance(getRepository(), getInstanceId()); if (worldListTab.isInitialized()) - worldListTab.getNode().loadInstance(getRepository(), getVersion()); + worldListTab.getNode().loadInstance(getRepository(), getInstanceId()); if (schematicsTab.isInitialized()) - schematicsTab.getNode().loadInstance(getRepository(), getVersion()); + schematicsTab.getNode().loadInstance(getRepository(), getInstanceId()); } }); @@ -115,8 +115,8 @@ private void checkSelectedVersion() { HMCLGameRepository repository = this.instanceReference.get().repository(); @Nullable GameInstanceID instanceId = this.instanceReference.get().instanceId(); if (instanceId == null || !repository.hasInstance(instanceId)) { - if (preferredVersionName != null) { - loadVersion(preferredVersionName, repository); + if (preferredInstanceId != null) { + loadInstance(preferredInstanceId, repository); } else { fireEvent(new PageCloseEvent()); } @@ -128,9 +128,9 @@ private Supplier loadVersionFor(Supplier nodeSupplier) { return () -> { T node = nodeSupplier.get(); if (instanceReference.get() != null) { - if (node instanceof VersionPage.GameInstanceLoadable) { + if (node instanceof VersionPage.GameInstanceLoadable loadable) { @Nullable GameInstanceID instanceId = instanceReference.get().instanceId(); - ((GameInstanceLoadable) node).loadInstance(instanceReference.get().repository(), instanceId == null ? null : instanceId.id()); + loadable.loadInstance(instanceReference.get().repository(), instanceId); } } return node; @@ -141,35 +141,35 @@ public void showInstanceSettings() { tab.select(versionSettingsTab, false); } - public void setVersion(String version, HMCLGameRepository repository) { - this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, new GameInstanceID(version))); + public void setInstance(GameInstanceID version, HMCLGameRepository repository) { + this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, version)); } - public void loadVersion(String version, HMCLGameRepository repository) { + public void loadInstance(GameInstanceID instanceId, HMCLGameRepository repository) { // If we jumped to game list page and deleted this version // and back to this page, we should return to main page. if (this.instanceReference.get() != null && (!getRepository().isLoaded() || - !getRepository().hasInstance(new GameInstanceID(version)))) { + !getRepository().hasInstance(instanceId))) { Platform.runLater(() -> fireEvent(new PageCloseEvent())); return; } - setVersion(version, repository); - preferredVersionName = version; + setInstance(instanceId, repository); + preferredInstanceId = instanceId; if (versionSettingsTab.isInitialized()) - versionSettingsTab.getNode().loadInstance(repository, version); + versionSettingsTab.getNode().loadInstance(repository, instanceId); if (installerListTab.isInitialized()) - installerListTab.getNode().loadInstance(repository, version); + installerListTab.getNode().loadInstance(repository, instanceId); if (modListTab.isInitialized()) - modListTab.getNode().loadInstance(repository, version); + modListTab.getNode().loadInstance(repository, instanceId); if (resourcePackTab.isInitialized()) - resourcePackTab.getNode().loadInstance(repository, version); + resourcePackTab.getNode().loadInstance(repository, instanceId); if (worldListTab.isInitialized()) - worldListTab.getNode().loadInstance(repository, version); + worldListTab.getNode().loadInstance(repository, instanceId); if (schematicsTab.isInitialized()) - schematicsTab.getNode().loadInstance(repository, version); - currentVersionUpgradable.set(repository.isModpack(new GameInstanceID(version))); + schematicsTab.getNode().loadInstance(repository, instanceId); + currentVersionUpgradable.set(repository.isModpack(instanceId)); } private void onNavigated(Navigator.NavigationEvent event) { @@ -179,20 +179,20 @@ private void onNavigated(Navigator.NavigationEvent event) { // If we jumped to game list page and deleted this version // and back to this page, we should return to main page. if (!getRepository().isLoaded() || - !getRepository().hasInstance(new GameInstanceID(getVersion()))) { + !getRepository().hasInstance(getInstanceId())) { Platform.runLater(() -> fireEvent(new PageCloseEvent())); return; } - loadVersion(getVersion(), getRepository()); + loadInstance(getInstanceId(), getRepository()); } private void onBrowse(String sub) { - FXUtils.openFolder(getRepository().getRunDirectory(new GameInstanceID(getVersion())).resolve(sub)); + FXUtils.openFolder(getRepository().getRunDirectory(getInstanceId()).resolve(sub)); } private void redownloadAssetIndex() { - Instances.updateGameAssets(getRepository(), getVersion()); + Instances.updateGameAssets(getRepository(), getInstanceId()); } private void clearLibraries() { @@ -227,46 +227,45 @@ private void clearAssets() { } private void clearJunkFiles() { - Instances.cleanVersion(getRepository(), getVersion()); + Instances.cleanInstance(getRepository(), getInstanceId()); } private void testGame() { - Instances.testGame(getRepository(), getVersion()); + Instances.testGame(getRepository(), getInstanceId()); } private void updateGame() { - Instances.updateVersion(getRepository(), getVersion()); + Instances.updateInstance(getRepository(), getInstanceId()); } private void generateLaunchScript() { - Instances.generateLaunchScript(getRepository(), getVersion()); + Instances.generateLaunchScript(getRepository(), getInstanceId()); } private void export() { - Instances.exportVersion(getRepository(), getVersion()); + Instances.exportInstance(getRepository(), getInstanceId()); } private void rename() { - Instances.renameVersion(getRepository(), getVersion()) - .thenApply(newVersionName -> this.preferredVersionName = newVersionName); + Instances.renameInstance(getRepository(), getInstanceId()) + .thenApply(newVersionName -> this.preferredInstanceId = new GameInstanceID(newVersionName)); } private void remove() { - Instances.deleteVersion(getRepository(), getVersion()); + Instances.deleteInstance(getRepository(), getInstanceId()); } private void duplicate() { - Instances.duplicateInstance(getRepository(), getVersion()); + Instances.duplicateInstance(getRepository(), getInstanceId()); } public HMCLGameRepository getRepository() { return Optional.ofNullable(instanceReference.get()).map(HMCLGameRepository.InstanceReference::repository).orElse(null); } - public String getVersion() { + public @Nullable GameInstanceID getInstanceId() { return Optional.ofNullable(instanceReference.get()) .map(HMCLGameRepository.InstanceReference::instanceId) - .map(GameInstanceID::id) .orElse(null); } @@ -350,7 +349,7 @@ protected Skin(VersionPage control) { } control.state.bind(Bindings.createObjectBinding(() -> - State.fromTitle(i18n("version.manage.manage.title", getSkinnable().getVersion()), -1), + State.fromTitle(i18n("version.manage.manage.title", getSkinnable().getInstanceId()), -1), getSkinnable().instanceReference)); //control.transitionPane.getStyleClass().add("gray-background"); @@ -365,6 +364,6 @@ public interface GameInstanceLoadable { /// /// @param repository the repository containing the game instance /// @param instanceId the game instance ID, or `null` when only repository context is available - void loadInstance(HMCLGameRepository repository, @Nullable String instanceId); + void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java index dbbc4b472e..a9ab987e0c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java @@ -35,6 +35,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.stage.FileChooser; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.World; import org.jackhuang.hmcl.task.Schedulers; @@ -45,6 +46,7 @@ import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.FileAlreadyExistsException; @@ -67,7 +69,7 @@ public final class WorldListPage extends ListPageBase implements VersionP private Path savesDir; private List worlds; private HMCLGameRepository repository; - private String instanceId; + private GameInstanceID instanceId; private final BooleanProperty supportQuickPlay = new SimpleBooleanProperty(this, "supportQuickPlay", false); private int refreshCount = 0; @@ -86,7 +88,7 @@ protected Skin createDefaultSkin() { } @Override - public void loadInstance(HMCLGameRepository repository, String instanceId) { + public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { this.repository = repository; this.instanceId = instanceId; this.savesDir = repository.getSavesDirectory(instanceId); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java index b04c0cce21..211ad1f541 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java @@ -24,6 +24,7 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.World; import org.jackhuang.hmcl.ui.Controllers; @@ -54,7 +55,7 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco private final World world; private final Path backupsDir; private final HMCLGameRepository repository; - private final String instanceId; + private final GameInstanceID instanceId; private final boolean supportQuickPlay; private FileChannel sessionLockChannel; @@ -69,7 +70,7 @@ public final class WorldManagePage extends DecoratorAnimatedPage implements Deco private final TabHeader.Tab worldBackupsTab = new TabHeader.Tab<>("worldBackupsPage"); private final TabHeader.Tab dataPackTab = new TabHeader.Tab<>("dataPackListPage"); - public WorldManagePage(World world, HMCLGameRepository repository, String instanceId) { + public WorldManagePage(World world, HMCLGameRepository repository, GameInstanceID instanceId) { this.world = world; this.backupsDir = repository.getBackupsDirectory(instanceId); this.repository = repository; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java index ddc4561405..29dd6f0378 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java @@ -172,7 +172,7 @@ public static GameInstanceManifest patchNative(DefaultGameRepository repository, } if (lwjglVersionChanged) { - ModManager modManager = repository.getModManager(version.getId()); + ModManager modManager = repository.getModManager(version.id()); try { for (LocalModFile mod : modManager.getLocalFiles()) { if ("sodium".equals(mod.getId())) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java index 0520b67a8f..0946df626f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonManager.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.addon; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.FileUtils; @@ -47,19 +48,19 @@ public static String getLocalAddonName(Path file) { protected final Set<@NotNull T> localFiles = new LinkedHashSet<>(); protected final GameRepository repository; - protected final String id; + protected final GameInstanceID instanceId; - public LocalAddonManager(GameRepository gameRepository, String versionId) { + public LocalAddonManager(GameRepository gameRepository, GameInstanceID instanceId) { this.repository = gameRepository; - this.id = versionId; + this.instanceId = instanceId; } public GameRepository getRepository() { return repository; } - public String getInstanceId() { - return id; + public GameInstanceID getInstanceId() { + return instanceId; } public abstract Path getDirectory(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java index 7fa070c630..51cd090f3c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java @@ -22,6 +22,7 @@ import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.addon.meta.*; import org.jackhuang.hmcl.download.LibraryAnalyzer; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.NoSuchGameInstanceException; import org.jackhuang.hmcl.util.Pair; @@ -69,13 +70,13 @@ private interface ModMetadataReader { private boolean loaded = false; - public ModManager(GameRepository repository, String id) { + public ModManager(GameRepository repository, GameInstanceID id) { super(repository, id); } @Override public Path getDirectory() { - return repository.getModsDirectory(id); + return repository.getModsDirectory(instanceId); } public LibraryAnalyzer getLibraryAnalyzer() { @@ -180,7 +181,7 @@ public void refresh() throws IOException { localMods.clear(); try { - analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedPreservingPatchesManifest(id), null); + analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedPreservingPatchesManifest(instanceId), null); } catch (NoSuchGameInstanceException e) { throw new IOException(e); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java index c804bdd81a..8c1e93d0dc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackManager.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.addon.resourcepack; import com.google.gson.annotations.SerializedName; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.addon.meta.PackMcMeta; @@ -218,10 +219,10 @@ private static List deserializePackList(String json) { private boolean loaded = false; - public ResourcePackManager(GameRepository repository, String id) { - super(repository, id); - this.resourcePackDirectory = this.repository.getResourcePackDirectory(this.id); - this.optionsFile = repository.getRunDirectory(id).resolve("options.txt"); + public ResourcePackManager(GameRepository repository, GameInstanceID instanceId) { + super(repository, instanceId); + this.resourcePackDirectory = this.repository.getResourcePackDirectory(this.instanceId); + this.optionsFile = repository.getRunDirectory(instanceId).resolve("options.txt"); } @NotNull @@ -262,7 +263,7 @@ public GameVersionNumber getMinecraftVersion() { lock.lock(); try { if (minecraftVersion == null) { - minecraftVersion = GameVersionNumber.asGameVersion(repository.getGameVersion(id)); + minecraftVersion = GameVersionNumber.asGameVersion(repository.getGameVersion(instanceId)); supportsNewOptionsFormat = isMcVersionSupportsNewOptionsFormat(minecraftVersion); } } finally { @@ -277,7 +278,7 @@ public PackMcMeta.PackVersion getRequiredVersion() { if (requiredVersion == null) { lock.lock(); try { - if (requiredVersion == null) requiredVersion = getPackVersion(getMinecraftVersion(), repository.getInstanceJar(id)); + if (requiredVersion == null) requiredVersion = getPackVersion(getMinecraftVersion(), repository.getInstanceJar(instanceId)); } finally { lock.unlock(); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 0a61e842f1..e01600024e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -646,6 +646,10 @@ public Path getSchematicsDirectory(String instanceId) { return getSchematicsDirectory(new GameInstanceID(instanceId)); } + public ModManager getModManager(GameInstanceID instanceId) { + return new ModManager(this, instanceId); + } + public ModManager getModManager(String instanceId) { return new ModManager(this, instanceId); } From e05d149992b35c55b0dfd5a919e3b4c30c06531a Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 13:39:32 +0800 Subject: [PATCH 34/85] refactor: update instance handling to use GameInstanceID in various classes --- .../hmcl/ui/download/DownloadPage.java | 4 +-- .../ui/download/ModpackSelectionPage.java | 3 +- .../org/jackhuang/hmcl/ui/main/MainPage.java | 2 +- .../terracotta/TerracottaControllerPage.java | 2 +- .../hmcl/ui/terracotta/TerracottaPage.java | 2 +- .../hmcl/ui/versions/DownloadListPage.java | 28 ++++++++----------- .../hmcl/ui/versions/GameListItem.java | 6 ++-- .../hmcl/ui/versions/InstallerListPage.java | 12 ++++---- .../jackhuang/hmcl/ui/versions/Instances.java | 4 +-- .../ui/versions/ResourcePackListPage.java | 6 ++-- .../jackhuang/hmcl/addon/mod/ModManager.java | 2 +- .../hmcl/game/DefaultGameRepository.java | 4 +-- 12 files changed, 36 insertions(+), 39 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index 94e9eebb83..da0003e8c2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -93,12 +93,12 @@ public DownloadPage() { this(null); } - public DownloadPage(String uploadVersion) { + public DownloadPage(GameInstanceID uploadInstance) { newGameTab.setNodeSupplier(loadVersionFor(() -> new VersionsPage(versionPageNavigator, i18n("install.installer.choose", i18n("install.installer.game")), "", DownloadProviders.getDownloadProvider(), "game", versionPageNavigator::onGameSelected))); modpackTab.setNodeSupplier(loadVersionFor(() -> { DownloadListPage page = HMCLLocalizedDownloadListPage.ofModPack((downloadProvider, repository, __, modpack, file) -> { - Instances.downloadModpackImpl(downloadProvider, repository, uploadVersion, modpack, file); + Instances.downloadModpackImpl(downloadProvider, repository, uploadInstance, modpack, file); }, false); JFXButton installLocalModpackButton = FXUtils.newRaisedButton(i18n("install.modpack")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java index dc19d5ae39..a014be7803 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java @@ -27,6 +27,7 @@ import javafx.scene.layout.VBox; import javafx.scene.shape.SVGPath; import javafx.stage.FileChooser; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.modpack.server.ServerModpackManifest; import org.jackhuang.hmcl.task.FileDownloadTask; @@ -166,7 +167,7 @@ private void onChooseRemoteFile() { public void onChooseRepository() { String modPackName = controller.getSettings().get(MODPACK_NAME); - DownloadPage downloadPage = new DownloadPage(modPackName); + DownloadPage downloadPage = new DownloadPage(new GameInstanceID(modPackName)); downloadPage.showModpackDownloads(); Controllers.navigate(downloadPage); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 1044d50553..e2ce16356b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -318,7 +318,7 @@ private void doAnimation(boolean show) { private void launch() { HMCLGameRepository repository = GameDirectoryManager.getSelectedRepository(); - Instances.launch(repository, repository.getSelectedInstance()); + Instances.launch(repository, repository.getSelectedInstance2()); } private void launchNoGame() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java index 2b558e8dba..33fb7663ef 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java @@ -212,7 +212,7 @@ public TerracottaControllerPage() { MessageDialogPane.MessageType.QUESTION ).addAction(i18n("version.launch"), () -> { var repository = GameDirectoryManager.getSelectedRepository(); - Instances.launch(repository, repository.getSelectedInstance(), launcherHelper -> { + Instances.launch(repository, repository.getSelectedInstance2(), launcherHelper -> { launcherHelper.setKeep(); launcherHelper.setDisableOfflineSkin(); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java index 4c304b53f7..533461b75a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java @@ -80,7 +80,7 @@ public TerracottaPage() { .add(accountListItem) .addNavigationDrawerItem(i18n("version.launch"), SVG.ROCKET_LAUNCH, () -> { var repository = GameDirectoryManager.getSelectedRepository(); - Instances.launch(repository, repository.getSelectedInstance(), launcherHelper -> { + Instances.launch(repository, repository.getSelectedInstance2(), launcherHelper -> { launcherHelper.setKeep(); launcherHelper.setDisableOfflineSkin(); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index 960a07e85f..036e7e1ca4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -77,8 +77,8 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP private final IntegerProperty pageOffset = new SimpleIntegerProperty(0); private final IntegerProperty pageCount = new SimpleIntegerProperty(-1); private final ListProperty items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList()); - private final ObservableList versions = FXCollections.observableArrayList(); - private final StringProperty selectedVersion = new SimpleStringProperty(); + private final ObservableList instances = FXCollections.observableArrayList(); + private final ObjectProperty selectedInstance = new SimpleObjectProperty<>(); private final DownloadPage.DownloadCallback callback; private boolean searchInitialized = false; protected final BooleanProperty supportChinese = new SimpleBooleanProperty(); @@ -124,10 +124,10 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID } if (versionSelection) { - versions.setAll(repository.getDisplayInstanceManifests() - .map(GameInstanceManifest::getId) - .collect(Collectors.toList())); - selectedVersion.set(repository.getSelectedInstance()); + instances.setAll(repository.getDisplayInstanceManifests() + .map(GameInstanceManifest::id) + .toList()); + selectedInstance.set(repository.getSelectedInstance2()); } } @@ -156,11 +156,7 @@ public void setLoading(boolean loading) { } public void selectInstance(GameInstanceID instanceId) { - FXUtils.runInFX(() -> selectedVersion.set(instanceId.toString())); - } - - public void selectVersion(String versionID) { - FXUtils.runInFX(() -> selectedVersion.set(versionID)); + FXUtils.runInFX(() -> selectedInstance.set(instanceId)); } private void search(String userGameVersion, RemoteAddonRepository.Category category, int pageOffset, String searchFilter, RemoteAddonRepository.SortType sort) { @@ -223,8 +219,8 @@ protected String getLocalizedOfficialPage() { protected HMCLGameRepository.InstanceReference getInstanceReference() { if (versionSelection) { - @Nullable String instanceId = selectedVersion.get(); - return new HMCLGameRepository.InstanceReference(instanceReference.get().repository(), instanceId == null ? null : new GameInstanceID(instanceId)); + @Nullable GameInstanceID instanceId = selectedInstance.get(); + return new HMCLGameRepository.InstanceReference(instanceReference.get().repository(), instanceId); } else { return instanceReference.get(); } @@ -282,10 +278,10 @@ protected ModDownloadListPageSkin(DownloadListPage control) { int columns = 0; Node lastNode = null; if (control.versionSelection) { - JFXComboBox versionsComboBox = new JFXComboBox<>(); + JFXComboBox versionsComboBox = new JFXComboBox<>(); versionsComboBox.setMaxWidth(Double.MAX_VALUE); - Bindings.bindContent(versionsComboBox.getItems(), control.versions); - selectedItemPropertyFor(versionsComboBox).bindBidirectional(control.selectedVersion); + Bindings.bindContent(versionsComboBox.getItems(), control.instances); + selectedItemPropertyFor(versionsComboBox).bindBidirectional(control.selectedInstance); searchPane.add(new Label(i18n("version")), columns++, rowIndex); searchPane.add(lastNode = versionsComboBox, columns++, rowIndex); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java index c0f6de1e0d..1a715a17ed 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java @@ -53,15 +53,15 @@ public void duplicate() { } public void remove() { - Instances.deleteInstance(repository, id); + Instances.deleteInstance(repository, instanceId); } public void export() { - Instances.exportInstance(repository, id); + Instances.exportInstance(repository, instanceId); } public void browse() { - Instances.openFolder(repository, id); + Instances.openFolder(repository, instanceId); } public void testGame() { 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 86ccf5f082..6a8126354a 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 @@ -46,7 +46,7 @@ public class InstallerListPage extends ListPageBase implements VersionPage.GameInstanceLoadable { private HMCLGameRepository repository; - private String versionId; + private GameInstanceID instanceId; private GameInstanceManifest version; private String gameVersion; @@ -65,14 +65,14 @@ protected Skin createDefaultSkin() { @Override public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { this.repository = repository; - this.versionId = instanceId; + this.instanceId = instanceId; this.version = repository.getInstanceManifest(instanceId); this.gameVersion = null; CompletableFuture.supplyAsync(() -> { gameVersion = repository.getGameVersion(version).orElse(null); - return LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); + return LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesInstanceManifest(instanceId).manifest(), gameVersion); }).thenAcceptAsync(analyzer -> { itemsProperty().clear(); @@ -106,7 +106,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID item.setOnRemove(() -> repository.getDependency().removeLibraryAsync(version, libraryId) .thenComposeAsync(repository::saveAsync) .withComposeAsync(repository.refreshAsync()) - .withRunAsync(Schedulers.javafx(), () -> loadInstance(this.repository, this.versionId)) + .withRunAsync(Schedulers.javafx(), () -> loadInstance(this.repository, this.instanceId)) .start()); itemsProperty().add(item); @@ -128,7 +128,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID installerItem.setOnRemove(() -> repository.getDependency().removeLibraryAsync(version, libraryId) .thenComposeAsync(repository::saveAsync) .withComposeAsync(repository.refreshAsync()) - .withRunAsync(Schedulers.javafx(), () -> loadInstance(this.repository, this.versionId)) + .withRunAsync(Schedulers.javafx(), () -> loadInstance(this.repository, this.instanceId)) .start()); itemsProperty().add(installerItem); @@ -153,7 +153,7 @@ private void doInstallOffline(Path file) { public void onStop(boolean success, TaskExecutor executor) { runInFX(() -> { if (success) { - loadInstance(repository, versionId); + loadInstance(repository, instanceId); Controllers.dialog(i18n("install.success")); } else { if (executor.getException() == null) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java index 2176ebca9a..0f3efcc979 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java @@ -164,8 +164,8 @@ public static void exportInstance(HMCLGameRepository repository, GameInstanceID Controllers.getDecorator().startWizard(new ExportWizardProvider(repository, instanceId.toString()), i18n("modpack.wizard")); // TODO } - public static void openFolder(HMCLGameRepository repository, String version) { - FXUtils.openFolder(repository.getRunDirectory(new GameInstanceID(version))); + public static void openFolder(HMCLGameRepository repository, GameInstanceID instanceId) { + FXUtils.openFolder(repository.getRunDirectory(instanceId)); } public static void installFromJson(HMCLGameRepository repository, Path file) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java index cca720fe0b..aec26b544a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java @@ -90,7 +90,7 @@ public final class ResourcePackListPage extends ListPageBase Date: Mon, 6 Jul 2026 13:42:27 +0800 Subject: [PATCH 35/85] refactor: update instance handling to use GameInstanceID in DefaultDependencyManager, DefaultLauncher, and various task classes --- .../jackhuang/hmcl/ui/versions/Instances.java | 2 +- .../download/DefaultDependencyManager.java | 2 +- .../download/fabric/FabricAPIInstallTask.java | 2 +- .../hmcl/download/game/GameInstallTask.java | 2 +- .../LegacyFabricAPIInstallTask.java | 2 +- .../download/quilt/QuiltAPIInstallTask.java | 2 +- .../hmcl/game/GameInstanceManifest.java | 7 ------ .../hmcl/launch/DefaultLauncher.java | 24 +++++++++---------- 8 files changed, 18 insertions(+), 25 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java index 0f3efcc979..fff3fdb993 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java @@ -386,7 +386,7 @@ public static void modifyGlobalSettings(HMCLGameRepository repository) { } public static void modifyGameSettings(HMCLGameRepository repository, GameInstanceID instanceId) { - Controllers.getVersionPage().setInstance(instanceId, repository); // TODO + Controllers.getVersionPage().setInstance(instanceId, repository); Controllers.getVersionPage().showInstanceSettings(); // VersionPage.loadVersion will be invoked after navigation Controllers.navigate(Controllers.getVersionPage()); 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 9335a48925..8008667c6d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -107,7 +107,7 @@ public Task checkPatchCompletionAsync(GameInstanceManifest version, boolean i String gameVersion = repository.getGameVersion(version).orElse(null); if (gameVersion == null) return null; - GameInstanceManifest original = repository.getInstanceManifest(version.getId()); + GameInstanceManifest original = repository.getInstanceManifest(version.id()); GameInstanceManifest resolved = original.resolvePreservingPatches(repository); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java index a6a622abd9..105623aa8a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java @@ -60,7 +60,7 @@ public boolean isRelyingOnDependencies() { public void execute() throws IOException { dependencies.add(new FileDownloadTask( remote.getVersion().file().url(), - dependencyManager.getGameRepository().getModsDirectory(version.getId()).resolve("fabric-api-" + remote.getVersion().version() + ".jar"), + dependencyManager.getGameRepository().getModsDirectory(version.id()).resolve("fabric-api-" + remote.getVersion().version() + ".jar"), remote.getVersion().file().getIntegrityCheck()) ); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java index d097931dee..9a8e0561ce 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java @@ -72,7 +72,7 @@ public void execute() throws Exception { GameInstancePatch.PRIORITY_MC).setJar(null); setResult(patch); - GameInstanceManifest version = new GameInstanceManifest(this.version.getId()).addPatch(patch); + GameInstanceManifest version = new GameInstanceManifest(this.version.id()).addPatch(patch); dependencies.add(Task.allOf( new GameDownloadTask(dependencyManager, remote.getGameVersion(), version), Task.allOf( diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java index 556d9743a0..d831788c44 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java @@ -55,7 +55,7 @@ public boolean isRelyingOnDependencies() { public void execute() throws IOException { dependencies.add(new FileDownloadTask( remote.getVersion().file().url(), - dependencyManager.getGameRepository().getModsDirectory(version.getId()).resolve("legacy-fabric-api-" + remote.getVersion().version() + ".jar"), + dependencyManager.getGameRepository().getModsDirectory(version.id()).resolve("legacy-fabric-api-" + remote.getVersion().version() + ".jar"), remote.getVersion().file().getIntegrityCheck()) ); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java index 1380181e8e..5def4a4690 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java @@ -60,7 +60,7 @@ public boolean isRelyingOnDependencies() { public void execute() throws IOException { dependencies.add(new FileDownloadTask( remote.getVersion().file().url(), - dependencyManager.getGameRepository().getModsDirectory(version.getId()).resolve("quilt-api-" + remote.getVersion().version() + ".jar"), + dependencyManager.getGameRepository().getModsDirectory(version.id()).resolve("quilt-api-" + remote.getVersion().version() + ".jar"), remote.getVersion().file().getIntegrityCheck()) ); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 187b9cecde..b1a5add53a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -329,13 +329,6 @@ public Boolean root() { return root != null && root; } - /// Returns the instance id as a string. - /// - /// @return the instance id string - public String getId() { - return id.id(); - } - /// Returns legacy game arguments. /// /// @return the legacy game arguments diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index af61cedd28..56d80227bf 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -352,7 +352,7 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { } if (manifest.getMainClass() == null) { - throw new IllegalStateException("Main class is null for instance " + manifest.getId()); + throw new IllegalStateException("Main class is null for instance " + manifest.id()); } res.add(manifest.getMainClass()); @@ -494,7 +494,7 @@ private boolean isUsingLog4j() { } public Path getLog4jConfigurationFile() { - return repository.getInstanceRoot(manifest.getId()).resolve("log4j2.xml"); + return repository.getInstanceRoot(manifest.id()).resolve("log4j2.xml"); } public void extractLog4jConfigurationFile() throws IOException { @@ -528,10 +528,10 @@ protected Map getConfigurations() { pair("${auth_session}", authInfo.getAccessToken()), pair("${auth_access_token}", authInfo.getAccessToken()), pair("${auth_uuid}", UUIDs.toCompactString(authInfo.getUUID())), - pair("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(manifest.getId())), + pair("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(manifest.id().toString())), pair("${profile_name}", Optional.ofNullable(options.getProfileName()).orElse("Minecraft")), pair("${version_type}", Optional.ofNullable(options.getVersionType()).orElse(manifest.getType().getId())), - pair("${game_directory}", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId()))), + pair("${game_directory}", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.id()))), pair("${user_type}", authInfo.getUserType()), pair("${assets_index_name}", manifest.getAssetIndex().getId()), pair("${user_properties}", authInfo.getUserProperties()), @@ -556,7 +556,7 @@ protected Map getConfigurations() { /// Returns the native library directory selected by the launch options. private Path getNativeFolder() { if (StringUtils.isBlank(options.getNativesDir())) { - return repository.getNativeDirectory(manifest.getId(), options.getJava().getPlatform()); + return repository.getNativeDirectory(manifest.id(), options.getJava().getPlatform()); } return Path.of(options.getNativesDir()); @@ -587,7 +587,7 @@ public ManagedProcess launch() throws IOException, InterruptedException { if (isUsingLog4j()) extractLog4jConfigurationFile(); - Path runDirectory = repository.getRunDirectory(manifest.getId()); + Path runDirectory = repository.getRunDirectory(manifest.id()); if (StringUtils.isNotBlank(options.getPreLaunchCommand())) { ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPreLaunchCommand(), getEnvVars(nativeFolder))).directory(runDirectory.toFile()); @@ -617,13 +617,13 @@ public ManagedProcess launch() throws IOException, InterruptedException { } private Map getEnvVars(Path nativeFolder) { - String versionName = Optional.ofNullable(options.getVersionName()).orElse(manifest.getId()); + String versionName = Optional.ofNullable(options.getVersionName()).orElse(manifest.id().toString()); Map env = new LinkedHashMap<>(); env.put("INST_NAME", versionName); env.put("INST_ID", versionName); - env.put("INST_DIR", FileUtils.getAbsolutePath(repository.getInstanceRoot(manifest.getId()))); - env.put("INST_MC_DIR", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId()))); + env.put("INST_DIR", FileUtils.getAbsolutePath(repository.getInstanceRoot(manifest.id()))); + env.put("INST_MC_DIR", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.id()))); env.put("INST_JAVA", options.getJava().getBinary().toString()); if (options.getRenderer() instanceof Renderer.Driver driver) { @@ -782,7 +782,7 @@ else if (!isWindows && !(scriptExtension.equalsIgnoreCase("sh") || scriptExtensi writer.newLine(); } writer.write("Set-Location -LiteralPath "); - writer.write(CommandBuilder.pwshString(FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId())))); + writer.write(CommandBuilder.pwshString(FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.id())))); writer.newLine(); @@ -826,7 +826,7 @@ else if (!isWindows && !(scriptExtension.equalsIgnoreCase("sh") || scriptExtensi writer.newLine(); } writer.newLine(); - writer.write(new CommandBuilder().addAll("cd", "/D", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId()))).toString()); + writer.write(new CommandBuilder().addAll("cd", "/D", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.id()))).toString()); } else { writer.write("#!/usr/bin/env bash"); writer.newLine(); @@ -838,7 +838,7 @@ else if (!isWindows && !(scriptExtension.equalsIgnoreCase("sh") || scriptExtensi writer.write(new CommandBuilder().addAll("ln", "-s", FileUtils.getAbsolutePath(nativeFolder), commandLine.tempNativeFolder.toString()).toString()); writer.newLine(); } - writer.write(new CommandBuilder().addAll("cd", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.getId()))).toString()); + writer.write(new CommandBuilder().addAll("cd", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.id()))).toString()); } writer.newLine(); if (StringUtils.isNotBlank(options.getPreLaunchCommand())) { From e6a68f0baf979db0cab8ce42999cc5434bc03873 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 13:49:07 +0800 Subject: [PATCH 36/85] refactor: update ExportWizardProvider and related tasks to use GameInstanceID instead of version strings --- .../hmcl/ui/export/ExportWizardProvider.java | 18 +++++++-------- .../ui/export/ModpackFileSelectionPage.java | 15 ++++++------ .../hmcl/ui/export/ModpackInfoPage.java | 16 ++++++------- .../jackhuang/hmcl/ui/versions/Instances.java | 2 +- .../jackhuang/hmcl/game/GameRepository.java | 12 ++-------- .../modpack/mcbbs/McbbsModpackExportTask.java | 19 +++++++-------- .../modrinth/ModrinthModpackExportTask.java | 23 ++++++++++--------- .../multimc/MultiMCModpackExportTask.java | 21 +++++++++-------- .../server/ServerModpackExportTask.java | 19 +++++++-------- 9 files changed, 69 insertions(+), 76 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java index 8172ce9570..28d08dadc2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java @@ -48,13 +48,11 @@ public final class ExportWizardProvider implements WizardProvider { private final HMCLGameRepository repository; - private final String version; private final GameInstanceID instanceId; - public ExportWizardProvider(HMCLGameRepository repository, String version) { + public ExportWizardProvider(HMCLGameRepository repository, GameInstanceID instanceId) { this.repository = repository; - this.version = version; - this.instanceId = new GameInstanceID(version); + this.instanceId = instanceId; } @Override @@ -167,7 +165,7 @@ private Task exportAsMcbbs(ModpackExportInfo exportInfo, Path modpackFile) { @Override public void execute() { - dependency = new McbbsModpackExportTask(repository, version, exportInfo, modpackFile); + dependency = new McbbsModpackExportTask(repository, instanceId, exportInfo, modpackFile); } @Override @@ -188,7 +186,7 @@ private Task exportAsMultiMC(ModpackExportInfo exportInfo, Path modpackFile) @Override public void execute() { GameSettings.Effective setting = repository.getEffectiveGameSettings(instanceId); - dependency = new MultiMCModpackExportTask(repository, version, exportInfo.getWhitelist(), + dependency = new MultiMCModpackExportTask(repository, instanceId, exportInfo.getWhitelist(), new MultiMCInstanceConfiguration( "OneSix", exportInfo.getName() + "-" + exportInfo.getVersion(), @@ -235,7 +233,7 @@ private Task exportAsServer(ModpackExportInfo exportInfo, Path modpackFile) { @Override public void execute() { - dependency = new ServerModpackExportTask(repository, version, exportInfo, modpackFile); + dependency = new ServerModpackExportTask(repository, instanceId, exportInfo, modpackFile); } @Override @@ -257,7 +255,7 @@ private Task exportAsModrinth(ModpackExportInfo exportInfo, Path modpackFile) public void execute() { dependency = new ModrinthModpackExportTask( repository, - version, + instanceId, exportInfo, modpackFile ); @@ -274,8 +272,8 @@ public Collection> getDependencies() { public Node createPage(WizardController controller, int step, SettingsMap settings) { return switch (step) { case 0 -> new ModpackTypeSelectionPage(controller); - case 1 -> new ModpackInfoPage(controller, repository, version); - case 2 -> new ModpackFileSelectionPage(controller, repository, version, ModAdviser::suggestMod); + case 1 -> new ModpackInfoPage(controller, repository, instanceId); + case 2 -> new ModpackFileSelectionPage(controller, repository, instanceId, ModAdviser::suggestMod); default -> throw new IllegalArgumentException("step"); }; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index 3feb91781b..5d9d3832a0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -27,6 +27,7 @@ import javafx.scene.control.TreeItem; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.ui.FXUtils; @@ -56,17 +57,17 @@ */ public final class ModpackFileSelectionPage extends BorderPane implements WizardPage { private final WizardController controller; - private final String version; + private final GameInstanceID instanceId; private final ModAdviser adviser; private final ModpackFileTreeItem rootNode; - public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository repository, String version, ModAdviser adviser) { + public ModpackFileSelectionPage(WizardController controller, HMCLGameRepository repository, GameInstanceID instanceId, ModAdviser adviser) { this.controller = controller; - this.version = version; + this.instanceId = instanceId; this.adviser = adviser; JFXTreeView treeView = new JFXTreeView<>(); - rootNode = getTreeItem(repository.getRunDirectory(version), "minecraft", 0); + rootNode = getTreeItem(repository.getRunDirectory(instanceId), "minecraft", 0); treeView.setRoot(rootNode); treeView.setSelectionModel(new NoneMultipleSelectionModel<>()); onEscPressed(treeView, () -> controller.onPrev(true)); @@ -107,12 +108,12 @@ private ModpackFileTreeItem getTreeItem(Path file, String basePath, int level) { } if (fileName.startsWith("._")) // macOS system file state = ModAdviser.ModSuggestion.HIDDEN; - if (FileUtils.getNameWithoutExtension(file).equals(version)) + if (FileUtils.getNameWithoutExtension(file).equals(instanceId)) state = ModAdviser.ModSuggestion.HIDDEN; } if (isDirectory) { - if (fileName.equals(version + "-natives")) { // Ignore -natives + if (fileName.equals(instanceId + "-natives")) { // Ignore -natives state = ModAdviser.ModSuggestion.HIDDEN; } if (level == 1 && fileName.startsWith("natives-")) { // Ignore natives-os-arch @@ -123,7 +124,7 @@ private ModpackFileTreeItem getTreeItem(Path file, String basePath, int level) { return null; } - ModpackFileTreeItem node = new ModpackFileTreeItem(level == 0 ? version : StringUtils.substringAfterLast(basePath, '/'), basePath); + ModpackFileTreeItem node = new ModpackFileTreeItem(level == 0 ? instanceId.toString() : StringUtils.substringAfterLast(basePath, '/'), basePath); if (state == ModAdviser.ModSuggestion.SUGGESTED) node.setSelected(true); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java index df0f83df4c..421518be86 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java @@ -66,9 +66,8 @@ public final class ModpackInfoPage extends Control implements WizardPage { private final WizardController controller; - private final HMCLGameRepository gameRepository; + private final HMCLGameRepository repository; private final ModpackExportInfo.Options options; - private final String versionName; private final GameInstanceID instanceId; private final boolean canIncludeLauncher; @@ -90,20 +89,19 @@ public final class ModpackInfoPage extends Control implements WizardPage { private final SimpleBooleanProperty noCreateRemoteFiles = new SimpleBooleanProperty(); private final SimpleBooleanProperty skipCurseForgeRemoteFiles = new SimpleBooleanProperty(); - public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepository, String version) { + public ModpackInfoPage(WizardController controller, HMCLGameRepository repository, GameInstanceID instanceId) { this.controller = controller; - this.gameRepository = gameRepository; + this.repository = repository; this.options = controller.getSettings().get(MODPACK_INFO_OPTION); - this.versionName = version; - this.instanceId = new GameInstanceID(version); + this.instanceId = instanceId; if (this.options == null) throw new IllegalArgumentException("Settings.MODPACK_INFO_OPTION is required"); - name.set(version); + name.set(instanceId.toString()); author.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getProfileName).orElse("")); - GameSettings.Effective versionSetting = gameRepository.getEffectiveGameSettings(instanceId); + GameSettings.Effective versionSetting = repository.getEffectiveGameSettings(this.instanceId); minMemory.set(Optional.ofNullable(versionSetting.get(GameSettings::minMemoryProperty)).orElse(0)); launchArguments.set(versionSetting.get(GameSettings::gameArgumentsProperty)); javaArguments.set(versionSetting.get(GameSettings::jvmOptionsProperty)); @@ -216,7 +214,7 @@ public ModpackInfoPageSkin(ModpackInfoPage skinnable) { var instanceNamePane = new LineTextPane(); { instanceNamePane.setTitle(i18n("modpack.wizard.step.initialization.exported_version")); - instanceNamePane.setText(skinnable.versionName); + instanceNamePane.setText(skinnable.instanceId.toString()); list.getContent().add(instanceNamePane); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java index fff3fdb993..82127cb06c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java @@ -161,7 +161,7 @@ public static CompletableFuture renameInstance(HMCLGameRepository reposi } public static void exportInstance(HMCLGameRepository repository, GameInstanceID instanceId) { - Controllers.getDecorator().startWizard(new ExportWizardProvider(repository, instanceId.toString()), i18n("modpack.wizard")); // TODO + Controllers.getDecorator().startWizard(new ExportWizardProvider(repository, instanceId), i18n("modpack.wizard")); } public static void openFolder(HMCLGameRepository repository, GameInstanceID instanceId) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index a4670e38ad..9c44f180b0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -92,14 +92,6 @@ default GameInstanceManifest getInstanceManifest(String id) throws NoSuchGameIns /// @return the resolved manifest view GameInstanceManifest.Resolved getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; - /// Returns a resolved instance manifest by string id. - /// - /// @param id the instance id string - /// @return the resolved manifest - default GameInstanceManifest getResolvedInstanceManifest(String id) throws NoSuchGameInstanceException { - return getResolvedInstanceManifest(new GameInstanceID(id)).manifest(); - } - /// Returns a standalone manifest view for the instance. /// /// @param instanceId the instance id @@ -112,8 +104,8 @@ default GameInstanceManifest.Standalone getResolvedPreservingPatchesInstanceMani /// /// @param id the instance id string /// @return the standalone manifest - default GameInstanceManifest getResolvedPreservingPatchesManifest(String id) throws NoSuchGameInstanceException { - return getResolvedPreservingPatchesInstanceManifest(new GameInstanceID(id)).manifest(); + default GameInstanceManifest getResolvedPreservingPatchesManifest(GameInstanceID id) throws NoSuchGameInstanceException { + return getResolvedPreservingPatchesInstanceManifest(id).manifest(); } /// Returns the number of loaded instances. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java index 60396dc654..217ca8d4ba 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java @@ -19,6 +19,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; @@ -45,13 +46,13 @@ public class McbbsModpackExportTask extends Task { private final DefaultGameRepository repository; - private final String version; + private final GameInstanceID instanceId; private final ModpackExportInfo info; private final Path modpackFile; - public McbbsModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo info, Path modpackFile) { + public McbbsModpackExportTask(DefaultGameRepository repository, GameInstanceID instanceId, ModpackExportInfo info, Path modpackFile) { this.repository = repository; - this.version = version; + this.instanceId = instanceId; this.info = info.validate(); this.modpackFile = modpackFile; @@ -69,11 +70,11 @@ public McbbsModpackExportTask(DefaultGameRepository repository, String version, @Override public void execute() throws Exception { ArrayList blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); - blackList.add(version + ".jar"); - blackList.add(version + ".json"); + blackList.add(instanceId + ".jar"); + blackList.add(instanceId + ".json"); LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); try (var zip = new Zipper(modpackFile)) { - Path runDirectory = repository.getRunDirectory(version); + Path runDirectory = repository.getRunDirectory(instanceId); List files = new ArrayList<>(); zip.putDirectory(runDirectory, "overrides", path -> { if (Modpack.acceptFile(path, blackList, info.getWhitelist())) { @@ -88,9 +89,9 @@ public void execute() throws Exception { } }); - String gameVersion = repository.getGameVersion(version) - .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(version), gameVersion); + String gameVersion = repository.getGameVersion(instanceId) + .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); // Mcbbs manifest List addons = new ArrayList<>(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java index f4341823e4..1c477bd994 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java @@ -27,6 +27,7 @@ import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackExportInfo; @@ -43,13 +44,13 @@ public class ModrinthModpackExportTask extends Task { private final DefaultGameRepository repository; - private final String version; + private final GameInstanceID instanceId; private final ModpackExportInfo info; private final Path modpackFile; - public ModrinthModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo info, Path modpackFile) { + public ModrinthModpackExportTask(DefaultGameRepository repository, GameInstanceID instanceId, ModpackExportInfo info, Path modpackFile) { this.repository = repository; - this.version = version; + this.instanceId = instanceId; this.info = info.validate(); this.modpackFile = modpackFile; @@ -69,9 +70,9 @@ private ModrinthManifest.File tryGetRemoteFile(Path file, String relativePath) t return null; } - boolean isDisabled = repository.getModManager(version).isDisabled(file); + boolean isDisabled = repository.getModManager(instanceId).isDisabled(file); if (isDisabled) { - relativePath = repository.getModManager(version).enableMod(Paths.get(relativePath)).toString(); + relativePath = repository.getModManager(instanceId).enableMod(Paths.get(relativePath)).toString(); } LocalModFile localModFile = null; @@ -128,11 +129,11 @@ private ModrinthManifest.File tryGetRemoteFile(Path file, String relativePath) t @Override public void execute() throws Exception { ArrayList blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); - blackList.add(version + ".jar"); - blackList.add(version + ".json"); + blackList.add(instanceId + ".jar"); + blackList.add(instanceId + ".json"); LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); try (var zip = new Zipper(modpackFile)) { - Path runDirectory = repository.getRunDirectory(version); + Path runDirectory = repository.getRunDirectory(instanceId); List files = new ArrayList<>(); Set filesInManifest = new HashSet<>(); @@ -170,9 +171,9 @@ public void execute() throws Exception { return Modpack.acceptFile(path, blackList, info.getWhitelist()); }); - String gameVersion = repository.getGameVersion(version) - .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(version), gameVersion); + String gameVersion = repository.getGameVersion(instanceId) + .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); Map dependencies = new HashMap<>(); dependencies.put("minecraft", gameVersion); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java index ef47a7dc98..f3ba850aac 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java @@ -19,6 +19,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackExportInfo; @@ -42,18 +43,18 @@ */ public class MultiMCModpackExportTask extends Task { private final DefaultGameRepository repository; - private final String versionId; + private final GameInstanceID instanceId; private final List whitelist; private final MultiMCInstanceConfiguration configuration; private final Path output; /** * @param output mod pack file. - * @param versionId to locate version.json + * @param instanceId to locate version.json */ - public MultiMCModpackExportTask(DefaultGameRepository repository, String versionId, List whitelist, MultiMCInstanceConfiguration configuration, Path output) { + public MultiMCModpackExportTask(DefaultGameRepository repository, GameInstanceID instanceId, List whitelist, MultiMCInstanceConfiguration configuration, Path output) { this.repository = repository; - this.versionId = versionId; + this.instanceId = instanceId; this.whitelist = whitelist; this.configuration = configuration; this.output = output; @@ -72,15 +73,15 @@ public MultiMCModpackExportTask(DefaultGameRepository repository, String version @Override public void execute() throws Exception { ArrayList blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); - blackList.add(versionId + ".jar"); - blackList.add(versionId + ".json"); + blackList.add(instanceId + ".jar"); + blackList.add(instanceId + ".json"); LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); try (Zipper zip = new Zipper(output)) { - zip.putDirectory(repository.getRunDirectory(versionId), ".minecraft", path -> Modpack.acceptFile(path, blackList, whitelist)); + zip.putDirectory(repository.getRunDirectory(instanceId), ".minecraft", path -> Modpack.acceptFile(path, blackList, whitelist)); - String gameVersion = repository.getGameVersion(versionId) - .orElseThrow(() -> new IOException("Cannot parse the version of " + versionId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(versionId), gameVersion); + String gameVersion = repository.getGameVersion(instanceId) + .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); List components = new ArrayList<>(); components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, MultiMCComponents.getComponent(MINECRAFT), gameVersion)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java index 1231782f8d..7806431ffb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java @@ -19,6 +19,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.ModAdviser; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; @@ -41,13 +42,13 @@ public class ServerModpackExportTask extends Task { private final DefaultGameRepository repository; - private final String versionId; + private final GameInstanceID instanceId; private final ModpackExportInfo exportInfo; private final Path modpackFile; - public ServerModpackExportTask(DefaultGameRepository repository, String version, ModpackExportInfo exportInfo, Path modpackFile) { + public ServerModpackExportTask(DefaultGameRepository repository, GameInstanceID instanceId, ModpackExportInfo exportInfo, Path modpackFile) { this.repository = repository; - this.versionId = version; + this.instanceId = instanceId; this.exportInfo = exportInfo.validate(); this.modpackFile = modpackFile; @@ -65,11 +66,11 @@ public ServerModpackExportTask(DefaultGameRepository repository, String version, @Override public void execute() throws Exception { ArrayList blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); - blackList.add(versionId + ".jar"); - blackList.add(versionId + ".json"); + blackList.add(instanceId + ".jar"); + blackList.add(instanceId + ".json"); LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); try (Zipper zip = new Zipper(modpackFile)) { - Path runDirectory = repository.getRunDirectory(versionId); + Path runDirectory = repository.getRunDirectory(instanceId); List files = new ArrayList<>(); zip.putDirectory(runDirectory, "overrides", path -> { if (Modpack.acceptFile(path, blackList, exportInfo.getWhitelist())) { @@ -84,9 +85,9 @@ public void execute() throws Exception { } }); - String gameVersion = repository.getGameVersion(versionId) - .orElseThrow(() -> new IOException("Cannot parse the version of " + versionId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(versionId), gameVersion); + String gameVersion = repository.getGameVersion(instanceId) + .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); List addons = new ArrayList<>(); addons.add(new ServerModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion)); analyzer.getVersion(FORGE).ifPresent(forgeVersion -> From 8cb61e1c4c35464a54f7255239691936474ce6cd Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 13:54:20 +0800 Subject: [PATCH 37/85] refactor: update LogExporter and related classes to use GameInstanceID instead of version strings --- .../org/jackhuang/hmcl/game/LogExporter.java | 28 +++++----- .../jackhuang/hmcl/ui/GameCrashWindow.java | 2 +- .../jackhuang/hmcl/game/GameRepository.java | 55 ------------------- .../jackhuang/hmcl/game/LaunchOptions.java | 10 ++++ 4 files changed, 25 insertions(+), 70 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java index 4d2350dfc9..07be77f54a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java @@ -42,22 +42,22 @@ private LogExporter() { } public static CompletableFuture exportLogs( - Path zipFile, DefaultGameRepository gameRepository, String versionId, String logs, String launchScript, + Path zipFile, DefaultGameRepository repository, GameInstanceID instanceId, String logs, String launchScript, PathMatcher logMatcher) { - Path runDirectory = gameRepository.getRunDirectory(versionId); - Path baseDirectory = gameRepository.getBaseDirectory(); - List versions = new ArrayList<>(); + Path runDirectory = repository.getRunDirectory(instanceId); + Path baseDirectory = repository.getBaseDirectory(); + List instances = new ArrayList<>(); - String currentVersionId = versionId; - HashSet resolvedSoFar = new HashSet<>(); + GameInstanceID currentInstanceId = instanceId; + HashSet resolvedSoFar = new HashSet<>(); while (true) { - if (resolvedSoFar.contains(currentVersionId)) break; - resolvedSoFar.add(currentVersionId); - GameInstanceManifest currentVersion = gameRepository.getInstanceManifest(currentVersionId); - versions.add(currentVersionId); + if (resolvedSoFar.contains(currentInstanceId)) break; + resolvedSoFar.add(currentInstanceId); + GameInstanceManifest currentVersion = repository.getInstanceManifest(currentInstanceId); + instances.add(currentInstanceId); - if (StringUtils.isNotBlank(currentVersion.getInheritsFrom())) { - currentVersionId = currentVersion.getInheritsFrom(); + if (currentVersion.inheritsFrom() != null) { + currentInstanceId = currentVersion.inheritsFrom(); } else { break; } @@ -74,8 +74,8 @@ public static CompletableFuture exportLogs( zipper.putTextFile(logs, "minecraft.log"); zipper.putTextFile(Logger.filterForbiddenToken(launchScript), OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "launch.bat" : "launch.sh"); - for (String id : versions) { - Path versionJson = baseDirectory.resolve("versions").resolve(id).resolve(id + ".json"); + for (GameInstanceID id : instances) { + Path versionJson = repository.getInstanceJson(id); if (Files.exists(versionJson)) { zipper.putFile(versionJson, id + ".json"); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java index c968172ad3..c443ca564b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java @@ -291,7 +291,7 @@ private CompletableFuture exportGameCrashInfo() { } }); - return LogExporter.exportLogs(logFile, repository, launchOptions.getVersionName(), logs, + return LogExporter.exportLogs(logFile, repository, launchOptions.getInstanceId(), logs, new CommandBuilder().addAll(managedProcess.getCommands()).toString(), path -> { try { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index 9c44f180b0..000f92b95c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -176,62 +176,24 @@ default Path getRunDirectory(String id) { /// @return the native library directory Path getNativeDirectory(GameInstanceID instanceId, Platform platform); - /// Returns the native directory by string id. - /// - /// @param id the instance id string - /// @param platform the target platform - /// @return the native library directory - default Path getNativeDirectory(String id, Platform platform) { - return getNativeDirectory(new GameInstanceID(id), platform); - } - /// Returns the mods directory for an instance. /// /// @param instanceId the instance id /// @return the mods directory Path getModsDirectory(GameInstanceID instanceId); - /// Returns the mods directory by string id. - /// - /// @param id the instance id string - /// @return the mods directory - default Path getModsDirectory(String id) { - return getModsDirectory(new GameInstanceID(id)); - } - /// Returns the resource pack directory for an instance. /// /// @param instanceId the instance id /// @return the resource pack directory Path getResourcePackDirectory(GameInstanceID instanceId); - /// Returns the resource pack directory by string id. - /// - /// @param id the instance id string - /// @return the resource pack directory - default Path getResourcePackDirectory(String id) { - return getResourcePackDirectory(new GameInstanceID(id)); - } - /// Returns the primary client jar path for a manifest. /// /// @param manifest the manifest whose jar should be located /// @return the primary client jar path Path getInstanceJar(GameInstanceManifest manifest); - /// Returns the instance jar path by string id. - /// - /// @param id the instance id string - /// @return the primary client jar path - /// @throws IllegalArgumentException if the instance id is invalid or the instance is not loaded - default Path getInstanceJar(String id) { - try { - return getInstanceJar(new GameInstanceID(id)); - } catch (NoSuchGameInstanceException e) { - throw new IllegalArgumentException(e); - } - } - /// Detects the Minecraft game version associated with a manifest. /// /// @param manifest the manifest to inspect @@ -247,18 +209,6 @@ default Optional getGameVersion(GameInstanceID instanceId) throws NoSuch return getGameVersion(getInstanceManifest(instanceId)); } - /// Returns the detected game version by string id. - /// - /// @param id the instance id string - /// @return the detected Minecraft game version, or empty if the id is invalid, the instance is missing, or detection fails - default Optional getGameVersion(String id) { - try { - return getGameVersion(new GameInstanceID(id)); - } catch (NoSuchGameInstanceException e) { - return Optional.empty(); - } - } - /// Returns the primary client jar path for an instance. /// /// @param instanceId the instance id @@ -275,11 +225,6 @@ default Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstance /// @return whether the instance was renamed boolean renameInstance(GameInstanceID from, GameInstanceID to); - /// Renames an instance by string id. - default boolean renameInstance(String from, String to) { - return renameInstance(new GameInstanceID(from), new GameInstanceID(to)); - } - /// Returns the asset directory that should be used at launch time. /// /// @param instanceId the instance id diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java index 309407a760..c086611681 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java @@ -30,6 +30,7 @@ */ public class LaunchOptions implements Serializable { + private GameInstanceID instanceId; private Path gameDir; private JavaRuntime java; private String versionName; @@ -65,6 +66,10 @@ public class LaunchOptions implements Serializable { private boolean disableAutoGameOptions; private boolean daemon; + public GameInstanceID getInstanceId() { + return instanceId; + } + /** * The game directory */ @@ -311,6 +316,11 @@ public List getJavaAgents() { return options.javaAgents; } + public Builder setInstanceId(GameInstanceID instanceId) { + options.instanceId = instanceId; + return this; + } + public Builder setGameDir(Path gameDir) { options.gameDir = gameDir; return this; From 83f0307bcc39c4ca912b4f57a564982e7b39647a Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 14:17:11 +0800 Subject: [PATCH 38/85] refactor: update CurseCompletionTask, CurseInstallTask, and related classes to use GameInstanceID instead of version strings --- .../hmcl/game/HMCLModpackInstallTask.java | 24 ++++++------- .../hmcl/game/HMCLModpackProvider.java | 10 +++--- .../jackhuang/hmcl/game/LauncherHelper.java | 6 ++-- .../jackhuang/hmcl/game/ModpackHelper.java | 35 +++++++++---------- .../hmcl/ui/download/DownloadPage.java | 9 ++--- .../ModpackInstallWizardProvider.java | 11 +++--- .../VanillaInstallWizardProvider.java | 4 +-- .../org/jackhuang/hmcl/ui/main/MainPage.java | 4 +-- .../download/DefaultDependencyManager.java | 2 +- .../hmcl/download/DependencyManager.java | 2 +- .../jackhuang/hmcl/download/GameBuilder.java | 8 +++-- .../org/jackhuang/hmcl/modpack/Modpack.java | 3 +- .../hmcl/modpack/ModpackProvider.java | 5 +-- .../hmcl/modpack/ModpackUpdateTask.java | 11 +++--- .../modpack/curse/CurseCompletionTask.java | 21 +++++------ .../hmcl/modpack/curse/CurseInstallTask.java | 26 ++++++++------ .../modpack/curse/CurseModpackProvider.java | 13 +++---- .../mcbbs/McbbsModpackCompletionTask.java | 19 +++++----- .../mcbbs/McbbsModpackLocalInstallTask.java | 29 +++++++-------- .../modpack/mcbbs/McbbsModpackManifest.java | 5 +-- .../modpack/mcbbs/McbbsModpackProvider.java | 9 ++--- .../mcbbs/McbbsModpackRemoteInstallTask.java | 21 +++++------ .../modrinth/ModrinthCompletionTask.java | 21 +++++------ .../modpack/modrinth/ModrinthInstallTask.java | 27 +++++++------- .../modrinth/ModrinthModpackProvider.java | 13 +++---- .../modpack/multimc/MultiMCInstancePatch.java | 6 ++-- .../multimc/MultiMCModpackInstallTask.java | 35 +++++++++---------- .../multimc/MultiMCModpackProvider.java | 11 +++--- .../server/ServerModpackCompletionTask.java | 21 +++++------ .../server/ServerModpackLocalInstallTask.java | 23 ++++++------ .../modpack/server/ServerModpackManifest.java | 5 +-- .../modpack/server/ServerModpackProvider.java | 9 ++--- .../ServerModpackRemoteInstallTask.java | 21 +++++------ 33 files changed, 244 insertions(+), 225 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java index 8fb7a8332a..b1cd325584 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java @@ -37,7 +37,6 @@ public final class HMCLModpackInstallTask extends Task { private final Path zipFile; - private final String name; private final GameInstanceID instanceId; private final HMCLGameRepository repository; private final DefaultDependencyManager dependency; @@ -45,23 +44,22 @@ public final class HMCLModpackInstallTask extends Task { private final List> dependencies = new ArrayList<>(1); private final List> dependents = new ArrayList<>(4); - public HMCLModpackInstallTask(HMCLGameRepository repository, Path zipFile, Modpack modpack, String name) { + public HMCLModpackInstallTask(HMCLGameRepository repository, Path zipFile, Modpack modpack, GameInstanceID instanceId) { this.repository = repository; this.dependency = repository.getDependency(); this.zipFile = zipFile; - this.name = name; - this.instanceId = new GameInstanceID(name); + this.instanceId = instanceId; this.modpack = modpack; - Path run = repository.getRunDirectory(instanceId); - Path json = repository.getModpackConfiguration(instanceId); - if (repository.hasInstance(instanceId) && Files.notExists(json)) - throw new IllegalArgumentException("Version " + name + " already exists"); + Path run = repository.getRunDirectory(this.instanceId); + Path json = repository.getModpackConfiguration(this.instanceId); + if (repository.hasInstance(this.instanceId) && Files.notExists(json)) + throw new IllegalArgumentException("Instance " + instanceId + " already exists"); - dependents.add(dependency.gameBuilder().name(name).gameVersion(modpack.getGameVersion()).buildAsync()); + dependents.add(dependency.newGameBuilder().name(this.instanceId).gameVersion(modpack.getGameVersion()).buildAsync()); onDone().register(event -> { - if (event.isFailed()) repository.removeInstanceFromDisk(instanceId); + if (event.isFailed()) repository.removeInstanceFromDisk(this.instanceId); }); ModpackConfiguration config = null; @@ -70,12 +68,12 @@ public HMCLModpackInstallTask(HMCLGameRepository repository, Path zipFile, Modpa config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(Modpack.class)); if (!HMCLModpackProvider.INSTANCE.getName().equals(config.getType())) - throw new IllegalArgumentException("Version " + name + " is not a HMCL modpack. Cannot update this version."); + throw new IllegalArgumentException("Version " + instanceId + " is not a HMCL modpack. Cannot update this version."); } } catch (JsonParseException | IOException ignore) { } dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList("/minecraft"), it -> !"pack.json".equals(it), config)); - dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/minecraft"), modpack, HMCLModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(instanceId)).withStage("hmcl.modpack")); + dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/minecraft"), modpack, HMCLModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(this.instanceId)).withStage("hmcl.modpack")); } @Override @@ -91,7 +89,7 @@ public List> getDependents() { @Override public void execute() throws Exception { String json = CompressingUtils.readTextZipEntry(zipFile, "minecraft/pack.json"); - GameInstanceManifest originalVersion = JsonUtils.GSON.fromJson(json, GameInstanceManifest.class).setId(name).setJar(null); + GameInstanceManifest originalVersion = JsonUtils.GSON.fromJson(json, GameInstanceManifest.class).withId(instanceId).withJar(null); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(originalVersion, null); Task libraryTask = Task.supplyAsync(() -> originalVersion); // reinstall libraries diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java index a2ee651483..8062bcbae7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java @@ -42,12 +42,12 @@ public String getName() { } @Override - public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { + public Task createCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { return null; } @Override - public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { + public Task createUpdateTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { if (!(modpack.getManifest() instanceof HMCLModpackManifest)) throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); @@ -55,7 +55,7 @@ public Task createUpdateTask(DefaultDependencyManager dependencyManager, Stri throw new IllegalArgumentException("HMCLModpackProvider requires HMCLGameRepository"); } - return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new HMCLModpackInstallTask(repository, zipFile, modpack, name)); + return new ModpackUpdateTask(dependencyManager.getGameRepository(), instanceId, new HMCLModpackInstallTask(repository, zipFile, modpack, instanceId)); } @Override @@ -76,8 +76,8 @@ public Modpack readManifest(ZipArchiveReader file, Path path, Charset encoding) private final static class HMCLModpack extends Modpack { @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, String name, String iconUrl) { - return new HMCLModpackInstallTask((HMCLGameRepository) dependencyManager.getGameRepository(), zipFile, this, name); + public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, GameInstanceID instanceId, String iconUrl) { + return new HMCLModpackInstallTask((HMCLGameRepository) dependencyManager.getGameRepository(), zipFile, this, instanceId); } } 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 222d223ca7..f2af29b08e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -84,7 +84,6 @@ public final class LauncherHelper { private final HMCLGameRepository repository; private Account account; - private final String selectedVersion; private final GameInstanceID selectedInstanceId; private Path scriptFile; private final GameSettings.Effective setting; @@ -96,7 +95,6 @@ public final class LauncherHelper { public LauncherHelper(HMCLGameRepository repository, Account account, GameInstanceID selectedInstanceId) { this.repository = Objects.requireNonNull(repository); this.account = Objects.requireNonNull(account); - this.selectedVersion = selectedInstanceId.toString(); this.selectedInstanceId = selectedInstanceId; this.setting = repository.getEffectiveGameSettings(selectedInstanceId); this.launcherVisibility = setting.getInheritable(GameSettings::launcherVisibilityProperty); @@ -134,7 +132,7 @@ public void setDisableOfflineSkin() { public void launch() { FXUtils.checkFxUserThread(); - LOG.info("Launching game version: " + selectedVersion); + LOG.info("Launching game version: " + selectedInstanceId); Controllers.dialog(launchingStepsPane); launch0(); @@ -172,7 +170,7 @@ private void launch0() { ModpackConfiguration configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedInstanceId)); ModpackProvider provider = ModpackHelper.getProviderByType(configuration.getType()); if (provider == null) return null; - else return provider.createCompletionTask(dependencyManager, selectedVersion); + else return provider.createCompletionTask(dependencyManager, selectedInstanceId); } catch (IOException e) { return null; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 51bf0db9d8..89ad1f7da9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -176,7 +176,7 @@ public static Task getInstallTask(HMCLGameRepository repository, ServerModpac } }; - return new ServerModpackRemoteInstallTask(repository.getDependency(), manifest, name) + return new ServerModpackRemoteInstallTask(repository.getDependency(), manifest, instanceId) .whenComplete(Schedulers.defaultScheduler(), success, failure) .withStagesHints(new Task.StagesHint("hmcl.modpack"), new Task.StagesHint("hmcl.modpack.download", List.of("hmcl.install.assets", "hmcl.install.libraries"))); } @@ -222,25 +222,25 @@ public static Task getInstallTask(HMCLGameRepository repository, Path zipFile }; if (modpack.getManifest() instanceof MultiMCInstanceConfiguration) - return modpack.getInstallTask(repository.getDependency(), zipFile, name, iconUrl) + return modpack.getInstallTask(repository.getDependency(), zipFile, instanceId, iconUrl) .whenComplete(Schedulers.defaultScheduler(), success, failure) - .thenComposeAsync(createMultiMCPostInstallTask(repository, (MultiMCInstanceConfiguration) modpack.getManifest(), name)) + .thenComposeAsync(createMultiMCPostInstallTask(repository, (MultiMCInstanceConfiguration) modpack.getManifest(), instanceId)) .withStagesHints(new Task.StagesHint("hmcl.modpack"), new Task.StagesHint("hmcl.modpack.download", List.of("hmcl.install.assets", "hmcl.install.libraries"))); else if (modpack.getManifest() instanceof McbbsModpackManifest) - return modpack.getInstallTask(repository.getDependency(), zipFile, name, iconUrl) + return modpack.getInstallTask(repository.getDependency(), zipFile, instanceId, iconUrl) .whenComplete(Schedulers.defaultScheduler(), success, failure) - .thenComposeAsync(createMcbbsPostInstallTask(repository, (McbbsModpackManifest) modpack.getManifest(), name)) + .thenComposeAsync(createMcbbsPostInstallTask(repository, (McbbsModpackManifest) modpack.getManifest(), instanceId)) .withStagesHints(new Task.StagesHint("hmcl.modpack"), new Task.StagesHint("hmcl.modpack.download", List.of("hmcl.install.assets", "hmcl.install.libraries"))); else - return modpack.getInstallTask(repository.getDependency(), zipFile, name, iconUrl) + return modpack.getInstallTask(repository.getDependency(), zipFile, instanceId, iconUrl) .whenComplete(Schedulers.javafx(), success, failure) .withStagesHints(new Task.StagesHint("hmcl.modpack"), new Task.StagesHint("hmcl.modpack.download", List.of("hmcl.install.assets", "hmcl.install.libraries"))); } - public static Task getUpdateTask(HMCLGameRepository repository, ServerModpackManifest manifest, Charset charset, String name, ModpackConfiguration configuration) throws UnsupportedModpackException { + public static Task getUpdateTask(HMCLGameRepository repository, ServerModpackManifest manifest, Charset charset, GameInstanceID instanceId, ModpackConfiguration configuration) throws UnsupportedModpackException { switch (configuration.getType()) { case ServerModpackRemoteInstallTask.MODPACK_TYPE: - return new ModpackUpdateTask(repository, name, new ServerModpackRemoteInstallTask(repository.getDependency(), manifest, name)) + return new ModpackUpdateTask(repository, instanceId, new ServerModpackRemoteInstallTask(repository.getDependency(), manifest, instanceId)) .thenComposeAsync(repository.refreshAsync()) .withStagesHints(new Task.StagesHint("hmcl.modpack"), new Task.StagesHint("hmcl.modpack.download", List.of("hmcl.install.assets", "hmcl.install.libraries"))); default: @@ -248,18 +248,18 @@ public static Task getUpdateTask(HMCLGameRepository repository, ServerModp } } - public static Task getUpdateTask(HMCLGameRepository repository, Path zipFile, Charset charset, String name, ModpackConfiguration configuration) throws UnsupportedModpackException, ManuallyCreatedModpackException, MismatchedModpackTypeException { + public static Task getUpdateTask(HMCLGameRepository repository, Path zipFile, Charset charset, GameInstanceID instanceId, ModpackConfiguration configuration) throws UnsupportedModpackException, ManuallyCreatedModpackException, MismatchedModpackTypeException { Modpack modpack = ModpackHelper.readModpackManifest(zipFile, charset); ModpackProvider provider = getProviderByType(configuration.getType()); if (provider == null) { throw new UnsupportedModpackException(); } if (modpack.getManifest() instanceof MultiMCInstanceConfiguration) - return provider.createUpdateTask(repository.getDependency(), name, zipFile, modpack) - .thenComposeAsync(() -> createMultiMCPostUpdateTask(repository, (MultiMCInstanceConfiguration) modpack.getManifest(), name)) + return provider.createUpdateTask(repository.getDependency(), instanceId, zipFile, modpack) + .thenComposeAsync(() -> createMultiMCPostUpdateTask(repository, (MultiMCInstanceConfiguration) modpack.getManifest(), instanceId)) .thenComposeAsync(repository.refreshAsync()); else - return provider.createUpdateTask(repository.getDependency(), name, zipFile, modpack) + return provider.createUpdateTask(repository.getDependency(), instanceId, zipFile, modpack) .thenComposeAsync(repository.refreshAsync()); } @@ -335,23 +335,22 @@ private static void applyCommandAndJvmSettings(MultiMCInstanceConfiguration c, G } } - private static Task createMultiMCPostUpdateTask(HMCLGameRepository repository, MultiMCInstanceConfiguration manifest, String version) { + private static Task createMultiMCPostUpdateTask(HMCLGameRepository repository, MultiMCInstanceConfiguration manifest, GameInstanceID instanceId) { return Task.runAsync(Schedulers.javafx(), () -> { - GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(new GameInstanceID(version))); + GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(instanceId)); ModpackHelper.applyCommandAndJvmSettings(manifest, setting); }); } - private static Task createMultiMCPostInstallTask(HMCLGameRepository repository, MultiMCInstanceConfiguration manifest, String version) { + private static Task createMultiMCPostInstallTask(HMCLGameRepository repository, MultiMCInstanceConfiguration manifest, GameInstanceID instanceId) { return Task.runAsync(Schedulers.javafx(), () -> { - GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(new GameInstanceID(version))); + GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(instanceId)); ModpackHelper.toGameSettings(manifest, setting); }); } - private static Task createMcbbsPostInstallTask(HMCLGameRepository repository, McbbsModpackManifest manifest, String version) { + private static Task createMcbbsPostInstallTask(HMCLGameRepository repository, McbbsModpackManifest manifest, GameInstanceID instanceId) { return Task.runAsync(Schedulers.javafx(), () -> { - GameInstanceID instanceId = new GameInstanceID(version); GameSettings.Effective effective = repository.getEffectiveGameSettings(instanceId); if (manifest.getLaunchInfo().getMinMemory() > effective.getMaxMemory()) { GameSettings.Instance setting = Objects.requireNonNull(repository.getInstanceGameSettingsOrCreate(instanceId)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index da0003e8c2..100681242e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -315,10 +315,11 @@ public void start(SettingsMap settings) { } private Task finishVersionDownloadingAsync(SettingsMap settings) { - GameBuilder builder = dependencyManager.gameBuilder(); + GameBuilder builder = dependencyManager.newGameBuilder(); String name = (String) settings.get("name"); - builder.name(name); + GameInstanceID instanceId = new GameInstanceID(name); + builder.name(instanceId); builder.gameVersion(((RemoteVersion) settings.get(LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId())).getGameVersion()); settings.asStringMap().forEach((key, value) -> { @@ -329,8 +330,8 @@ private Task finishVersionDownloadingAsync(SettingsMap settings) { return builder.buildAsync().whenComplete(any -> { repository.refresh(); - repository.applyDefaultIsolationSetting(new GameInstanceID(name)); - }).thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(new GameInstanceID(name))); + repository.applyDefaultIsolationSetting(instanceId); + }).thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(instanceId)); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index 422f3a24a2..41f25deef9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -94,6 +94,8 @@ private Task finishModpackInstallingAsync(SettingsMap settings) { Charset charset = settings.get(LocalModpackPage.MODPACK_CHARSET); boolean isManuallyCreated = settings.getOrDefault(LocalModpackPage.MODPACK_MANUALLY_CREATED, false); + GameInstanceID instanceId = new GameInstanceID(name); + if (isManuallyCreated) { return ModpackHelper.getInstallManuallyCreatedModpackTask(selected, name, charset); } @@ -106,11 +108,10 @@ private Task finishModpackInstallingAsync(SettingsMap settings) { return null; } try { - GameInstanceID instanceId = new GameInstanceID(name); if (serverModpackManifest != null) { - return ModpackHelper.getUpdateTask(repository, serverModpackManifest, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(instanceId))); + return ModpackHelper.getUpdateTask(repository, serverModpackManifest, modpack.getEncoding(), instanceId, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(instanceId))); } else { - return ModpackHelper.getUpdateTask(repository, selected, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(instanceId))); + return ModpackHelper.getUpdateTask(repository, selected, modpack.getEncoding(), instanceId, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(instanceId))); } } catch (UnsupportedModpackException | ManuallyCreatedModpackException e) { Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageType.ERROR); @@ -123,10 +124,10 @@ private Task finishModpackInstallingAsync(SettingsMap settings) { } else { if (serverModpackManifest != null) { return ModpackHelper.getInstallTask(repository, serverModpackManifest, name, modpack) - .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(new GameInstanceID(name))); + .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(instanceId)); } else { return ModpackHelper.getInstallTask(repository, selected, name, modpack, iconUrl) - .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(new GameInstanceID(name))); + .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(instanceId)); } } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java index f3e2ae15cb..e281d034f8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java @@ -51,10 +51,10 @@ public void start(SettingsMap settings) { } private Task finishVersionDownloadingAsync(SettingsMap settings) { - GameBuilder builder = dependencyManager.gameBuilder(); + GameBuilder builder = dependencyManager.newGameBuilder(); String name = (String) settings.get("name"); - builder.name(name); + builder.name(new GameInstanceID(name)); builder.gameVersion(((RemoteVersion) settings.get("game")).getGameVersion()); settings.asStringMap().forEach((key, value) -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index e2ce16356b..ff45d8c33b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -338,8 +338,8 @@ private void launchNoGame() { DefaultDependencyManager dependency = repository.getDependency(); String gameVersion = gameVersionHolder.value = version.getGameVersion(); - return dependency.gameBuilder() - .name(gameVersion) + return dependency.newGameBuilder() + .name(new GameInstanceID(gameVersion)) .gameVersion(gameVersion) .buildAsync(); }) 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 8008667c6d..153a2ed2dc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -74,7 +74,7 @@ public DefaultCacheRepository getCacheRepository() { } @Override - public GameBuilder gameBuilder() { + public GameBuilder newGameBuilder() { return new DefaultGameBuilder(this); } 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 d709f6daea..87e00bb1a7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java @@ -69,7 +69,7 @@ public interface DependencyManager { /** * The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine. */ - GameBuilder gameBuilder(); + GameBuilder newGameBuilder(); /** * Install a library to a version. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/GameBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/GameBuilder.java index 4d2e3f5fdc..c16c19d600 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/GameBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/GameBuilder.java @@ -17,7 +17,9 @@ */ package org.jackhuang.hmcl.download; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.task.Task; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -28,12 +30,12 @@ */ public abstract class GameBuilder { - protected String name = ""; + protected @Nullable GameInstanceID name; protected String gameVersion = ""; protected final Map toolVersions = new HashMap<>(); protected final Set remoteVersions = new HashSet<>(); - public String getName() { + public GameInstanceID getName() { return name; } @@ -42,7 +44,7 @@ public String getName() { * * @param name the name of new game version. */ - public GameBuilder name(String name) { + public GameBuilder name(GameInstanceID name) { this.name = Objects.requireNonNull(name); return this; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/Modpack.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/Modpack.java index b6a60f699f..d0dd38d9f5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/Modpack.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/Modpack.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.modpack; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.task.Task; import java.nio.charset.Charset; @@ -119,7 +120,7 @@ public Modpack setManifest(ModpackManifest manifest) { return this; } - public abstract Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, String name, String iconUrl); + public abstract Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, GameInstanceID instanceId, String iconUrl); public static boolean acceptFile(String path, List blackList, List whiteList) { if (path.isEmpty()) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackProvider.java index f3a516ce3d..014447f936 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackProvider.java @@ -20,6 +20,7 @@ import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.task.Task; @@ -31,9 +32,9 @@ public interface ModpackProvider { String getName(); - Task createCompletionTask(DefaultDependencyManager dependencyManager, String version); + Task createCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId); - Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException; + Task createUpdateTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException; /** * @param zipFile the opened modpack zip file. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java index 43ca3a9989..f06f08a726 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/ModpackUpdateTask.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.modpack; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.io.FileUtils; @@ -29,20 +30,20 @@ public class ModpackUpdateTask extends Task { private final DefaultGameRepository repository; - private final String id; + private final GameInstanceID id; private final Task updateTask; private final Path backupFolder; - public ModpackUpdateTask(DefaultGameRepository repository, String id, Task updateTask) { + public ModpackUpdateTask(DefaultGameRepository repository, GameInstanceID instanceId, Task updateTask) { this.repository = repository; - this.id = id; + this.id = instanceId; this.updateTask = updateTask; Path backup = repository.getBaseDirectory().resolve("backup"); while (true) { int num = (int)(Math.random() * 10000000); - if (!Files.exists(backup.resolve(id + "-" + num))) { - backupFolder = backup.resolve(id + "-" + num); + if (!Files.exists(backup.resolve(instanceId + "-" + num))) { + backupFolder = backup.resolve(instanceId + "-" + num); break; } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java index f8dcf01124..a26fd6d9f6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseCompletionTask.java @@ -23,6 +23,7 @@ import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.ModpackCompletionException; import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.task.FileDownloadTask; @@ -53,7 +54,7 @@ public final class CurseCompletionTask extends Task { private final DefaultDependencyManager dependency; private final DefaultGameRepository repository; private final ModManager modManager; - private final String version; + private final GameInstanceID instanceId; private CurseManifest manifest; private List> dependencies; @@ -65,29 +66,29 @@ public final class CurseCompletionTask extends Task { * Constructor. * * @param dependencyManager the dependency manager. - * @param version the existent and physical version. + * @param instanceId the existent and physical version. */ - public CurseCompletionTask(DefaultDependencyManager dependencyManager, String version) { - this(dependencyManager, version, null); + public CurseCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { + this(dependencyManager, instanceId, null); } /** * Constructor. * * @param dependencyManager the dependency manager. - * @param version the existent and physical version. + * @param instanceId the existent and physical version. * @param manifest the CurseForgeModpack manifest. */ - public CurseCompletionTask(DefaultDependencyManager dependencyManager, String version, CurseManifest manifest) { + public CurseCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, CurseManifest manifest) { this.dependency = dependencyManager; this.repository = dependencyManager.getGameRepository(); - this.modManager = repository.getModManager(version); - this.version = version; + this.modManager = repository.getModManager(instanceId); + this.instanceId = instanceId; this.manifest = manifest; if (manifest == null) try { - Path manifestFile = repository.getInstanceRoot(version).resolve("manifest.json"); + Path manifestFile = repository.getInstanceRoot(instanceId).resolve("manifest.json"); if (Files.exists(manifestFile)) this.manifest = JsonUtils.fromJsonFile(manifestFile, CurseManifest.class); } catch (Exception e) { @@ -112,7 +113,7 @@ public void execute() throws Exception { if (manifest == null) return; - Path root = repository.getInstanceRoot(version); + Path root = repository.getInstanceRoot(instanceId); // Because in China, Curse is too difficult to visit, // if failed, ignore it and retry next time. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java index eaeca83d2d..c5da9fa47f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseInstallTask.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.*; import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Task; @@ -50,7 +51,7 @@ public final class CurseInstallTask extends Task { private final Path zipFile; private final Modpack modpack; private final CurseManifest manifest; - private final String name; + private final GameInstanceID instanceId; private final String iconUrl; private final Path run; private final ModpackConfiguration config; @@ -67,21 +68,24 @@ public final class CurseInstallTask extends Task { * @param manifest The manifest content of given CurseForge modpack. * @param name the new version name */ - public CurseInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, CurseManifest manifest, String name, String iconUrl) { + public CurseInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, CurseManifest manifest, GameInstanceID name, String iconUrl) { this.dependencyManager = dependencyManager; this.zipFile = zipFile; this.modpack = modpack; this.manifest = manifest; - this.name = name; + this.instanceId = name; this.iconUrl = iconUrl; this.repository = dependencyManager.getGameRepository(); - this.run = repository.getRunDirectory(name); - Path json = repository.getModpackConfiguration(name); - if (repository.hasInstance(name) && Files.notExists(json)) - throw new IllegalArgumentException("Version " + name + " already exists."); + GameInstanceID instanceId = name; - GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.minecraft().gameVersion()); + this.run = repository.getRunDirectory(instanceId); + + Path json = repository.getModpackConfiguration(instanceId); + if (repository.hasInstance(instanceId) && Files.notExists(json)) + throw new IllegalArgumentException("Instance " + instanceId + " already exists."); + + GameBuilder builder = dependencyManager.newGameBuilder().name(instanceId).gameVersion(manifest.minecraft().gameVersion()); for (CurseManifestModLoader modLoader : manifest.minecraft().modLoaders()) { if (modLoader.id().startsWith("forge-")) { builder.version("forge", modLoader.id().substring("forge-".length())); @@ -124,7 +128,7 @@ public CurseInstallTask(DefaultDependencyManager dependencyManager, Path zipFile dependents.add(downloadIconTask = new CacheFileTask(dependencyManager.getDownloadProvider().injectURLWithCandidates(iconUrl))); } } - dependencies.add(new CurseCompletionTask(dependencyManager, name, manifest)); + dependencies.add(new CurseCompletionTask(dependencyManager, instanceId, manifest)); } @Override @@ -171,7 +175,7 @@ public void execute() throws Exception { // CurseForge manifest where fileName is missing. CurseCompletionTask // resolves those file names and writes the enriched manifest to // manifest.json, so read from there when available. - Path oldManifestFile = repository.getInstanceRoot(name).resolve("manifest.json"); + Path oldManifestFile = repository.getInstanceRoot(instanceId).resolve("manifest.json"); List oldFiles = config.getManifest().files(); if (Files.exists(oldManifestFile)) { try { @@ -195,7 +199,7 @@ public void execute() throws Exception { } } - Path root = repository.getInstanceRoot(name); + Path root = repository.getInstanceRoot(instanceId); Files.createDirectories(root); JsonUtils.writeToJsonFile(root.resolve("manifest.json"), manifest); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseModpackProvider.java index 00cb8ce912..f66aef4f19 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseModpackProvider.java @@ -21,6 +21,7 @@ import kala.compress.archivers.zip.ZipArchiveEntry; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.MismatchedModpackTypeException; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackProvider; @@ -43,16 +44,16 @@ public String getName() { } @Override - public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { - return new CurseCompletionTask(dependencyManager, version); + public Task createCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { + return new CurseCompletionTask(dependencyManager, instanceId); } @Override - public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { + public Task createUpdateTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { if (!(modpack.getManifest() instanceof CurseManifest curseManifest)) throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); - return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new CurseInstallTask(dependencyManager, zipFile, modpack, curseManifest, name, null)); + return new ModpackUpdateTask(dependencyManager.getGameRepository(), instanceId, new CurseInstallTask(dependencyManager, zipFile, modpack, curseManifest, instanceId, null)); } @Override @@ -68,8 +69,8 @@ public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) t return new Modpack(manifest.name(), manifest.author(), manifest.version(), manifest.minecraft().gameVersion(), description, encoding, manifest) { @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, String name, String iconUrl) { - return new CurseInstallTask(dependencyManager, zipFile, this, manifest, name, iconUrl); + public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, GameInstanceID instanceId, String iconUrl) { + return new CurseInstallTask(dependencyManager, zipFile, this, manifest, instanceId, iconUrl); } }; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java index 82682446a0..b97bf82bc8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackCompletionTask.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.modpack.ModpackCompletionException; import org.jackhuang.hmcl.modpack.curse.CurseMetaMod; @@ -53,7 +54,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask { private final DefaultDependencyManager dependency; private final DefaultGameRepository repository; private final ModManager modManager; - private final String version; + private final GameInstanceID instanceId; private final Path configurationFile; private ModpackConfiguration configuration; private McbbsModpackManifest manifest; @@ -63,16 +64,16 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask { private final AtomicInteger finished = new AtomicInteger(0); private final AtomicBoolean notFound = new AtomicBoolean(false); - public McbbsModpackCompletionTask(DefaultDependencyManager dependencyManager, String version) { - this(dependencyManager, version, null); + public McbbsModpackCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { + this(dependencyManager, instanceId, null); } - public McbbsModpackCompletionTask(DefaultDependencyManager dependencyManager, String version, ModpackConfiguration configuration) { + public McbbsModpackCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, ModpackConfiguration configuration) { this.dependency = dependencyManager; this.repository = dependencyManager.getGameRepository(); - this.modManager = repository.getModManager(version); - this.version = version; - this.configurationFile = repository.getModpackConfiguration(version); + this.modManager = repository.getModManager(instanceId); + this.instanceId = instanceId; + this.configurationFile = repository.getModpackConfiguration(instanceId); this.configuration = configuration; setStage("hmcl.modpack.download"); @@ -109,7 +110,7 @@ public CompletableFuture getFuture(TaskCompletableFuture executor) { throw new IOException("Unable to parse server manifest.json from " + manifest.getFileApi(), e); } - Path rootPath = repository.getInstanceRoot(version); + Path rootPath = repository.getInstanceRoot(instanceId); Files.createDirectories(rootPath); Map localFiles = manifest.getFiles().stream().collect(Collectors.toMap(Function.identity(), Function.identity())); @@ -171,7 +172,7 @@ public CompletableFuture getFuture(TaskCompletableFuture executor) { manifest = remoteManifest.setFiles(newFiles); return executor.all(tasks.stream().filter(Objects::nonNull).collect(Collectors.toList())); })).thenAcceptAsync(wrapConsumer(unused1 -> { - Path manifestFile = repository.getModpackConfiguration(version); + Path manifestFile = repository.getModpackConfiguration(instanceId); JsonUtils.writeToJsonFile(manifestFile, new ModpackConfiguration<>(manifest, this.configuration.getType(), this.manifest.getName(), this.manifest.getVersion(), this.manifest.getFiles().stream() diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java index 28bcd30f02..f59b85615d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.modpack.MinecraftInstanceTask; @@ -44,29 +45,29 @@ public final class McbbsModpackLocalInstallTask extends Task { private final Path zipFile; private final Modpack modpack; private final McbbsModpackManifest manifest; - private final String name; + private final GameInstanceID instanceId; private final boolean update; private final DefaultGameRepository repository; private final MinecraftInstanceTask instanceTask; private final List> dependencies = new ArrayList<>(2); private final List> dependents = new ArrayList<>(4); - public McbbsModpackLocalInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, McbbsModpackManifest manifest, String name) { + public McbbsModpackLocalInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, McbbsModpackManifest manifest, GameInstanceID instanceId) { this.dependencyManager = dependencyManager; this.zipFile = zipFile; this.modpack = modpack; this.manifest = manifest; - this.name = name; + this.instanceId = instanceId; this.repository = dependencyManager.getGameRepository(); - Path run = repository.getRunDirectory(name); + Path run = repository.getRunDirectory(instanceId); - Path json = repository.getModpackConfiguration(name); - if (repository.hasInstance(name) && Files.notExists(json)) - throw new IllegalArgumentException("Version " + name + " already exists."); - this.update = repository.hasInstance(name); + Path json = repository.getModpackConfiguration(instanceId); + if (repository.hasInstance(instanceId) && Files.notExists(json)) + throw new IllegalArgumentException("Instance " + instanceId + " already exists."); + this.update = repository.hasInstance(instanceId); - GameBuilder builder = dependencyManager.gameBuilder().name(name); + GameBuilder builder = dependencyManager.newGameBuilder().name(instanceId); for (McbbsModpackManifest.Addon addon : manifest.getAddons()) { builder.version(addon.getId(), addon.getVersion()); } @@ -74,7 +75,7 @@ public McbbsModpackLocalInstallTask(DefaultDependencyManager dependencyManager, dependents.add(builder.buildAsync()); onDone().register(event -> { if (event.isFailed()) - repository.removeInstanceFromDisk(name); + repository.removeInstanceFromDisk(instanceId); }); ModpackConfiguration config = null; @@ -83,12 +84,12 @@ public McbbsModpackLocalInstallTask(DefaultDependencyManager dependencyManager, config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(McbbsModpackManifest.class)); if (!McbbsModpackProvider.INSTANCE.getName().equals(config.getType())) - throw new IllegalArgumentException("Version " + name + " is not a Mcbbs modpack. Cannot update this version."); + throw new IllegalArgumentException("Instance " + instanceId + " is not a Mcbbs modpack. Cannot update this instance."); } } catch (JsonParseException | IOException ignore) { } dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList("/overrides"), any -> true, config).withStage("hmcl.modpack")); - instanceTask = new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, McbbsModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name)); + instanceTask = new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, McbbsModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(instanceId)); dependents.add(instanceTask.withStage("hmcl.modpack")); } @@ -104,7 +105,7 @@ public List> getDependencies() { @Override public void execute() throws Exception { - GameInstanceManifest version = repository.getInstanceManifest(name); + GameInstanceManifest version = repository.getInstanceManifest(instanceId); Optional mcbbsPatch = version.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst(); if (!update) { GameInstancePatch patch = new GameInstancePatch(PATCH_NAME).setLibraries(manifest.getLibraries()); @@ -118,7 +119,7 @@ public void execute() throws Exception { // TODO: maintain libraries. } - dependencies.add(new McbbsModpackCompletionTask(dependencyManager, name, instanceTask.getResult())); + dependencies.add(new McbbsModpackCompletionTask(dependencyManager, instanceId, instanceTask.getResult())); } private static final String PATCH_NAME = "mcbbs"; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackManifest.java index 1213aacf5c..69e63a1ee5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackManifest.java @@ -20,6 +20,7 @@ import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.modpack.Modpack; @@ -424,8 +425,8 @@ public Modpack toModpack(Charset encoding) throws IOException { .orElseThrow(() -> new IOException("Cannot find game version")).getVersion(); return new Modpack(name, author, version, gameVersion, description, encoding, this) { @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, String name, String iconUrl) { - return new McbbsModpackLocalInstallTask(dependencyManager, zipFile, this, McbbsModpackManifest.this, name); + public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, GameInstanceID instanceId, String iconUrl) { + return new McbbsModpackLocalInstallTask(dependencyManager, zipFile, this, McbbsModpackManifest.this, instanceId); } }; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackProvider.java index 966c08bfe1..5c63d3d064 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackProvider.java @@ -21,6 +21,7 @@ import kala.compress.archivers.zip.ZipArchiveEntry; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.modpack.*; import org.jackhuang.hmcl.task.Task; @@ -40,16 +41,16 @@ public String getName() { } @Override - public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { - return new McbbsModpackCompletionTask(dependencyManager, version); + public Task createCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { + return new McbbsModpackCompletionTask(dependencyManager, instanceId); } @Override - public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { + public Task createUpdateTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { if (!(modpack.getManifest() instanceof McbbsModpackManifest mcbbsModpackManifest)) throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); - return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new McbbsModpackLocalInstallTask(dependencyManager, zipFile, modpack, mcbbsModpackManifest, name)); + return new ModpackUpdateTask(dependencyManager.getGameRepository(), instanceId, new McbbsModpackLocalInstallTask(dependencyManager, zipFile, modpack, mcbbsModpackManifest, instanceId)); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java index d128fd80a4..c8e0b80c05 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackRemoteInstallTask.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -34,24 +35,24 @@ public class McbbsModpackRemoteInstallTask extends Task { - private final String name; + private final GameInstanceID instanceId; private final DefaultDependencyManager dependency; private final DefaultGameRepository repository; private final List> dependencies = new ArrayList<>(1); private final List> dependents = new ArrayList<>(1); private final McbbsModpackManifest manifest; - public McbbsModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, McbbsModpackManifest manifest, String name) { - this.name = name; + public McbbsModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, McbbsModpackManifest manifest, GameInstanceID instanceId) { + this.instanceId = instanceId; this.dependency = dependencyManager; this.repository = dependencyManager.getGameRepository(); this.manifest = manifest; - Path json = repository.getModpackConfiguration(name); - if (repository.hasInstance(name) && Files.notExists(json)) - throw new IllegalArgumentException("Version " + name + " already exists."); + Path json = repository.getModpackConfiguration(instanceId); + if (repository.hasInstance(instanceId) && Files.notExists(json)) + throw new IllegalArgumentException("Instance " + instanceId + " already exists."); - GameBuilder builder = dependencyManager.gameBuilder().name(name); + GameBuilder builder = dependencyManager.newGameBuilder().name(instanceId); for (McbbsModpackManifest.Addon addon : manifest.getAddons()) { builder.version(addon.getId(), addon.getVersion()); } @@ -59,7 +60,7 @@ public McbbsModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, dependents.add(builder.buildAsync()); onDone().register(event -> { if (event.isFailed()) - repository.removeInstanceFromDisk(name); + repository.removeInstanceFromDisk(instanceId); }); ModpackConfiguration config; @@ -68,7 +69,7 @@ public McbbsModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(McbbsModpackManifest.class)); if (!MODPACK_TYPE.equals(config.getType())) - throw new IllegalArgumentException("Version " + name + " is not a Mcbbs modpack. Cannot update this version."); + throw new IllegalArgumentException("Instance " + instanceId + " is not a Mcbbs modpack. Cannot update this instance."); } } catch (JsonParseException | IOException ignore) { } @@ -86,7 +87,7 @@ public List> getDependencies() { @Override public void execute() throws Exception { - dependencies.add(new McbbsModpackCompletionTask(dependency, name, new ModpackConfiguration<>(manifest, MODPACK_TYPE, manifest.getName(), manifest.getVersion(), Collections.emptyList()))); + dependencies.add(new McbbsModpackCompletionTask(dependency, instanceId, new ModpackConfiguration<>(manifest, MODPACK_TYPE, manifest.getName(), manifest.getVersion(), Collections.emptyList()))); } public static final String MODPACK_TYPE = "Server"; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java index 0dd24e6636..d44673ac8b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthCompletionTask.java @@ -20,6 +20,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.addon.mod.ModManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.ModpackCompletionException; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; @@ -43,7 +44,7 @@ public class ModrinthCompletionTask extends Task { private final DefaultDependencyManager dependency; private final DefaultGameRepository repository; private final ModManager modManager; - private final String version; + private final GameInstanceID instanceId; private ModrinthManifest manifest; private final List> dependencies = new ArrayList<>(); @@ -55,29 +56,29 @@ public class ModrinthCompletionTask extends Task { * Constructor. * * @param dependencyManager the dependency manager. - * @param version the existent and physical version. + * @param instanceId the existent and physical version. */ - public ModrinthCompletionTask(DefaultDependencyManager dependencyManager, String version) { - this(dependencyManager, version, null); + public ModrinthCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { + this(dependencyManager, instanceId, null); } /** * Constructor. * * @param dependencyManager the dependency manager. - * @param version the existent and physical version. + * @param instanceId the existent and physical version. * @param manifest the CurseForgeModpack manifest. */ - public ModrinthCompletionTask(DefaultDependencyManager dependencyManager, String version, ModrinthManifest manifest) { + public ModrinthCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, ModrinthManifest manifest) { this.dependency = dependencyManager; this.repository = dependencyManager.getGameRepository(); - this.modManager = repository.getModManager(version); - this.version = version; + this.modManager = repository.getModManager(instanceId); + this.instanceId = instanceId; this.manifest = manifest; if (manifest == null) try { - Path manifestFile = repository.getInstanceRoot(version).resolve("modrinth.index.json"); + Path manifestFile = repository.getInstanceRoot(instanceId).resolve("modrinth.index.json"); if (Files.exists(manifestFile)) this.manifest = JsonUtils.fromJsonFile(manifestFile, ModrinthManifest.class); } catch (Exception e) { @@ -102,7 +103,7 @@ public void execute() throws Exception { if (manifest == null) return; - Path runDirectory = FileUtils.toAbsolute(repository.getRunDirectory(version)); + Path runDirectory = FileUtils.toAbsolute(repository.getRunDirectory(instanceId)); Path modsDirectory = runDirectory.resolve("mods"); for (ModrinthManifest.File file : manifest.getFiles()) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java index e09f418e32..886dd9ec8d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthInstallTask.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.*; import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Task; @@ -44,7 +45,7 @@ public class ModrinthInstallTask extends Task { private final Path zipFile; private final Modpack modpack; private final ModrinthManifest manifest; - private final String name; + private final GameInstanceID instanceId; private final String iconUrl; private final Path run; private final ModpackConfiguration config; @@ -53,21 +54,21 @@ public class ModrinthInstallTask extends Task { private final List> dependents = new ArrayList<>(4); private final List> dependencies = new ArrayList<>(1); - public ModrinthInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, ModrinthManifest manifest, String name, String iconUrl) { + public ModrinthInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, ModrinthManifest manifest, GameInstanceID instanceId, String iconUrl) { this.dependencyManager = dependencyManager; this.zipFile = zipFile; this.modpack = modpack; this.manifest = manifest; - this.name = name; + this.instanceId = instanceId; this.iconUrl = iconUrl; this.repository = dependencyManager.getGameRepository(); - this.run = repository.getRunDirectory(name); + this.run = repository.getRunDirectory(instanceId); - Path json = repository.getModpackConfiguration(name); - if (repository.hasInstance(name) && Files.notExists(json)) - throw new IllegalArgumentException("Version " + name + " already exists."); + Path json = repository.getModpackConfiguration(instanceId); + if (repository.hasInstance(instanceId) && Files.notExists(json)) + throw new IllegalArgumentException("Version " + instanceId + " already exists."); - GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion()); + GameBuilder builder = dependencyManager.newGameBuilder().name(instanceId).gameVersion(manifest.getGameVersion()); for (Map.Entry modLoader : manifest.getDependencies().entrySet()) { switch (modLoader.getKey()) { case "minecraft": @@ -96,7 +97,7 @@ public ModrinthInstallTask(DefaultDependencyManager dependencyManager, Path zipF Exception ex = event.getTask().getException(); if (event.isFailed()) { if (!(ex instanceof ModpackCompletionException)) { - repository.removeInstanceFromDisk(name); + repository.removeInstanceFromDisk(instanceId); } } }); @@ -107,7 +108,7 @@ public ModrinthInstallTask(DefaultDependencyManager dependencyManager, Path zipF config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(ModrinthManifest.class)); if (!ModrinthModpackProvider.INSTANCE.getName().equals(config.getType())) - throw new IllegalArgumentException("Version " + name + " is not a Modrinth modpack. Cannot update this version."); + throw new IllegalArgumentException("Version " + instanceId + " is not a Modrinth modpack. Cannot update this version."); } } catch (JsonParseException | IOException ignore) { } @@ -115,7 +116,7 @@ public ModrinthInstallTask(DefaultDependencyManager dependencyManager, Path zipF this.config = config; List subDirectories = Arrays.asList("/client-overrides", "/overrides"); dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), subDirectories, any -> true, config).withStage("hmcl.modpack")); - dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), subDirectories, manifest, ModrinthModpackProvider.INSTANCE, manifest.getName(), manifest.getVersionId(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack")); + dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), subDirectories, manifest, ModrinthModpackProvider.INSTANCE, manifest.getName(), manifest.getVersionId(), repository.getModpackConfiguration(instanceId)).withStage("hmcl.modpack")); URI iconUri = NetworkUtils.toURIOrNull(iconUrl); if (iconUri != null) { @@ -126,7 +127,7 @@ public ModrinthInstallTask(DefaultDependencyManager dependencyManager, Path zipF dependents.add(downloadIconTask = new CacheFileTask(dependencyManager.getDownloadProvider().injectURLWithCandidates(iconUrl))); } } - dependencies.add(new ModrinthCompletionTask(dependencyManager, name, manifest)); + dependencies.add(new ModrinthCompletionTask(dependencyManager, instanceId, manifest)); } @Override @@ -152,7 +153,7 @@ public void execute() throws Exception { } } - Path root = repository.getInstanceRoot(name); + Path root = repository.getInstanceRoot(instanceId); Files.createDirectories(root); JsonUtils.writeToJsonFile(root.resolve("modrinth.index.json"), manifest); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackProvider.java index fdaa506c92..6b4f7374bf 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackProvider.java @@ -20,6 +20,7 @@ import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.MismatchedModpackTypeException; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackProvider; @@ -41,16 +42,16 @@ public String getName() { } @Override - public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { - return new ModrinthCompletionTask(dependencyManager, version); + public Task createCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { + return new ModrinthCompletionTask(dependencyManager, instanceId); } @Override - public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { + public Task createUpdateTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { if (!(modpack.getManifest() instanceof ModrinthManifest modrinthManifest)) throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); - return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new ModrinthInstallTask(dependencyManager, zipFile, modpack, modrinthManifest, name, null)); + return new ModpackUpdateTask(dependencyManager.getGameRepository(), instanceId, new ModrinthInstallTask(dependencyManager, zipFile, modpack, modrinthManifest, instanceId, null)); } @Override @@ -58,8 +59,8 @@ public Modpack readManifest(ZipArchiveReader zip, Path file, Charset encoding) t ModrinthManifest manifest = JsonUtils.fromNonNullJson(CompressingUtils.readTextZipEntry(zip, "modrinth.index.json"), ModrinthManifest.class); return new Modpack(manifest.getName(), "", manifest.getVersionId(), manifest.getGameVersion(), manifest.getSummary(), encoding, manifest) { @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, String name, String iconUrl) { - return new ModrinthInstallTask(dependencyManager, zipFile, this, manifest, name, iconUrl); + public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, GameInstanceID instanceId, String iconUrl) { + return new ModrinthInstallTask(dependencyManager, zipFile, this, manifest, instanceId, iconUrl); } }; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java index e3ee144266..90039b5f93 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java @@ -244,10 +244,10 @@ public List getMavenOnlyFiles() { * See to do marks below for more information

* * @param patches List of all Json-Patch. - * @param versionID the version ID. Used when constructing a manifest. + * @param instanceId the version ID. Used when constructing a manifest. * @return The resolved instance. */ - public static ResolvedInstance resolveArtifact(List patches, String versionID) { + public static ResolvedInstance resolveArtifact(List patches, GameInstanceID instanceId) { if (patches.isEmpty()) { throw new IllegalArgumentException("Empty components."); } @@ -385,7 +385,7 @@ public static ResolvedInstance resolveArtifact(List patche mainClass = "org.jackhuang.hmcl.HMCLMultiMCBootstrap"; } - GameInstanceManifest version = new GameInstanceManifest(versionID) + GameInstanceManifest version = new GameInstanceManifest(instanceId) .setArguments(new Arguments().addGameArguments(minecraftArguments).addJVMArgumentsDirect(jvmArguments)) .setMainClass(mainClass) .setLibraries(libraries) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java index 5a3260fecb..4d68fbebd9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java @@ -24,10 +24,7 @@ import org.jackhuang.hmcl.download.game.GameAssetDownloadTask; import org.jackhuang.hmcl.download.game.GameDownloadTask; import org.jackhuang.hmcl.download.game.GameLibrariesTask; -import org.jackhuang.hmcl.game.Artifact; -import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.game.GameInstanceManifest; -import org.jackhuang.hmcl.game.Library; +import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.modpack.MinecraftInstanceTask; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; @@ -79,27 +76,27 @@ public final class MultiMCModpackInstallTask extends Task> dependents = new ArrayList<>(); private final List> dependencies = new ArrayList<>(); private final DefaultDependencyManager dependencyManager; - public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) { + public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, GameInstanceID instanceId) { this.zipFile = zipFile; this.modpack = modpack; this.manifest = manifest; - this.name = name; + this.instanceId = instanceId; this.dependencyManager = dependencyManager; this.repository = dependencyManager.getGameRepository(); - Path json = repository.getModpackConfiguration(name); - if (repository.hasInstance(name) && Files.notExists(json)) - throw new IllegalArgumentException("Version " + name + " already exists."); + Path json = repository.getModpackConfiguration(instanceId); + if (repository.hasInstance(instanceId) && Files.notExists(json)) + throw new IllegalArgumentException("Instance " + instanceId + " already exists."); onDone().register(event -> { if (event.isFailed()) - repository.removeInstanceFromDisk(name); + repository.removeInstanceFromDisk(instanceId); }); } @@ -112,8 +109,8 @@ public boolean doPreExecute() { public void preExecute() throws Exception { // Stage #0: General Setup { - Path run = repository.getRunDirectory(name); - Path json = repository.getModpackConfiguration(name); + Path run = repository.getRunDirectory(instanceId); + Path json = repository.getModpackConfiguration(instanceId); ModpackConfiguration config = null; try { @@ -121,7 +118,7 @@ public void preExecute() throws Exception { config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(MultiMCInstanceConfiguration.class)); if (!MultiMCModpackProvider.INSTANCE.getName().equals(config.getType())) - throw new IllegalArgumentException("Version " + name + " is not a MultiMC modpack. Cannot update this version."); + throw new IllegalArgumentException("Instance " + instanceId + " is not a MultiMC modpack. Cannot update this instance."); } } catch (JsonParseException | IOException ignore) { } @@ -133,7 +130,7 @@ public void preExecute() throws Exception { // TODO: Optimize unbearably slow ModpackInstallTask dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList(mcDirectory), any -> true, config).withStage("hmcl.modpack")); - dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(mcDirectory), manifest, MultiMCModpackProvider.INSTANCE, manifest.getName(), null, repository.getModpackConfiguration(name)).withStage("hmcl.modpack")); + dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList(mcDirectory), manifest, MultiMCModpackProvider.INSTANCE, manifest.getName(), null, repository.getModpackConfiguration(instanceId)).withStage("hmcl.modpack")); } // Stage #1: Load all related Json-Patch from meta maven or local mod pack. @@ -240,7 +237,7 @@ public void execute() throws Exception { for (int i = dependents.size() - 1; i >= 0; i--) { Task task = dependents.get(i); if (task instanceof MMCInstancePatchesAssembleTask) { - artifact = MultiMCInstancePatch.resolveArtifact(((MMCInstancePatchesAssembleTask) task).getResult(), name); + artifact = MultiMCInstancePatch.resolveArtifact(((MMCInstancePatchesAssembleTask) task).getResult(), instanceId); break; } } @@ -252,7 +249,7 @@ public void execute() throws Exception { Path libraries = root.resolve("libraries"); if (Files.exists(libraries)) - FileUtils.copyDirectory(libraries, repository.getInstanceRoot(name).resolve("libraries")); + FileUtils.copyDirectory(libraries, repository.getInstanceRoot(instanceId).resolve("libraries")); for (Library library : artifact.getVersion().getLibraries()) { if ("local".equals(library.getHint())) { @@ -278,7 +275,7 @@ public void execute() throws Exception { if (iconKey != null) { Path iconFile = root.resolve(iconKey + ".png"); if (Files.exists(iconFile)) { - FileUtils.copyFile(iconFile, repository.getInstanceRoot(name).resolve("icon.png")); + FileUtils.copyFile(iconFile, repository.getInstanceRoot(instanceId).resolve("icon.png")); } } } @@ -338,7 +335,7 @@ public void postExecute() throws Exception { Path root = getRootPath(fs).resolve("jarmods"); try (FileSystem mc = CompressingUtils.writable( - repository.getInstanceRoot(name).resolve(name + ".jar") + repository.getInstanceRoot(instanceId).resolve(instanceId + ".jar") ).setAutoDetectEncoding(true).build()) { for (String fileName : files) { try (FileSystem jm = CompressingUtils.readonly(root.resolve(fileName)).setAutoDetectEncoding(true).build()) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackProvider.java index 3aa4fa1e59..479095d981 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackProvider.java @@ -20,6 +20,7 @@ import kala.compress.archivers.zip.ZipArchiveEntry; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.MismatchedModpackTypeException; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackProvider; @@ -41,16 +42,16 @@ public String getName() { } @Override - public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { + public Task createCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { return null; } @Override - public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { + public Task createUpdateTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { if (!(modpack.getManifest() instanceof MultiMCInstanceConfiguration multiMCInstanceConfiguration)) throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); - return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new MultiMCModpackInstallTask(dependencyManager, zipFile, modpack, multiMCInstanceConfiguration, name)); + return new ModpackUpdateTask(dependencyManager.getGameRepository(), instanceId, new MultiMCModpackInstallTask(dependencyManager, zipFile, modpack, multiMCInstanceConfiguration, instanceId)); } private static String getRootEntryName(ZipArchiveReader file) throws IOException { @@ -85,8 +86,8 @@ public Modpack readManifest(ZipArchiveReader modpackFile, Path modpackPath, Char MultiMCInstanceConfiguration cfg = new MultiMCInstanceConfiguration(name, instanceStream, manifest); return new Modpack(cfg.getName(), "", "", cfg.getGameVersion(), cfg.getNotes(), encoding, cfg) { @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, String name, String iconUrl) { - return new MultiMCModpackInstallTask(dependencyManager, zipFile, this, cfg, name); + public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, GameInstanceID instanceId, String iconUrl) { + return new MultiMCModpackInstallTask(dependencyManager, zipFile, this, cfg, instanceId); } }; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java index ce16275012..2caca9ab70 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackCompletionTask.java @@ -22,6 +22,7 @@ import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.addon.LocalAddonManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.GetTask; @@ -43,24 +44,24 @@ public class ServerModpackCompletionTask extends Task { private final DefaultDependencyManager dependencyManager; private final DefaultGameRepository repository; - private final String version; + private final GameInstanceID instanceId; private ModpackConfiguration manifest; private GetTask dependent; private ServerModpackManifest remoteManifest; private final List> dependencies = new ArrayList<>(); - public ServerModpackCompletionTask(DefaultDependencyManager dependencyManager, String version) { - this(dependencyManager, version, null); + public ServerModpackCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { + this(dependencyManager, instanceId, null); } - public ServerModpackCompletionTask(DefaultDependencyManager dependencyManager, String version, ModpackConfiguration manifest) { + public ServerModpackCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, ModpackConfiguration manifest) { this.dependencyManager = dependencyManager; this.repository = dependencyManager.getGameRepository(); - this.version = version; + this.instanceId = instanceId; if (manifest == null) { try { - Path manifestFile = repository.getModpackConfiguration(version); + Path manifestFile = repository.getModpackConfiguration(instanceId); if (Files.exists(manifestFile)) { this.manifest = JsonUtils.fromJsonFile(manifestFile, ModpackConfiguration.typeOf(ServerModpackManifest.class)); } @@ -112,7 +113,7 @@ public void execute() throws Exception { Map oldAddons = toMap(manifest.getManifest().getAddons()); Map newAddons = toMap(remoteManifest.getAddons()); if (!Objects.equals(oldAddons, newAddons)) { - GameBuilder builder = dependencyManager.gameBuilder().name(version); + GameBuilder builder = dependencyManager.newGameBuilder().name(instanceId); for (ServerModpackManifest.Addon addon : remoteManifest.getAddons()) { builder.version(addon.getId(), addon.getVersion()); } @@ -120,7 +121,7 @@ public void execute() throws Exception { dependencies.add(builder.buildAsync()); } - Path rootPath = repository.getInstanceRoot(version).toAbsolutePath().normalize(); + Path rootPath = repository.getInstanceRoot(instanceId).toAbsolutePath().normalize(); Map files = manifest.getManifest().getFiles().stream() .collect(Collectors.toMap(ModpackConfiguration.FileInformation::getPath, Function.identity())); @@ -128,7 +129,7 @@ public void execute() throws Exception { Set remoteFiles = remoteManifest.getFiles().stream().map(ModpackConfiguration.FileInformation::getPath) .collect(Collectors.toSet()); - Path runDirectory = repository.getRunDirectory(version).toAbsolutePath().normalize(); + Path runDirectory = repository.getRunDirectory(instanceId).toAbsolutePath().normalize(); Path modsDirectory = runDirectory.resolve("mods"); int total = 0; @@ -192,7 +193,7 @@ public boolean doPostExecute() { @Override public void postExecute() throws Exception { if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return; - Path manifestFile = repository.getModpackConfiguration(version); + Path manifestFile = repository.getModpackConfiguration(instanceId); Files.createDirectories(manifestFile.getParent()); JsonUtils.writeToJsonFile(manifestFile, new ModpackConfiguration<>(remoteManifest, this.manifest.getType(), this.manifest.getName(), this.manifest.getVersion(), remoteManifest.getFiles())); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java index 918f4acfe6..fc34ab1f6f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackLocalInstallTask.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.MinecraftInstanceTask; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; @@ -40,24 +41,24 @@ public class ServerModpackLocalInstallTask extends Task { private final Path zipFile; private final Modpack modpack; private final ServerModpackManifest manifest; - private final String name; + private final GameInstanceID instanceId; private final DefaultGameRepository repository; private final List> dependencies = new ArrayList<>(); private final List> dependents = new ArrayList<>(4); - public ServerModpackLocalInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, ServerModpackManifest manifest, String name) { + public ServerModpackLocalInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, Modpack modpack, ServerModpackManifest manifest, GameInstanceID instanceId) { this.zipFile = zipFile; this.modpack = modpack; this.manifest = manifest; - this.name = name; + this.instanceId = instanceId; this.repository = dependencyManager.getGameRepository(); - Path run = repository.getRunDirectory(name); + Path run = repository.getRunDirectory(instanceId); - Path json = repository.getModpackConfiguration(name); - if (repository.hasInstance(name) && Files.notExists(json)) - throw new IllegalArgumentException("Version " + name + " already exists."); + Path json = repository.getModpackConfiguration(instanceId); + if (repository.hasInstance(instanceId) && Files.notExists(json)) + throw new IllegalArgumentException("Version " + instanceId + " already exists."); - GameBuilder builder = dependencyManager.gameBuilder().name(name); + GameBuilder builder = dependencyManager.newGameBuilder().name(instanceId); for (ServerModpackManifest.Addon addon : manifest.getAddons()) { builder.version(addon.getId(), addon.getVersion()); } @@ -65,7 +66,7 @@ public ServerModpackLocalInstallTask(DefaultDependencyManager dependencyManager, dependents.add(builder.buildAsync()); onDone().register(event -> { if (event.isFailed()) - repository.removeInstanceFromDisk(name); + repository.removeInstanceFromDisk(instanceId); }); ModpackConfiguration config = null; @@ -74,12 +75,12 @@ public ServerModpackLocalInstallTask(DefaultDependencyManager dependencyManager, config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(ServerModpackManifest.class)); if (!ServerModpackProvider.INSTANCE.getName().equals(config.getType())) - throw new IllegalArgumentException("Version " + name + " is not a Server modpack. Cannot update this version."); + throw new IllegalArgumentException("Version " + instanceId + " is not a Server modpack. Cannot update this version."); } } catch (JsonParseException | IOException ignore) { } dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), Collections.singletonList("/overrides"), any -> true, config).withStage("hmcl.modpack")); - dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, ServerModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack")); + dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), Collections.singletonList("/overrides"), manifest, ServerModpackProvider.INSTANCE, modpack.getName(), modpack.getVersion(), repository.getModpackConfiguration(instanceId)).withStage("hmcl.modpack")); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackManifest.java index f706b0add4..41a44b7b1b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackManifest.java @@ -19,6 +19,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.modpack.ModpackManifest; @@ -126,8 +127,8 @@ public Modpack toModpack(Charset encoding) throws IOException { .orElseThrow(() -> new IOException("Cannot find game version")).getVersion(); return new Modpack(name, author, version, gameVersion, description, encoding, this) { @Override - public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, String name, String iconUrl) { - return new ServerModpackLocalInstallTask(dependencyManager, zipFile, this, ServerModpackManifest.this, name); + public Task getInstallTask(DefaultDependencyManager dependencyManager, Path zipFile, GameInstanceID instanceId, String iconUrl) { + return new ServerModpackLocalInstallTask(dependencyManager, zipFile, this, ServerModpackManifest.this, instanceId); } }; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackProvider.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackProvider.java index 01917c15d7..76e98f7fdd 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackProvider.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackProvider.java @@ -20,6 +20,7 @@ import com.google.gson.JsonParseException; import kala.compress.archivers.zip.ZipArchiveReader; import org.jackhuang.hmcl.download.DefaultDependencyManager; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.MismatchedModpackTypeException; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.modpack.ModpackProvider; @@ -41,16 +42,16 @@ public String getName() { } @Override - public Task createCompletionTask(DefaultDependencyManager dependencyManager, String version) { - return new ServerModpackCompletionTask(dependencyManager, version); + public Task createCompletionTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId) { + return new ServerModpackCompletionTask(dependencyManager, instanceId); } @Override - public Task createUpdateTask(DefaultDependencyManager dependencyManager, String name, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { + public Task createUpdateTask(DefaultDependencyManager dependencyManager, GameInstanceID instanceId, Path zipFile, Modpack modpack) throws MismatchedModpackTypeException { if (!(modpack.getManifest() instanceof ServerModpackManifest serverModpackManifest)) throw new MismatchedModpackTypeException(getName(), modpack.getManifest().getProvider().getName()); - return new ModpackUpdateTask(dependencyManager.getGameRepository(), name, new ServerModpackLocalInstallTask(dependencyManager, zipFile, modpack, serverModpackManifest, name)); + return new ModpackUpdateTask(dependencyManager.getGameRepository(), instanceId, new ServerModpackLocalInstallTask(dependencyManager, zipFile, modpack, serverModpackManifest, instanceId)); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java index 7f75a46019..e29f843869 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackRemoteInstallTask.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.GameBuilder; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -34,24 +35,24 @@ public class ServerModpackRemoteInstallTask extends Task { - private final String name; + private final GameInstanceID instanceId; private final DefaultDependencyManager dependency; private final DefaultGameRepository repository; private final List> dependencies = new ArrayList<>(1); private final List> dependents = new ArrayList<>(1); private final ServerModpackManifest manifest; - public ServerModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, ServerModpackManifest manifest, String name) { - this.name = name; + public ServerModpackRemoteInstallTask(DefaultDependencyManager dependencyManager, ServerModpackManifest manifest, GameInstanceID instanceId) { + this.instanceId = instanceId; this.dependency = dependencyManager; this.repository = dependencyManager.getGameRepository(); this.manifest = manifest; - Path json = repository.getModpackConfiguration(name); - if (repository.hasInstance(name) && Files.notExists(json)) - throw new IllegalArgumentException("Version " + name + " already exists."); + Path json = repository.getModpackConfiguration(instanceId); + if (repository.hasInstance(instanceId) && Files.notExists(json)) + throw new IllegalArgumentException("Instance " + instanceId + " already exists."); - GameBuilder builder = dependencyManager.gameBuilder().name(name); + GameBuilder builder = dependencyManager.newGameBuilder().name(instanceId); for (ServerModpackManifest.Addon addon : manifest.getAddons()) { builder.version(addon.getId(), addon.getVersion()); } @@ -59,7 +60,7 @@ public ServerModpackRemoteInstallTask(DefaultDependencyManager dependencyManager dependents.add(builder.buildAsync()); onDone().register(event -> { if (event.isFailed()) - repository.removeInstanceFromDisk(name); + repository.removeInstanceFromDisk(instanceId); }); ModpackConfiguration config; @@ -68,7 +69,7 @@ public ServerModpackRemoteInstallTask(DefaultDependencyManager dependencyManager config = JsonUtils.fromJsonFile(json, ModpackConfiguration.typeOf(ServerModpackManifest.class)); if (!MODPACK_TYPE.equals(config.getType())) - throw new IllegalArgumentException("Version " + name + " is not a Server modpack. Cannot update this version."); + throw new IllegalArgumentException("Instance " + instanceId + " is not a Server modpack. Cannot update this instance."); } } catch (JsonParseException | IOException ignore) { } @@ -86,7 +87,7 @@ public List> getDependencies() { @Override public void execute() throws Exception { - dependencies.add(new ServerModpackCompletionTask(dependency, name, new ModpackConfiguration<>(manifest, MODPACK_TYPE, manifest.getName(), manifest.getVersion(), Collections.emptyList()))); + dependencies.add(new ServerModpackCompletionTask(dependency, instanceId, new ModpackConfiguration<>(manifest, MODPACK_TYPE, manifest.getName(), manifest.getVersion(), Collections.emptyList()))); } public static final String MODPACK_TYPE = "Server"; From ce79064becde0cd5a4d317466b2c91f0e37e7026 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 14:19:05 +0800 Subject: [PATCH 39/85] refactor: update MainPage to use GameInstanceID instead of version strings --- .../java/org/jackhuang/hmcl/ui/main/MainPage.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index ff45d8c33b..8b2a0803ba 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -325,7 +325,7 @@ private void launchNoGame() { DownloadProvider downloadProvider = DownloadProviders.getDownloadProvider(); VersionList versionList = downloadProvider.getVersionListById("game"); - Holder gameVersionHolder = new Holder<>(); + Holder instanceHolder = new Holder<>(); Task task = versionList.refreshAsync("") .thenSupplyAsync(() -> versionList.getVersions("").stream() .filter(it -> it.getVersionType() == RELEASE) @@ -336,17 +336,21 @@ private void launchNoGame() { .thenComposeAsync(version -> { HMCLGameRepository repository = GameDirectoryManager.getSelectedRepository(); DefaultDependencyManager dependency = repository.getDependency(); - String gameVersion = gameVersionHolder.value = version.getGameVersion(); + + String gameVersion = version.getGameVersion(); + GameInstanceID instanceId = new GameInstanceID(gameVersion); + + instanceHolder.value = instanceId; return dependency.newGameBuilder() - .name(new GameInstanceID(gameVersion)) + .name(instanceId) .gameVersion(gameVersion) .buildAsync(); }) .whenComplete(any -> GameDirectoryManager.getSelectedRepository().refresh()) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { - GameDirectoryManager.getSelectedRepository().setSelectedInstance(new GameInstanceID(gameVersionHolder.value)); + GameDirectoryManager.getSelectedRepository().setSelectedInstance(instanceHolder.value); launch(); } else if (exception instanceof CancellationException) { Controllers.showToast(i18n("message.cancelled")); From 9df1c078552e27eb1d9b595b9a2169d14031edc4 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 14:19:45 +0800 Subject: [PATCH 40/85] refactor: remove string id overloads in GameRepository to use GameInstanceID --- .../org/jackhuang/hmcl/game/GameRepository.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index 000f92b95c..72ef875379 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -134,28 +134,12 @@ default Task refreshAsync() { /// @return the instance root directory Path getInstanceRoot(GameInstanceID instanceId); - /// Returns the instance root by string id. - /// - /// @param id the instance id string - /// @return the instance root - default Path getInstanceRoot(String id) { - return getInstanceRoot(new GameInstanceID(id)); - } - /// Returns the working directory used when launching an instance. /// /// @param instanceId the instance id /// @return the run directory Path getRunDirectory(GameInstanceID instanceId); - /// Returns the run directory by string id. - /// - /// @param id the instance id string - /// @return the run directory - default Path getRunDirectory(String id) { - return getRunDirectory(new GameInstanceID(id)); - } - /// Returns the base directory used to store shared libraries for a manifest. /// /// @param manifest the manifest whose libraries are being resolved From c81e4997cb1a56eba16a0ebff65e74918945353c Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 14:21:53 +0800 Subject: [PATCH 41/85] refactor: add instanceIdConflicts method to HMCLGameRepository and update duplicateInstance logic --- .../jackhuang/hmcl/game/HMCLGameRepository.java | 14 ++++++++++++++ .../org/jackhuang/hmcl/ui/versions/Instances.java | 9 +++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 2c5a61cb48..8b11576f7e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -844,6 +844,20 @@ public boolean instanceIdConflicts(String id) { } } + public boolean instanceIdConflicts(GameInstanceID id) { + if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { + // on Windows, filenames are case-insensitive + for (GameInstanceManifest manifest : getInstanceManifests()) { + if (manifest.id().toString().equalsIgnoreCase(id.toString())) { + return true; + } + } + return false; + } else { + return hasInstance(id); + } + } + public static long getAllocatedMemory(long minimum, long available, boolean auto) { if (auto) { available -= 512 * 1024 * 1024; // Reserve 512 MiB memory for off-heap memory and HMCL itself diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java index 82127cb06c..c1eeb6b897 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java @@ -210,17 +210,18 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { public static void duplicateInstance(HMCLGameRepository repository, GameInstanceID instanceId) { Controllers.prompt( new PromptDialogPane.Builder(i18n("version.manage.duplicate.prompt"), (res, handler) -> { - String newVersionName = ((PromptDialogPane.Builder.StringQuestion) res.get(1)).getValue(); + String newInstanceName = ((PromptDialogPane.Builder.StringQuestion) res.get(1)).getValue(); + GameInstanceID newInstanceId = new GameInstanceID(newInstanceName); boolean copySaves = ((PromptDialogPane.Builder.BooleanQuestion) res.get(2)).getValue(); - Task.runAsync(() -> repository.duplicateInstance(instanceId, new GameInstanceID(newVersionName), copySaves)) + Task.runAsync(() -> repository.duplicateInstance(instanceId, newInstanceId, copySaves)) .thenComposeAsync(repository.refreshAsync()) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { handler.resolve(); } else { handler.reject(StringUtils.getStackTrace(exception)); - if (!repository.instanceIdConflicts(newVersionName)) { - repository.removeInstanceFromDisk(new GameInstanceID(newVersionName)); + if (!repository.instanceIdConflicts(newInstanceId)) { + repository.removeInstanceFromDisk(newInstanceId); } } }).start(); From d8317c4b199dff620aa5ed590c2f27969017ac50 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 14:30:57 +0800 Subject: [PATCH 42/85] refactor: update repository to use GameInstanceID consistently across controllers and settings --- .../hmcl/game/HMCLGameRepository.java | 22 ++++++++----------- .../hmcl/setting/GameDirectoryManager.java | 12 +++++----- .../hmcl/setting/LauncherSettings.java | 18 ++++++--------- .../org/jackhuang/hmcl/ui/Controllers.java | 2 +- .../hmcl/ui/download/DownloadPage.java | 8 +------ .../org/jackhuang/hmcl/ui/main/MainPage.java | 15 +++++++------ .../org/jackhuang/hmcl/ui/main/RootPage.java | 2 +- .../terracotta/TerracottaControllerPage.java | 2 +- .../hmcl/ui/terracotta/TerracottaPage.java | 6 ++--- .../hmcl/ui/versions/DownloadListPage.java | 2 +- .../ui/versions/GameAdvancedListItem.java | 22 ++++++++----------- .../hmcl/ui/versions/GameListItem.java | 5 ++++- .../hmcl/setting/GameDirectoriesTest.java | 2 +- .../jackhuang/hmcl/setting/TypedIDTest.java | 5 +++-- 14 files changed, 55 insertions(+), 68 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 8b11576f7e..eb078e1172 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -20,8 +20,9 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; +import javafx.beans.binding.Binding; import javafx.beans.binding.Bindings; -import javafx.beans.binding.StringBinding; +import javafx.beans.binding.ObjectBinding; import javafx.scene.image.Image; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.download.DefaultDependencyManager; @@ -100,7 +101,7 @@ public record InstanceReference(HMCLGameRepository repository, @Nullable GameIns private final GameDirectory gameDirectory; /// The selected instance ID persisted for this repository's game directory. - private final StringBinding selectedInstance; + private final ObjectBinding<@Nullable GameInstanceID> selectedInstance; // instance game settings private final Map instanceGameSettings = new HashMap<>(); @@ -115,7 +116,7 @@ public record InstanceReference(HMCLGameRepository repository, @Nullable GameIns public HMCLGameRepository(GameDirectory gameDirectory) { super(gameDirectory.getPath().toPath()); this.gameDirectory = gameDirectory; - this.selectedInstance = Bindings.stringValueAt(settings().getSelectedInstance(), gameDirectory.getId()); + this.selectedInstance = Bindings.valueAt(settings().getSelectedInstance(), gameDirectory.getId()); gameDirectory.pathProperty().addListener((a, b, newValue) -> changeDirectory(newValue.toPath())); } @@ -125,30 +126,25 @@ public GameDirectory getGameDirectory() { } /// Returns the selected instance ID property for this repository's game directory. - public StringBinding selectedInstanceProperty() { + public Binding<@Nullable GameInstanceID> selectedInstanceProperty() { return selectedInstance; } /// Returns the selected instance ID for this repository's game directory. - public @Nullable String getSelectedInstance() { + public @Nullable GameInstanceID getSelectedInstance() { return selectedInstance.get(); } - public @Nullable GameInstanceID getSelectedInstance2() { - return selectedInstance.get() == null ? null : new GameInstanceID(selectedInstance.get()); - } - - /// Sets the selected instance ID for this repository's game directory. public void setSelectedInstance(@Nullable GameInstanceID instanceId) { - settings().setSelectedInstance(gameDirectory.getId(), instanceId == null ? null : instanceId.id()); + settings().setSelectedInstance(gameDirectory.getId(), instanceId); } /// Refreshes the selected instance ID after versions are loaded. public void refreshSelectedInstance() { - @Nullable GameInstanceID selectedInstance = settings().getSelectedInstance2(gameDirectory.getId()); + @Nullable GameInstanceID selectedInstance = settings().getSelectedInstance(gameDirectory.getId()); @Nullable GameInstanceID refreshedInstance = selectedInstance; - if (!hasInstance(refreshedInstance)) { + if (refreshedInstance == null || !hasInstance(refreshedInstance)) { refreshedInstance = getInstanceManifests().isEmpty() ? null : getInstanceManifests().iterator().next().id(); } if (!Objects.equals(selectedInstance, refreshedInstance)) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java index 21a60cab29..e662e8a67d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java @@ -133,10 +133,10 @@ private static boolean isGameDirectoryPath(GameDirectory gameDirectory, Portable private static final ObjectProperty<@UnknownNullability HMCLGameRepository> selectedRepository = new SimpleObjectProperty<>(GameDirectoryManager.class, "selectedRepository"); /// The selected instance ID projected from the selected repository. - private static final ReadOnlyStringWrapper selectedInstance = new ReadOnlyStringWrapper(GameDirectoryManager.class, "selectedInstance"); + private static final ReadOnlyObjectWrapper selectedInstance = new ReadOnlyObjectWrapper<>(GameDirectoryManager.class, "selectedInstance"); /// Updates [#selectedInstance] when the selected repository changes its selected instance. - private static final ChangeListener selectedRepositoryInstanceListener = + private static final ChangeListener selectedRepositoryInstanceListener = (observable, oldValue, newValue) -> selectedInstance.set(newValue); /// Initializes game directory state from the stores loaded by [SettingsManager]. @@ -467,18 +467,18 @@ public static ObjectProperty selectedRepositoryProperty() { } /// Returns the selected instance property projected from the selected repository. - public static ReadOnlyStringProperty selectedInstanceProperty() { + public static ReadOnlyObjectProperty<@Nullable GameInstanceID> selectedInstanceProperty() { return selectedInstance.getReadOnlyProperty(); } /// Returns the selected instance ID for the selected repository. - public static @Nullable String getSelectedInstance() { + public static @Nullable GameInstanceID getSelectedInstance() { return getSelectedRepository().getSelectedInstance(); } /// Sets the selected instance ID for the selected repository. - public static void setSelectedInstance(@Nullable String instance) { - getSelectedRepository().setSelectedInstance(instance == null ? null : new GameInstanceID(instance)); + public static void setSelectedInstance(@Nullable GameInstanceID instance) { + getSelectedRepository().setSelectedInstance(instance); } /// Listeners notified after the selected repository has loaded versions. diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java index 84f9ac8d7a..098aedfe98 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java @@ -610,38 +610,34 @@ public StringProperty proxyPasswordProperty() { /// /// This field is owned by [GameDirectoryManager]. Code outside [GameDirectoryManager] should not modify it directly. @SerializedName(PROPERTY_SELECTED_INSTANCE) - private final ObservableMap selectedInstance = FXCollections.observableHashMap(); + private final ObservableMap selectedInstance = FXCollections.observableHashMap(); /// Returns selected instance IDs keyed by game directory ID. /// /// The map stores persisted selected instance values by game directory ID. - public ObservableMap getSelectedInstance() { + public ObservableMap getSelectedInstance() { return selectedInstance; } /// Returns the selected instance ID for the given game directory ID. /// /// The value is loaded by the game repository for the matching game directory. - public @Nullable String getSelectedInstance(@Nullable GameDirectoryID gameDirectoryId) { + public @Nullable GameInstanceID getSelectedInstance(@Nullable GameDirectoryID gameDirectoryId) { return gameDirectoryId != null ? selectedInstance.get(gameDirectoryId) : null; } - public @Nullable GameInstanceID getSelectedInstance2(@Nullable GameDirectoryID gameDirectoryId) { - return gameDirectoryId != null ? new GameInstanceID(selectedInstance.get(gameDirectoryId)) : null; - } - /// Sets the selected instance ID for the given game directory ID. /// /// Blank values remove the persisted selected instance entry. - public void setSelectedInstance(@Nullable GameDirectoryID gameDirectoryId, @Nullable String selectedInstance) { + public void setSelectedInstance(@Nullable GameDirectoryID gameDirectoryId, @Nullable GameInstanceID selectedInstance) { if (gameDirectoryId == null) { return; } - if (StringUtils.isBlank(selectedInstance)) { - this.selectedInstance.remove(gameDirectoryId); - } else { + if (selectedInstance != null) { this.selectedInstance.put(gameDirectoryId, selectedInstance); + } else { + this.selectedInstance.remove(gameDirectoryId); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index 139b9581a4..6bad25b1c3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -703,7 +703,7 @@ public static void onHyperlinkAction(String href) { break; case "hmcl://game/launch": var repository = GameDirectoryManager.getSelectedRepository(); - Instances.launch(repository, repository.getSelectedInstance2(), LauncherHelper::setKeep); + Instances.launch(repository, repository.getSelectedInstance(), LauncherHelper::setKeep); break; } } else { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index 100681242e..997aabf666 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -144,13 +144,7 @@ private static Supplier loadVersionFor(Supplier nodeSuppl public static void download(DownloadProvider downloadProvider, HMCLGameRepository repository, @Nullable GameInstanceID instanceId, RemoteAddon.Version file, String subdirectoryName) { if (instanceId == null) { - @Nullable String selectedInstance = repository.getSelectedInstance(); - if (selectedInstance != null) { - try { - instanceId = new GameInstanceID(selectedInstance); - } catch (IllegalArgumentException ignored) { - } - } + instanceId = repository.getSelectedInstance(); } Path runDirectory = instanceId != null && repository.hasInstance(instanceId) ? repository.getRunDirectory(instanceId) : repository.getBaseDirectory(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 8b2a0803ba..0eaa34e226 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -73,6 +73,7 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.platform.Platform; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.List; @@ -91,7 +92,7 @@ public final class MainPage extends StackPane implements DecoratorPage { private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(); - private final StringProperty currentGame = new SimpleStringProperty(this, "currentGame"); + private final ObjectProperty<@Nullable GameInstanceID> currentGame = new SimpleObjectProperty<>(this, "currentGame"); private final BooleanProperty showUpdate = new SimpleBooleanProperty(this, "showUpdate"); private final BooleanProperty showUpdateDialog = new SimpleBooleanProperty(this, "showUpdateDialog"); private final ObjectProperty latestVersion = new SimpleObjectProperty<>(this, "latestVersion"); @@ -230,7 +231,7 @@ public final class MainPage extends StackPane implements DecoratorPage { private Tooltip tooltip; @Override - public void accept(String currentGame) { + public void accept(@Nullable GameInstanceID currentGame) { if (currentGame == null) { launchLabel.setText(i18n("version.launch.empty")); currentLabel.setText(null); @@ -241,7 +242,7 @@ public void accept(String currentGame) { FXUtils.installFastTooltip(launchButton, tooltip); } else { launchLabel.setText(i18n("version.launch")); - currentLabel.setText(currentGame); + currentLabel.setText(currentGame.toString()); graphic.getChildren().setAll(launchLabel, currentLabel); FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launch); if (tooltip != null) @@ -318,7 +319,7 @@ private void doAnimation(boolean show) { private void launch() { HMCLGameRepository repository = GameDirectoryManager.getSelectedRepository(); - Instances.launch(repository, repository.getSelectedInstance2()); + Instances.launch(repository, repository.getSelectedInstance()); } private void launchNoGame() { @@ -391,14 +392,14 @@ public HMCLGameRepository getRepository() { } public GameInstanceID getCurrentGame() { - return new GameInstanceID(currentGame.get()); + return currentGame.get(); } - public StringProperty currentGameProperty() { + public ObjectProperty<@Nullable GameInstanceID> currentGameProperty() { return currentGame; } - public void setCurrentGame(String currentGame) { + public void setCurrentGame(@Nullable GameInstanceID currentGame) { this.currentGame.set(currentGame); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index c1359cc207..a16982890e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -155,7 +155,7 @@ protected Skin(RootPage control) { // second item in left sidebar GameAdvancedListItem gameListItem = new GameAdvancedListItem(); gameListItem.setOnAction(e -> { - GameInstanceID instanceId = GameDirectoryManager.getSelectedRepository().getSelectedInstance2(); + GameInstanceID instanceId = GameDirectoryManager.getSelectedRepository().getSelectedInstance(); if (instanceId == null) { Controllers.navigate(Controllers.getGameListPage()); } else { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java index 33fb7663ef..2b558e8dba 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java @@ -212,7 +212,7 @@ public TerracottaControllerPage() { MessageDialogPane.MessageType.QUESTION ).addAction(i18n("version.launch"), () -> { var repository = GameDirectoryManager.getSelectedRepository(); - Instances.launch(repository, repository.getSelectedInstance2(), launcherHelper -> { + Instances.launch(repository, repository.getSelectedInstance(), launcherHelper -> { launcherHelper.setKeep(); launcherHelper.setDisableOfflineSkin(); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java index 533461b75a..5e0e8f727b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java @@ -55,7 +55,7 @@ public class TerracottaPage extends DecoratorAnimatedPage implements DecoratorPa private final TransitionPane transitionPane = new TransitionPane(); @SuppressWarnings("unused") - private ChangeListener instanceChangeListenerHolder; + private ChangeListener instanceChangeListenerHolder; public TerracottaPage() { statusPage.setNodeSupplier(TerracottaControllerPage::new); @@ -80,13 +80,13 @@ public TerracottaPage() { .add(accountListItem) .addNavigationDrawerItem(i18n("version.launch"), SVG.ROCKET_LAUNCH, () -> { var repository = GameDirectoryManager.getSelectedRepository(); - Instances.launch(repository, repository.getSelectedInstance2(), launcherHelper -> { + Instances.launch(repository, repository.getSelectedInstance(), launcherHelper -> { launcherHelper.setKeep(); launcherHelper.setDisableOfflineSkin(); }); }, item -> { instanceChangeListenerHolder = FXUtils.onWeakChangeAndOperate(GameDirectoryManager.selectedInstanceProperty(), - instanceName -> item.setSubtitle(StringUtils.isNotBlank(instanceName) ? instanceName : i18n("version.empty")) + instanceName -> item.setSubtitle(instanceName != null ? instanceName.toString() : i18n("version.empty")) ); MainPage mainPage = Controllers.getRootPage().getMainPage(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index 036e7e1ca4..a7fe4463a8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -127,7 +127,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instances.setAll(repository.getDisplayInstanceManifests() .map(GameInstanceManifest::id) .toList()); - selectedInstance.set(repository.getSelectedInstance2()); + selectedInstance.set(repository.getSelectedInstance()); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java index 4de9d3129c..d84d031e14 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java @@ -45,28 +45,24 @@ public GameAdvancedListItem() { AdvancedListItem.setAlignment(imageContainer, Pos.CENTER); setLeftGraphic(imageContainer); - holder.add(FXUtils.onWeakChangeAndOperate(GameDirectoryManager.selectedInstanceProperty(), this::loadVersion)); + holder.add(FXUtils.onWeakChangeAndOperate(GameDirectoryManager.selectedInstanceProperty(), this::loadInstance)); } - private void loadVersion(String version) { + private void loadInstance(GameInstanceID instanceId) { if (GameDirectoryManager.getSelectedRepository() != repository) { repository = GameDirectoryManager.getSelectedRepository(); if (repository != null) { onInstanceIconChangedListener = repository.onInstanceIconChanged.registerWeak(event -> { - this.loadVersion(repository.getSelectedInstance()); + this.loadInstance(repository.getSelectedInstance()); }); } } - if (version != null && repository != null) { - try { - GameInstanceID instanceId = new GameInstanceID(version); - if (repository.hasInstance(instanceId)) { - setTitle(i18n("version.manage.manage")); - setSubtitle(version); - imageContainer.setImage(repository.getInstanceIconImage(instanceId)); - return; - } - } catch (IllegalArgumentException ignored) { + if (instanceId != null && repository != null) { + if (repository.hasInstance(instanceId)) { + setTitle(i18n("version.manage.manage")); + setSubtitle(instanceId.toString()); + imageContainer.setImage(repository.getInstanceIconImage(instanceId)); + return; } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java index 1a715a17ed..44e6be52cc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java @@ -35,7 +35,10 @@ public GameListItem(HMCLGameRepository repository, GameInstanceID instanceId) { super(repository, instanceId); this.isModpack = repository.isModpack(instanceId); selected.bind(Bindings.createBooleanBinding( - () -> repository.getGameDirectory() == GameDirectoryManager.getSelectedGameDirectory() && Objects.equals(repository.getSelectedInstance2(), instanceId), + () -> { + if (repository.getGameDirectory() != GameDirectoryManager.getSelectedGameDirectory()) return false; + return Objects.equals(repository.getSelectedInstance(), instanceId); + }, GameDirectoryManager.selectedGameDirectoryProperty(), GameDirectoryManager.selectedInstanceProperty())); } diff --git a/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java b/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java index 8a8983fa69..cd41d0a901 100644 --- a/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java +++ b/HMCL/src/test/java/org/jackhuang/hmcl/setting/GameDirectoriesTest.java @@ -141,7 +141,7 @@ public void migratesLegacySelectedVersionsFromConfigurations() { assertFalse(settings.has("configurations")); assertEquals(id, config.selectedGameDirectoryProperty().get()); - assertEquals("1.20.1", config.getSelectedInstance(id)); + assertEquals("1.20.1", config.getSelectedInstance(id).toString()); JsonObject serialized = JsonParser.parseString(config.toJson()).getAsJsonObject(); assertEquals("1.20.1", serialized diff --git a/HMCL/src/test/java/org/jackhuang/hmcl/setting/TypedIDTest.java b/HMCL/src/test/java/org/jackhuang/hmcl/setting/TypedIDTest.java index 2c944171a9..3644b879cb 100644 --- a/HMCL/src/test/java/org/jackhuang/hmcl/setting/TypedIDTest.java +++ b/HMCL/src/test/java/org/jackhuang/hmcl/setting/TypedIDTest.java @@ -20,6 +20,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.jackhuang.hmcl.auth.AccountID; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jetbrains.annotations.NotNullByDefault; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -100,7 +101,7 @@ public void serializesLauncherSettingsMapKeysAsPrefixedStrings() { GameDirectoryID id = GameDirectoryID.parse("game-directory:123e4567-e89b-12d3-a456-426614174000"); LauncherSettings settings = new LauncherSettings(); - settings.setSelectedInstance(id, "1.20.1"); + settings.setSelectedInstance(id, new GameInstanceID("1.20.1")); JsonObject serialized = JsonParser.parseString(settings.toJson()).getAsJsonObject(); LauncherSettings deserialized = Objects.requireNonNull(LauncherSettings.fromJson(serialized)); @@ -108,6 +109,6 @@ public void serializesLauncherSettingsMapKeysAsPrefixedStrings() { .getAsJsonObject(LauncherSettings.PROPERTY_SELECTED_INSTANCE) .get(id.toString()) .getAsString()); - assertEquals("1.20.1", deserialized.getSelectedInstance(id)); + assertEquals("1.20.1", deserialized.getSelectedInstance(id).toString()); } } From 90ec6c3937327e07b40f97a3cb905567786c82d7 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 14:34:17 +0800 Subject: [PATCH 43/85] refactor: remove string id overloads and update methods to use GameInstanceID in DefaultGameRepository and GameRepository --- .../hmcl/game/HMCLGameRepository.java | 15 ++------ .../hmcl/game/DefaultGameRepository.java | 37 ------------------- .../jackhuang/hmcl/game/GameRepository.java | 25 ------------- 3 files changed, 4 insertions(+), 73 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index eb078e1172..9a160b67c8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -825,18 +825,11 @@ public static boolean isValidInstanceId(String id) { /** * Returns true if the given version id conflicts with an existing version. */ - public boolean instanceIdConflicts(String id) { - if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { - // on Windows, filenames are case-insensitive - for (GameInstanceManifest manifest : getInstanceManifests()) { - String existingId = manifest.id().toString(); - if (existingId.equalsIgnoreCase(id)) { - return true; - } - } + public boolean instanceIdConflicts(String instanceId) { + try { + return instanceIdConflicts(new GameInstanceID(instanceId)); + } catch (IllegalArgumentException e) { return false; - } else { - return hasInstance(id); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index ceb6db929c..f511a71a45 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -389,14 +389,6 @@ public boolean renameInstance(GameInstanceID from, GameInstanceID to) { } } - public boolean removeInstanceFromDisk(String id) { - try { - return removeInstanceFromDisk(new GameInstanceID(id)); - } catch (IllegalArgumentException e) { - return false; - } - } - public boolean removeInstanceFromDisk(GameInstanceID id) { if (EventBus.EVENT_BUS.fireEvent(new RemoveVersionEvent(this, id.id())) == Event.Result.DENY) { return false; @@ -597,10 +589,6 @@ public Path getModpackConfiguration(GameInstanceID instanceId) { return getInstanceRoot(instanceId).resolve("modpack.json"); } - public Path getModpackConfiguration(String instanceId) { - return getModpackConfiguration(new GameInstanceID(instanceId)); - } - @Nullable public ModpackConfiguration readModpackConfiguration(GameInstanceID instanceId) throws IOException, NoSuchGameInstanceException { if (!hasInstance(instanceId)) throw new NoSuchGameInstanceException(instanceId); @@ -609,51 +597,26 @@ public ModpackConfiguration readModpackConfiguration(GameInstanceID instanceI return JsonUtils.fromJsonFile(file, ModpackConfiguration.class); } - @Nullable - public ModpackConfiguration readModpackConfiguration(String instanceId) throws IOException, NoSuchGameInstanceException { - return readModpackConfiguration(new GameInstanceID(instanceId)); - } - public boolean isModpack(GameInstanceID instanceId) { return Files.exists(getModpackConfiguration(instanceId)); } - public boolean isModpack(String instanceId) { - return isModpack(new GameInstanceID(instanceId)); - } - public Path getSavesDirectory(GameInstanceID instanceId) { return getRunDirectory(instanceId).resolve("saves"); } - public Path getSavesDirectory(String instanceId) { - return getSavesDirectory(new GameInstanceID(instanceId)); - } - public Path getBackupsDirectory(GameInstanceID instanceID) { return getRunDirectory(instanceID).resolve("backups"); } - public Path getBackupsDirectory(String instanceId) { - return getBackupsDirectory(new GameInstanceID(instanceId)); - } - public Path getSchematicsDirectory(GameInstanceID instanceId) { return getRunDirectory(instanceId).resolve("schematics"); } - public Path getSchematicsDirectory(String instanceId) { - return getSchematicsDirectory(new GameInstanceID(instanceId)); - } - public ModManager getModManager(GameInstanceID instanceId) { return new ModManager(this, instanceId); } - public ModManager getModManager(String instanceId) { - return new ModManager(this, new GameInstanceID(instanceId)); - } - public ResourcePackManager getResourcePackManager(GameInstanceID instanceId) { return new ResourcePackManager(this, instanceId); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index 72ef875379..2a1490e006 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -21,7 +21,6 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.Platform; import org.jetbrains.annotations.NotNullByDefault; -import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.Files; @@ -55,22 +54,6 @@ public interface GameRepository { /// @return whether the instance exists boolean hasInstance(GameInstanceID instanceId); - /// Returns whether the instance exists. - /// - /// @param id the instance id string - /// @return whether the instance exists - default boolean hasInstance(@Nullable String id) { - if (id == null) { - return false; - } - - try { - return hasInstance(new GameInstanceID(id)); - } catch (IllegalArgumentException e) { - return false; - } - } - /// Returns the stored manifest for an instance without resolving inheritance or patches. /// /// @param instanceId the instance id @@ -78,14 +61,6 @@ default boolean hasInstance(@Nullable String id) { /// @throws NoSuchGameInstanceException if the instance is not loaded in this repository GameInstanceManifest getInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; - /// Returns an instance manifest by string id. - /// - /// @param id the instance id string - /// @return the instance manifest - default GameInstanceManifest getInstanceManifest(String id) throws NoSuchGameInstanceException { - return getInstanceManifest(new GameInstanceID(id)); - } - /// Returns a cached launch-ready manifest view for the instance. /// /// @param instanceId the instance id From 30b0fc028290136336db94076a20871b744c07d0 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 14:35:11 +0800 Subject: [PATCH 44/85] refactor: update LibraryAnalyzer to use GameInstanceManifest instead of version strings --- .../org/jackhuang/hmcl/download/LibraryAnalyzer.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 0cb419d24c..47f42c0bfa 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -154,8 +154,8 @@ public GameInstanceManifest build() { return version; } - public static LibraryAnalyzer analyze(GameInstanceManifest version, String gameVersion) { - if (version.getInheritsFrom() != null) + public static LibraryAnalyzer analyze(GameInstanceManifest manifest, String gameVersion) { + if (manifest.getInheritsFrom() != null) throw new IllegalArgumentException("LibraryAnalyzer can only analyze independent game version"); Map> libraries = new HashMap<>(); @@ -164,22 +164,22 @@ public static LibraryAnalyzer analyze(GameInstanceManifest version, String gameV libraries.put(LibraryType.MINECRAFT.getPatchId(), pair(null, gameVersion)); } - List rawLibraries = version.getLibraries(); + List rawLibraries = manifest.getLibraries(); for (Library library : rawLibraries) { for (LibraryType type : LibraryType.values()) { if (type.matchLibrary(library, rawLibraries)) { - libraries.put(type.getPatchId(), pair(library, type.patchVersion(version, library.getVersion()))); + libraries.put(type.getPatchId(), pair(library, type.patchVersion(manifest, library.getVersion()))); break; } } } - for (GameInstancePatch patch : version.getPatches()) { + for (GameInstancePatch patch : manifest.getPatches()) { if (patch.isHidden()) continue; libraries.put(patch.getId(), pair(null, patch.getVersion())); } - return new LibraryAnalyzer(version, libraries); + return new LibraryAnalyzer(manifest, libraries); } public static boolean isModded(GameRepository provider, GameInstanceManifest version) { From 072cdee768c64b1457211fe3f5fa4f0677193f32 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 14:56:54 +0800 Subject: [PATCH 45/85] refactor: update methods to use standaloneManifest instead of resolved manifest --- .../jackhuang/hmcl/game/LauncherHelper.java | 2 +- .../ui/download/AdditionalInstallersPage.java | 4 +- .../hmcl/ui/game/GameSettingsPage.java | 2 +- .../hmcl/ui/versions/DownloadPage.java | 2 +- .../jackhuang/hmcl/ui/versions/GameItem.java | 2 +- .../hmcl/ui/versions/InstallerListPage.java | 2 +- .../hmcl/ui/versions/ModListPage.java | 2 +- .../jackhuang/hmcl/addon/mod/ModManager.java | 2 +- .../download/DefaultDependencyManager.java | 8 +-- .../hmcl/game/DefaultGameRepository.java | 27 ++++------ .../hmcl/game/GameInstanceManifest.java | 51 +++++++------------ .../jackhuang/hmcl/game/GameRepository.java | 26 +--------- .../modpack/mcbbs/McbbsModpackExportTask.java | 2 +- .../modrinth/ModrinthModpackExportTask.java | 2 +- .../multimc/MultiMCModpackExportTask.java | 2 +- .../server/ServerModpackExportTask.java | 2 +- .../hmcl/game/GameInstanceManifestTest.java | 5 +- 17 files changed, 48 insertions(+), 95 deletions(-) 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 f2af29b08e..18d52d36a4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -148,7 +148,7 @@ private void launch0() { PROCESSES.removeIf(it -> it.get() == null); DefaultDependencyManager dependencyManager = repository.getDependency(); - AtomicReference version = new AtomicReference<>(MaintainTask.maintain(repository, repository.getResolvedInstanceManifest(selectedInstanceId).manifest())); + AtomicReference version = new AtomicReference<>(MaintainTask.maintain(repository, repository.getResolvedInstanceManifest(selectedInstanceId).launchManifest())); Optional gameVersion = repository.getGameVersion(version.get()); boolean integrityCheck = repository.unmarkInstanceLaunchedAbnormally(selectedInstanceId); CountDownLatch launchingLatch = new CountDownLatch(1); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java index a394ea5a67..6575f93d91 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java @@ -80,8 +80,8 @@ private String getVersion(String id) { @Override protected void reload() { - GameInstanceManifest resolved = version.resolvePreservingPatches(repository); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, repository.getGameVersion(resolved).orElse(null)); + GameInstanceManifest standalone = repository.resolve(version).standaloneManifest(); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(standalone, repository.getGameVersion(standalone).orElse(null)); String game = analyzer.getVersion(MINECRAFT).orElse(null); String currentGameVersion = Lang.nonNull(getVersion("game"), game); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java index e2071e9941..af0608b2f1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java @@ -2626,7 +2626,7 @@ private void initJavaSubtitle() { if (this.instanceId == null) { version = null; } else { - version = repository != null && loadedInstanceId != null ? repository.getResolvedInstanceManifest(loadedInstanceId).manifest() : null; + version = repository != null && loadedInstanceId != null ? repository.getResolvedInstanceManifest(loadedInstanceId).launchManifest() : null; } try { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index 136d286357..f0d89aab7d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -272,7 +272,7 @@ protected DownloadPageSkin(DownloadPage control) { if (control.instanceReference.repository() != null && control.instanceReference.instanceId() != null) { HMCLGameRepository repository = control.instanceReference.repository(); - GameInstanceManifest game = repository.getResolvedPreservingPatchesInstanceManifest(control.instanceReference.instanceId()).manifest(); + GameInstanceManifest game = repository.getResolvedInstanceManifest(control.instanceReference.instanceId()).standaloneManifest(); String gameVersion = repository.getGameVersion(game).orElse(null); if (gameVersion != null && control.versions.containsKey(gameVersion)) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java index 1ca0caabf8..e17bc4ddf0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java @@ -102,7 +102,7 @@ record Result(@Nullable String gameVersion, @Nullable String tag) { } StringBuilder libraries = new StringBuilder(Objects.requireNonNullElse(result.gameVersion, i18n("message.unknown"))); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesInstanceManifest(instanceId).manifest(), result.gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), result.gameVersion); for (LibraryAnalyzer.LibraryMark mark : analyzer) { String libraryId = mark.getLibraryId(); String libraryVersion = mark.getLibraryVersion(); 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 6a8126354a..69417ad611 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 @@ -72,7 +72,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID CompletableFuture.supplyAsync(() -> { gameVersion = repository.getGameVersion(version).orElse(null); - return LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesInstanceManifest(instanceId).manifest(), gameVersion); + return LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); }).thenAcceptAsync(analyzer -> { itemsProperty().clear(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java index 89ef00528e..328e746a00 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java @@ -92,7 +92,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID this.repository = repository; this.instanceId = instanceId; - GameInstanceManifest resolved = repository.getResolvedPreservingPatchesInstanceManifest(instanceId).manifest(); + GameInstanceManifest resolved = repository.getResolvedInstanceManifest(instanceId).standaloneManifest(); this.gameVersion = repository.getGameVersion(resolved).orElse(null); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion); modded.set(analyzer.hasModLoader()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java index 1f982b5dee..79ca0ece44 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java @@ -181,7 +181,7 @@ public void refresh() throws IOException { localMods.clear(); try { - analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedPreservingPatchesInstanceManifest(instanceId).manifest(), null); + analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedInstanceManifest(instanceId).standaloneManifest(), null); } catch (NoSuchGameInstanceException e) { throw new IOException(e); } 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 153a2ed2dc..dfbd12aa37 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -108,9 +108,9 @@ public Task checkPatchCompletionAsync(GameInstanceManifest version, boolean i if (gameVersion == null) return null; GameInstanceManifest original = repository.getInstanceManifest(version.id()); - GameInstanceManifest resolved = original.resolvePreservingPatches(repository); + GameInstanceManifest standalone = repository.resolve(original).standaloneManifest(); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolved, gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(standalone, gameVersion); for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values()) { if (!analyzer.has(type)) continue; @@ -121,7 +121,7 @@ public Task checkPatchCompletionAsync(GameInstanceManifest version, boolean i Matcher matcher = Pattern.compile("^([0-9.]+)_(?HD_.+)$").matcher(optifineVersion); return matcher.find() ? matcher.group("optifine") : optifineVersion; }) - .orElseGet(() -> resolved.getPatches().stream() + .orElseGet(() -> standalone.getPatches().stream() .filter(patch -> "optifine".equals(patch.getId())) .findAny() .map(GameInstancePatch::getVersion) @@ -219,7 +219,7 @@ public Task removeLibraryAsync(GameInstanceManifest versio // If we want to remove a library in dependent version, we should keep the dependents not changed // So resolving this game version to preserve all information in this version.json is necessary. return Task.supplyAsync(() -> { - GameInstanceManifest independentVersion = version.resolvePreservingPatches(repository); + GameInstanceManifest independentVersion = repository.resolve(version).standaloneManifest(); String gameVersion = repository.getGameVersion(independentVersion).orElse(null); return LibraryAnalyzer.analyze(independentVersion, gameVersion).removeLibrary(libraryId).build(); }); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index f511a71a45..3beb92798b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -333,7 +333,7 @@ public Path getRunDirectory(GameInstanceID instanceId) { @Override public Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceException { - return getInstanceJar(getResolvedInstanceManifest(instanceId).manifest()); + return getInstanceJar(getResolvedInstanceManifest(instanceId).launchManifest()); } @Override @@ -437,7 +437,7 @@ public boolean removeInstanceFromDisk(GameInstanceID id) { @Override public Optional getGameVersion(GameInstanceManifest manifest) { try { - GameInstanceManifest resolved = resolve(manifest).manifest(); + GameInstanceManifest resolved = resolve(manifest).launchManifest(); Path instanceJar = getInstanceJar(resolved); return gameVersions.computeIfAbsent(instanceJar, jar -> { Optional gameVersion = GameVersion.minecraftVersion(jar); @@ -623,22 +623,13 @@ public ResourcePackManager getResourcePackManager(GameInstanceID instanceId) { @Override public GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException { - return toResolved(status.resolve(manifest, new HashSet<>())); - } - - @Override - public GameInstanceManifest.Standalone resolvePreservingPatches(GameInstanceManifest manifest) throws NoSuchGameInstanceException { - GameInstanceManifest standaloneManifest = status.resolvePreservingPatches(manifest, new HashSet<>()); - GameInstanceID jar = resolve(manifest).manifest().jar(); + GameInstanceManifest launchManifest = status.resolve(manifest, new HashSet<>()).withPatches(null); + GameInstanceManifest standaloneManifest = status.resolveStandalone(manifest, new HashSet<>()); + GameInstanceID jar = launchManifest.jar(); if (jar != null) { standaloneManifest = standaloneManifest.withJar(jar); } - return new GameInstanceManifest.Standalone(standaloneManifest); - } - - private static GameInstanceManifest.Resolved toResolved(GameInstanceManifest manifest) { - List appliedPatches = manifest.patches() == null ? List.of() : manifest.patches(); - return new GameInstanceManifest.Resolved(manifest.withPatches(null), appliedPatches); + return new GameInstanceManifest.Resolved(launchManifest, standaloneManifest); } protected static class Status { @@ -693,8 +684,8 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, return currentManifest.withId(manifest.id()); } - private GameInstanceManifest resolvePreservingPatches(GameInstanceManifest manifest, - Set resolvedSoFar) throws NoSuchGameInstanceException { + private GameInstanceManifest resolveStandalone(GameInstanceManifest manifest, + Set resolvedSoFar) throws NoSuchGameInstanceException { GameInstanceManifest currentManifest = manifest.root() ? manifest : addPatches( @@ -712,7 +703,7 @@ private GameInstanceManifest resolvePreservingPatches(GameInstanceManifest manif } currentManifest = addPatches( - addPatches(resolvePreservingPatches(parentInstance.manifest, resolvedSoFar), Collections.singleton(manifest.toPatch())), + addPatches(resolveStandalone(parentInstance.manifest, resolvedSoFar), Collections.singleton(manifest.toPatch())), manifest.patches()); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index b1a5add53a..dbe44c3983 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -60,39 +60,30 @@ public record GameInstanceManifest( @Nullable @Unmodifiable JsonObject rawJson ) { - /// A launch-ready manifest view with inheritance and pending patches applied. + /// Resolved manifest views with inheritance folded. /// - /// @param manifest the final manifest data used by launch-time consumers - /// @param appliedPatches patches that have already been applied to produce the final manifest + /// @param launchManifest the final manifest data used by launch-time consumers + /// @param standaloneManifest the standalone manifest data with pending patches preserved @NotNullByDefault - public record Resolved(GameInstanceManifest manifest, - @Unmodifiable List appliedPatches) { + public record Resolved(GameInstanceManifest launchManifest, + GameInstanceManifest standaloneManifest) { /// Creates a resolved manifest view. public Resolved { - Objects.requireNonNull(manifest); - appliedPatches = List.copyOf(appliedPatches); + Objects.requireNonNull(launchManifest); + Objects.requireNonNull(standaloneManifest); - if (manifest.inheritsFrom() != null) { - throw new IllegalArgumentException("Resolved manifest cannot inherit from another manifest"); + if (!launchManifest.id().equals(standaloneManifest.id())) { + throw new IllegalArgumentException("Resolved manifest views must have the same id"); } - if (manifest.patches() != null && !manifest.patches().isEmpty()) { - throw new IllegalArgumentException("Resolved manifest cannot contain pending patches"); - } - } - } - - /// A manifest view whose inheritance has been folded while preserving pending patches. - /// - /// @param manifest the standalone manifest data that can be saved back to disk - @NotNullByDefault - public record Standalone(GameInstanceManifest manifest) { - - /// Creates a standalone manifest view. - public Standalone { - Objects.requireNonNull(manifest); - if (manifest.inheritsFrom() != null) { + if (launchManifest.inheritsFrom() != null) { + throw new IllegalArgumentException("Launch manifest cannot inherit from another manifest"); + } + if (launchManifest.patches() != null && !launchManifest.patches().isEmpty()) { + throw new IllegalArgumentException("Launch manifest cannot contain pending patches"); + } + if (standaloneManifest.inheritsFrom() != null) { throw new IllegalArgumentException("Standalone manifest cannot inherit from another manifest"); } } @@ -531,15 +522,7 @@ public boolean appliesToCurrentEnvironment() { /// @param repository the repository that provides parent manifests /// @return the resolved manifest public GameInstanceManifest resolve(GameRepository repository) throws NoSuchGameInstanceException { - return repository.resolve(this).manifest(); - } - - /// Resolves this manifest through the repository while preserving patches. - /// - /// @param repository the repository that provides parent manifests - /// @return the standalone manifest - public GameInstanceManifest resolvePreservingPatches(GameRepository repository) throws NoSuchGameInstanceException { - return repository.resolvePreservingPatches(this).manifest(); + return repository.resolve(this).launchManifest(); } /// Returns a manifest copy with the given id. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java index 2a1490e006..5949fac8d5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameRepository.java @@ -36,18 +36,12 @@ /// locating instance-owned files, and exposing helper paths used by launch, download, and maintenance code. @NotNullByDefault public interface GameRepository { - /// Resolves inheritance and pending patches into a launch-ready manifest view. + /// Resolves inheritance into launch and standalone manifest views. /// /// @param manifest the manifest to resolve /// @return the resolved manifest view GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException; - /// Resolves inheritance while leaving patches as manifest data. - /// - /// @param manifest the manifest to resolve - /// @return the standalone manifest view - GameInstanceManifest.Standalone resolvePreservingPatches(GameInstanceManifest manifest) throws NoSuchGameInstanceException; - /// Returns whether the instance exists in the current repository index. /// /// @param instanceId the instance id @@ -67,22 +61,6 @@ public interface GameRepository { /// @return the resolved manifest view GameInstanceManifest.Resolved getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException; - /// Returns a standalone manifest view for the instance. - /// - /// @param instanceId the instance id - /// @return the standalone manifest view - default GameInstanceManifest.Standalone getResolvedPreservingPatchesInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException { - return resolvePreservingPatches(getInstanceManifest(instanceId)); - } - - /// Returns a standalone manifest by string id. - /// - /// @param id the instance id string - /// @return the standalone manifest - default GameInstanceManifest getResolvedPreservingPatchesManifest(GameInstanceID id) throws NoSuchGameInstanceException { - return getResolvedPreservingPatchesInstanceManifest(id).manifest(); - } - /// Returns the number of loaded instances. /// /// @return the loaded instance count @@ -174,7 +152,7 @@ default Optional getGameVersion(GameInstanceID instanceId) throws NoSuch /// @return the primary client jar path /// @throws NoSuchGameInstanceException if the instance is not loaded in this repository default Path getInstanceJar(GameInstanceID instanceId) throws NoSuchGameInstanceException { - return getInstanceJar(getResolvedInstanceManifest(instanceId).manifest()); + return getInstanceJar(getResolvedInstanceManifest(instanceId).launchManifest()); } /// Renames an instance and updates repository-managed references. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java index 217ca8d4ba..464487958f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java @@ -91,7 +91,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(instanceId) .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); // Mcbbs manifest List addons = new ArrayList<>(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java index 1c477bd994..68223a1def 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java @@ -173,7 +173,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(instanceId) .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); Map dependencies = new HashMap<>(); dependencies.put("minecraft", gameVersion); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java index f3ba850aac..631fc9242d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java @@ -81,7 +81,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(instanceId) .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); List components = new ArrayList<>(); components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, MultiMCComponents.getComponent(MINECRAFT), gameVersion)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java index 7806431ffb..3949d27c99 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java @@ -87,7 +87,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(instanceId) .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesManifest(instanceId), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); List addons = new ArrayList<>(); addons.add(new ServerModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion)); analyzer.getVersion(FORGE).ifPresent(forgeVersion -> diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java index d724020ead..a0fe2be6e0 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java @@ -56,8 +56,9 @@ public void testRootManifestWithPatchesUsesPatchView() throws NoSuchGameInstance GameInstanceManifest.Resolved resolved = new DefaultGameRepository(Path.of(".")).resolve(manifest); - assertNull(resolved.manifest().mainClass()); - assertEquals(List.of(patch("patch", null)), resolved.appliedPatches()); + assertNull(resolved.launchManifest().mainClass()); + assertNull(resolved.launchManifest().patches()); + assertEquals(List.of(patch("patch", null)), resolved.standaloneManifest().getPatches()); } /// Creates a minimal manifest for tests. From aad077273d7190a5647552cbb5fa5ebc956a1d2c Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 15:42:44 +0800 Subject: [PATCH 46/85] refactor: simplify resolve methods in DefaultGameRepository to streamline manifest handling --- .../hmcl/game/DefaultGameRepository.java | 74 +++++++------------ 1 file changed, 27 insertions(+), 47 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 3beb92798b..484a00c934 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -209,7 +209,7 @@ protected void refreshImpl() { Map loadedInstances = new TreeMap<>(); for (InstanceHolder holder : newStatus.instances.values()) { try { - GameInstanceManifest resolved = newStatus.resolve(holder.manifest, new HashSet<>()); + GameInstanceManifest resolved = newStatus.resolve(holder.manifest, new HashSet<>()).launchManifest(); if (CompatibilityRule.appliesToCurrentEnvironment(resolved.compatibilityRules())) { loadedInstances.put(holder.id, holder); } @@ -623,13 +623,7 @@ public ResourcePackManager getResourcePackManager(GameInstanceID instanceId) { @Override public GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest) throws NoSuchGameInstanceException { - GameInstanceManifest launchManifest = status.resolve(manifest, new HashSet<>()).withPatches(null); - GameInstanceManifest standaloneManifest = status.resolveStandalone(manifest, new HashSet<>()); - GameInstanceID jar = launchManifest.jar(); - if (jar != null) { - standaloneManifest = standaloneManifest.withJar(jar); - } - return new GameInstanceManifest.Resolved(launchManifest, standaloneManifest); + return status.resolve(manifest, new HashSet<>()); } protected static class Status { @@ -640,23 +634,28 @@ protected Status(Path baseDirectory) { this.baseDirectory = baseDirectory; } - private GameInstanceManifest resolve(GameInstanceManifest manifest, - Set resolvedSoFar) throws NoSuchGameInstanceException { - GameInstanceManifest currentManifest; + private GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest, + Set resolvedSoFar) throws NoSuchGameInstanceException { + GameInstanceManifest launchManifest; + GameInstanceManifest standaloneManifest = manifest.root() + ? manifest + : addPatches( + addPatches(new GameInstanceManifest(manifest.id()), List.of(manifest.toPatch())), + manifest.patches()); if (manifest.inheritsFrom() == null) { if (manifest.root()) { // TODO: Breaking change, require much testing on versions installed with external installer, other launchers, and all kinds of versions. - currentManifest = manifest.patches() != null ? new GameInstanceManifest(manifest.id()).withPatches(manifest.patches()) : manifest; + launchManifest = manifest.patches() != null ? new GameInstanceManifest(manifest.id()).withPatches(manifest.patches()) : manifest; } else { - currentManifest = manifest; + launchManifest = manifest; } - currentManifest = currentManifest.withJar(manifest.jar() == null ? manifest.id() : manifest.jar()); + launchManifest = launchManifest.withJar(manifest.jar() == null ? manifest.id() : manifest.jar()); } else { // To maximize the compatibility. if (!resolvedSoFar.add(manifest.id())) { LOG.warning("Found circular dependency versions: " + resolvedSoFar); - currentManifest = manifest.jar() == null ? manifest.withJar(manifest.id()) : manifest; + launchManifest = manifest.jar() == null ? manifest.withJar(manifest.id()) : manifest; } else { InstanceHolder parentInstance = instances.get(manifest.inheritsFrom()); if (parentInstance == null) { @@ -664,51 +663,32 @@ private GameInstanceManifest resolve(GameInstanceManifest manifest, } // It is supposed to auto-install a version in getVersion. - currentManifest = manifest.merge(resolve(parentInstance.manifest, resolvedSoFar)); + GameInstanceManifest.Resolved parentResolved = resolve(parentInstance.manifest, resolvedSoFar); + launchManifest = manifest.merge(parentResolved.launchManifest()); + standaloneManifest = addPatches( + addPatches(parentResolved.standaloneManifest(), Collections.singleton(manifest.toPatch())), + manifest.patches()); } } - if (manifest.patches() == null) { - // This is a version from an external launcher. NO need to resolve the patches. - return currentManifest; - } else if (!manifest.patches().isEmpty()) { + if (manifest.patches() != null && !manifest.patches().isEmpty()) { // Assume patches themselves do not have patches recursively. List sortedPatches = manifest.patches().stream() .sorted(Comparator.comparing(GameInstancePatch::getPriority)) .toList(); for (GameInstancePatch patch : sortedPatches) { - currentManifest = patch.merge(currentManifest); + launchManifest = patch.merge(launchManifest); } } - return currentManifest.withId(manifest.id()); - } - - private GameInstanceManifest resolveStandalone(GameInstanceManifest manifest, - Set resolvedSoFar) throws NoSuchGameInstanceException { - GameInstanceManifest currentManifest = manifest.root() - ? manifest - : addPatches( - addPatches(new GameInstanceManifest(manifest.id()), Collections.singleton(manifest.toPatch())), - manifest.patches()); - - if (manifest.inheritsFrom() != null) { - // To maximize the compatibility. - if (!resolvedSoFar.add(manifest.id())) { - LOG.warning("Found circular dependency versions: " + resolvedSoFar); - } else { - InstanceHolder parentInstance = instances.get(manifest.inheritsFrom()); - if (parentInstance == null) { - throw new NoSuchGameInstanceException(manifest.inheritsFrom()); - } - - currentManifest = addPatches( - addPatches(resolveStandalone(parentInstance.manifest, resolvedSoFar), Collections.singleton(manifest.toPatch())), - manifest.patches()); - } + launchManifest = launchManifest.withId(manifest.id()).withPatches(null); + standaloneManifest = standaloneManifest.withId(manifest.id()); + GameInstanceID jar = launchManifest.jar(); + if (jar != null) { + standaloneManifest = standaloneManifest.withJar(jar); } - return currentManifest.withId(manifest.id()); + return new GameInstanceManifest.Resolved(launchManifest, standaloneManifest); } private static GameInstanceManifest addPatches(GameInstanceManifest manifest, @Nullable Collection additional) { From a9956c953eeec6f9ebda37a1edf02fb614d83aff Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 15:49:43 +0800 Subject: [PATCH 47/85] refactor: streamline jar handling in DefaultGameRepository's manifest processing --- .../java/org/jackhuang/hmcl/game/DefaultGameRepository.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 484a00c934..e4906e8e2a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -683,9 +683,8 @@ private GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest, launchManifest = launchManifest.withId(manifest.id()).withPatches(null); standaloneManifest = standaloneManifest.withId(manifest.id()); - GameInstanceID jar = launchManifest.jar(); - if (jar != null) { - standaloneManifest = standaloneManifest.withJar(jar); + if (launchManifest.jar() != null) { + standaloneManifest = standaloneManifest.withJar(launchManifest.jar()); } return new GameInstanceManifest.Resolved(launchManifest, standaloneManifest); From 83df3135fecbb54e611939f1e2b9416a7ceb494c Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 15:58:06 +0800 Subject: [PATCH 48/85] refactor: remove deprecated methods and update references to inheritsFrom in GameInstanceManifest --- .../hmcl/download/LibraryAnalyzer.java | 2 +- .../jackhuang/hmcl/download/MaintainTask.java | 4 +-- .../hmcl/game/GameInstanceManifest.java | 28 ------------------- 3 files changed, 3 insertions(+), 31 deletions(-) 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 47f42c0bfa..18d181720c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -155,7 +155,7 @@ public GameInstanceManifest build() { } public static LibraryAnalyzer analyze(GameInstanceManifest manifest, String gameVersion) { - if (manifest.getInheritsFrom() != null) + if (manifest.inheritsFrom() != null) throw new IllegalArgumentException("LibraryAnalyzer can only analyze independent game version"); Map> libraries = new HashMap<>(); 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 27628a4410..6e1b9a07df 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -46,7 +46,7 @@ public MaintainTask(GameRepository repository, GameInstanceManifest version) { this.repository = repository; this.version = version; - if (version.getInheritsFrom() != null) + if (version.inheritsFrom() != null) throw new IllegalArgumentException("MaintainTask requires independent game version"); } @@ -56,7 +56,7 @@ public void execute() { } public static GameInstanceManifest maintain(GameRepository repository, GameInstanceManifest version) { - if (version.getInheritsFrom() != null) + if (version.inheritsFrom() != null) throw new IllegalArgumentException("MaintainTask requires independent game version"); String mainClass = version.resolve(repository).getMainClass(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index dbe44c3983..bc2c5416c1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -383,27 +383,6 @@ public ReleaseType getType() { return jar == null ? null : jar.id(); } - /// Returns the parent instance id as a string. - /// - /// @return the parent instance id, or `null` when absent - public @Nullable String getInheritsFrom() { - return inheritsFrom == null ? null : inheritsFrom.id(); - } - - /// Returns the minimum launcher version. - /// - /// @return the minimum launcher version, or zero when absent - public int getMinimumLauncherVersion() { - return minimumLauncherVersion == null ? 0 : minimumLauncherVersion; - } - - /// Returns the compliance level. - /// - /// @return the compliance level, or `null` when absent - public @Nullable Integer getComplianceLevel() { - return complianceLevel; - } - /// Returns the preferred Java version. /// /// @return the preferred Java version, or `null` when absent @@ -453,13 +432,6 @@ public List getLibraries() { return libraries == null ? List.of() : libraries; } - /// Returns compatibility rules. - /// - /// @return compatibility rules - public List getCompatibilityRules() { - return compatibilityRules == null ? List.of() : compatibilityRules; - } - /// Returns download metadata. /// /// @return download metadata From 72c7764d11cbf00180ce87df2d68ed27259877bc Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 16:02:29 +0800 Subject: [PATCH 49/85] refactor: add getInheritanceChain method to retrieve inheritance hierarchy in GameInstanceManifest --- .../hmcl/game/DefaultGameRepository.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index e4906e8e2a..77606df1e8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -634,6 +634,28 @@ protected Status(Path baseDirectory) { this.baseDirectory = baseDirectory; } + private List getInheritanceChain(GameInstanceManifest manifest) throws NoSuchGameInstanceException { + if (manifest.inheritsFrom() == null) + return List.of(manifest); + + List chain = new ArrayList<>(2); + + for (GameInstanceManifest currentManifest = manifest; ; ) { + chain.add(currentManifest); + if (currentManifest.inheritsFrom() == null) { + break; + } else { + InstanceHolder parent = instances.get(currentManifest.inheritsFrom()); + if (parent == null) { + throw new NoSuchGameInstanceException(currentManifest.inheritsFrom()); + } + currentManifest = parent.manifest; + } + } + + return chain; + } + private GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest, Set resolvedSoFar) throws NoSuchGameInstanceException { GameInstanceManifest launchManifest; From a8eff69b7f9fc7062acac4fc0c2b2daecbbf46fd Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:17:57 +0800 Subject: [PATCH 50/85] refactor: remove deprecated setId, setJar, and setInheritsFrom methods in GameInstanceManifest --- .../jackhuang/hmcl/ui/versions/Instances.java | 4 ++- .../hmcl/game/GameInstanceManifest.java | 28 ++----------------- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java index c1eeb6b897..1426ff0480 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java @@ -183,8 +183,10 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { Controllers.prompt(i18n("version.manage.duplicate.prompt"), (result, handler) -> { handler.resolve(); + GameInstanceID instanceId = new GameInstanceID(result); + DefaultDependencyManager dependencyManager = repository.getDependency(); - GameInstanceManifest newVersion = version.setId(result).setJar(result); + GameInstanceManifest newVersion = version.withId(instanceId).withJar(instanceId); Controllers.taskDialog( Task.allOf(new GameDownloadTask(dependencyManager, null, newVersion), diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index bc2c5416c1..b9a10faf72 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -497,14 +497,6 @@ public GameInstanceManifest resolve(GameRepository repository) throws NoSuchGame return repository.resolve(this).launchManifest(); } - /// Returns a manifest copy with the given id. - /// - /// @param id the new instance id - /// @return the updated manifest - public GameInstanceManifest setId(String id) { - return withId(new GameInstanceID(id)); - } - public GameInstanceManifest withId(GameInstanceID id) { Objects.requireNonNull(id); @@ -517,14 +509,6 @@ public GameInstanceManifest withId(GameInstanceID id) { return builder.toManifest(); } - /// Returns a manifest copy with the given jar id. - /// - /// @param jar the jar id, or `null` when absent - /// @return the updated manifest - public GameInstanceManifest setJar(@Nullable String jar) { - return withJar(jar == null ? null : new GameInstanceID(jar)); - } - public GameInstanceManifest withJar(@Nullable GameInstanceID jar) { if (Objects.equals(this.jar, jar)) { return this; @@ -535,14 +519,6 @@ public GameInstanceManifest withJar(@Nullable GameInstanceID jar) { return builder.toManifest(); } - /// Returns a manifest copy with the given parent id. - /// - /// @param inheritsFrom the parent id, or `null` when absent - /// @return the updated manifest - public GameInstanceManifest setInheritsFrom(@Nullable String inheritsFrom) { - return withInheritsFrom(inheritsFrom == null ? null : new GameInstanceID(inheritsFrom)); - } - /// Returns a manifest copy with the given parent instance id. /// /// @param inheritsFrom the parent instance id, or `null` if this manifest is independent @@ -636,8 +612,8 @@ public GameInstanceManifest setPatches(@Nullable List patches } /// Returns a manifest copy with additional patches. - public GameInstanceManifest addPatch(GameInstancePatch... additional) { - return addPatches(Arrays.asList(additional)); + public GameInstanceManifest addPatch(GameInstancePatch additional) { + return addPatches(List.of(additional)); } /// Returns a manifest copy with additional patches. From e4bab378f20e819cb894f5b58d6f2fb134a0c55e Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:19:49 +0800 Subject: [PATCH 51/85] refactor: rename setMinecraftArguments to withMinecraftArguments for consistency --- .../java/org/jackhuang/hmcl/game/GameInstanceManifest.java | 6 +++++- .../java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index b9a10faf72..7ff97e954d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -534,7 +534,11 @@ public GameInstanceManifest withInheritsFrom(@Nullable GameInstanceID inheritsFr } /// Returns a manifest copy with the given legacy game arguments. - public GameInstanceManifest setMinecraftArguments(@Nullable String minecraftArguments) { + public GameInstanceManifest withMinecraftArguments(@Nullable String minecraftArguments) { + if (Objects.equals(this.minecraftArguments, minecraftArguments)) { + return this; + } + Builder builder = new Builder(this); builder.setMinecraftArguments(minecraftArguments); return builder.toManifest(); 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 87b3fef7d4..878440a800 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java @@ -58,7 +58,7 @@ public GameInstanceManifest build() { // Since $ will be escaped in linux, and our maintain of minecraftArgument will not cause escaping, // so we regenerate the minecraftArgument without escaping. - ret = ret.setMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString()); + ret = ret.withMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString()); } else { ret = ret.setArguments(ret.getArguments() .map(args -> args.withGame(game)) From 2012897cd3530a02015f46497a8c833c519e31d7 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:34:53 +0800 Subject: [PATCH 52/85] refactor: replace set methods with with methods for consistency in GameInstance classes --- .../hmcl/game/HMCLGameRepository.java | 2 +- .../hmcl/game/HMCLModpackProvider.java | 4 +- .../org/jackhuang/hmcl/ui/main/RootPage.java | 2 +- .../hmcl/download/LibraryAnalyzer.java | 2 +- .../cleanroom/CleanroomInstallTask.java | 4 +- .../hmcl/download/game/GameInstallTask.java | 2 +- .../liteloader/LiteLoaderInstallTask.java | 2 +- .../neoforge/NeoForgeInstallTask.java | 4 +- .../hmcl/game/GameInstanceManifest.java | 60 ++++++------------- .../hmcl/game/GameInstancePatch.java | 54 ++++++++++------- .../hmcl/launch/DefaultLauncher.java | 2 +- .../mcbbs/McbbsModpackLocalInstallTask.java | 4 +- 12 files changed, 63 insertions(+), 79 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 9a160b67c8..a0b1739850 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -205,7 +205,7 @@ private String getSelectedRunningDirectory( public Stream getDisplayInstanceManifests() { return getInstanceManifests().stream() .filter(v -> !v.isHidden()) - .sorted(Comparator.comparing((GameInstanceManifest v) -> Lang.requireNonNullElse(v.getReleaseTime(), Instant.EPOCH)) + .sorted(Comparator.comparing((GameInstanceManifest v) -> Lang.requireNonNullElse(v.releaseTime(), Instant.EPOCH)) .thenComparing(v -> VersionNumber.asVersion(v.id().id()))); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java index 8062bcbae7..b9db4d364c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackProvider.java @@ -64,13 +64,13 @@ public Modpack readManifest(ZipArchiveReader file, Path path, Charset encoding) Modpack manifest = JsonUtils.fromNonNullJson(manifestJson, HMCLModpack.class).setEncoding(encoding); String gameJson = CompressingUtils.readTextZipEntry(file, "minecraft/pack.json"); GameInstanceManifest game = JsonUtils.fromNonNullJson(gameJson, GameInstanceManifest.class); - if (game.getJar() == null) + if (game.jar() == null) if (StringUtils.isBlank(manifest.getVersion())) throw new JsonParseException("Cannot recognize the game version of modpack " + file + "."); else manifest.setManifest(HMCLModpackManifest.INSTANCE); else - manifest.setManifest(HMCLModpackManifest.INSTANCE).setGameVersion(game.getJar()); + manifest.setManifest(HMCLModpackManifest.INSTANCE).setGameVersion(game.jar().id()); return manifest; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index a16982890e..4fc8000056 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -128,7 +128,7 @@ public MainPage getMainPage() { List children = repository.getInstanceManifests().parallelStream() .filter(version -> !version.isHidden()) .sorted(Comparator - .comparing((GameInstanceManifest manifest) -> Lang.requireNonNullElse(manifest.getReleaseTime(), Instant.EPOCH)) + .comparing((GameInstanceManifest manifest) -> Lang.requireNonNullElse(manifest.releaseTime(), Instant.EPOCH)) .thenComparing(manifest -> VersionNumber.asVersion(repository.getGameVersion(manifest).orElse(manifest.id().toString())))) .collect(Collectors.toList()); runInFX(() -> { 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 18d181720c..d60feedf54 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -131,7 +131,7 @@ private GameInstancePatch removingMatchedLibrary(GameInstancePatch patch, String libraries.add(library); } } - return patch.setLibraries(libraries); + return patch.withLibraries(libraries); } /** diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java index d3093f9b5a..b14369e74b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java @@ -113,9 +113,9 @@ public Collection> getDependencies() { @Override public void execute() throws IOException, VersionMismatchException, UnsupportedInstallationException { if (selfVersion == null) { - task = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer).thenApplyAsync((version) -> version.setId(LibraryAnalyzer.LibraryType.CLEANROOM.getPatchId())); + task = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer).thenApplyAsync((version) -> version.withId(LibraryAnalyzer.LibraryType.CLEANROOM.getPatchId())); } else { - task = new ForgeNewInstallTask(dependencyManager, version, selfVersion, installer).thenApplyAsync((version) -> version.setId(LibraryAnalyzer.LibraryType.CLEANROOM.getPatchId())); + task = new ForgeNewInstallTask(dependencyManager, version, selfVersion, installer).thenApplyAsync((version) -> version.withId(LibraryAnalyzer.LibraryType.CLEANROOM.getPatchId())); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java index 9a8e0561ce..b864a2ae94 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java @@ -69,7 +69,7 @@ public void execute() throws Exception { JsonUtils.fromNonNullJson(downloadTask.getResult(), GameInstanceManifest.class), MINECRAFT.getPatchId(), remote.getGameVersion(), - GameInstancePatch.PRIORITY_MC).setJar(null); + GameInstancePatch.PRIORITY_MC).withJar(null); setResult(patch); GameInstanceManifest version = new GameInstanceManifest(this.version.id()).addPatch(patch); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java index 74938865cd..2dc44f65e5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java @@ -71,7 +71,7 @@ public void execute() { new Arguments().addGameArguments("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker"), LibraryAnalyzer.LAUNCH_WRAPPER_MAIN, Lang.merge(remote.getLibraries(), Collections.singleton(library))) - .setLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration + .withLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration ); dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java index 1d53f6402b..ee961fe7c8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java @@ -114,8 +114,8 @@ public static Task install(DefaultDependencyManager dependenc if (!neoForgeVersion.getId().equals(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) || neoForgeVersion.getVersion() == null) { throw new IOException("Invalid neoforge version."); } - return neoForgeVersion.setId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId()) - .setVersion( + return neoForgeVersion.withId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId()) + .withVersion( removePrefix(neoForgeVersion.getVersion().replace(LibraryAnalyzer.LibraryType.FORGE.getPatchId(), ""), "-") ); }); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 7ff97e954d..11e7bad5c9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -23,6 +23,7 @@ import com.google.gson.stream.JsonWriter; import org.jackhuang.hmcl.util.Constants; import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.gson.InstantTypeAdapter; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; @@ -30,6 +31,8 @@ import java.io.IOException; import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.format.DateTimeParseException; import java.util.*; @@ -51,8 +54,8 @@ public record GameInstanceManifest( @Nullable @Unmodifiable Map downloads, @Nullable @Unmodifiable Map logging, @Nullable ReleaseType type, - @Nullable String time, - @Nullable String releaseTime, + @Nullable Instant time, + @Nullable Instant releaseTime, @Nullable Integer minimumLauncherVersion, @Nullable Boolean root, @Nullable Boolean hidden, @@ -270,12 +273,18 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } case "time" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - builder.time = primitive.getAsString(); + try { + builder.time = InstantTypeAdapter.deserializeToInstant(primitive.getAsString()); + } catch (Exception ignored) { + } } } case "releaseTime" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - builder.releaseTime = primitive.getAsString(); + try { + builder.releaseTime = InstantTypeAdapter.deserializeToInstant(primitive.getAsString()); + } catch (Exception ignored) { + } } } case "minimumLauncherVersion" -> { @@ -341,20 +350,6 @@ public Optional getArguments() { return mainClass; } - /// Returns the manifest timestamp. - /// - /// @return the parsed timestamp, or `null` when absent or malformed - public @Nullable Instant getTime() { - return parseInstant(time); - } - - /// Returns the release timestamp. - /// - /// @return the parsed release timestamp, or `null` when absent or malformed - public @Nullable Instant getReleaseTime() { - return parseInstant(releaseTime); - } - /// Returns the patch version. /// /// @return always `null` for manifests @@ -362,27 +357,6 @@ public Optional getArguments() { return null; } - /// Returns the patch priority. - /// - /// @return the lowest priority for manifests - public int getPriority() { - return Integer.MIN_VALUE; - } - - /// Returns the release type. - /// - /// @return the release type, or unknown when absent - public ReleaseType getType() { - return type == null ? ReleaseType.UNKNOWN : type; - } - - /// Returns the primary jar instance id as a string. - /// - /// @return the primary jar instance id, or `null` when absent - public @Nullable String getJar() { - return jar == null ? null : jar.id(); - } - /// Returns the preferred Java version. /// /// @return the preferred Java version, or `null` when absent @@ -746,9 +720,9 @@ public JsonObject toJsonObject() { if (type != null) json.addProperty("type", type.name()); if (time != null) - json.addProperty("time", time); + json.addProperty("time", InstantTypeAdapter.serializeToString(time, ZoneOffset.UTC)); if (releaseTime != null) - json.addProperty("releaseTime", releaseTime); + json.addProperty("releaseTime", InstantTypeAdapter.serializeToString(releaseTime, ZoneOffset.UTC)); if (minimumLauncherVersion != null) json.addProperty("minimumLauncherVersion", minimumLauncherVersion); if (root != null) @@ -795,8 +769,8 @@ private static final class Builder { private @Nullable @Unmodifiable Map downloads; private @Nullable @Unmodifiable Map logging; private @Nullable ReleaseType type; - private @Nullable String time; - private @Nullable String releaseTime; + private @Nullable Instant time; + private @Nullable Instant releaseTime; private @Nullable Integer minimumLauncherVersion; private @Nullable Boolean root; private @Nullable Boolean hidden; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index ca74ac682b..1d6bb6874a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -23,11 +23,15 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.gson.InstantTypeAdapter; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; @@ -54,8 +58,8 @@ public record GameInstancePatch( @Nullable @Unmodifiable Map downloads, @Nullable @Unmodifiable Map logging, @Nullable ReleaseType type, - @Nullable String time, - @Nullable String releaseTime, + @Nullable Instant time, + @Nullable Instant releaseTime, @Nullable Integer minimumLauncherVersion, @Nullable Boolean hidden, @Nullable @Unmodifiable JsonObject rawJson @@ -229,78 +233,78 @@ public boolean isHidden() { } /// Returns a patch copy with the given id. - public GameInstancePatch setId(@Nullable String id) { + public GameInstancePatch withId(@Nullable String id) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given version. - public GameInstancePatch setVersion(@Nullable String version) { + public GameInstancePatch withVersion(@Nullable String version) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given priority. - public GameInstancePatch setPriority(@Nullable Integer priority) { + public GameInstancePatch withPriority(@Nullable Integer priority) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given jar id. - public GameInstancePatch setJar(@Nullable String jar) { + public GameInstancePatch withJar(@Nullable GameInstanceID jar) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, - jar == null ? null : new GameInstanceID(jar), assetIndex, assets, complianceLevel, javaVersion, + jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given libraries. - public GameInstancePatch setLibraries(@Nullable List libraries) { + public GameInstancePatch withLibraries(@Nullable List libraries) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given Java version. - public GameInstancePatch setJavaVersion(@Nullable GameJavaVersion javaVersion) { + public GameInstancePatch withJavaVersion(@Nullable GameJavaVersion javaVersion) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given arguments. - public GameInstancePatch setArguments(@Nullable Arguments arguments) { + public GameInstancePatch withArguments(@Nullable Arguments arguments) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given main class. - public GameInstancePatch setMainClass(@Nullable String mainClass) { + public GameInstancePatch withMainClass(@Nullable String mainClass) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given asset index. - public GameInstancePatch setAssetIndex(@Nullable AssetIndexInfo assetIndex) { + public GameInstancePatch withAssetIndex(@Nullable AssetIndexInfo assetIndex) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given downloads. - public GameInstancePatch setDownload(@Nullable Map downloads) { + public GameInstancePatch withDownload(@Nullable Map downloads) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); } /// Returns a patch copy with the given logging metadata. - public GameInstancePatch setLogging(@Nullable Map logging) { + public GameInstancePatch withLogging(@Nullable Map logging) { return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, hidden); @@ -324,8 +328,8 @@ private static GameInstancePatch copy( @Nullable Map downloads, @Nullable Map logging, @Nullable ReleaseType type, - @Nullable String time, - @Nullable String releaseTime, + @Nullable Instant time, + @Nullable Instant releaseTime, @Nullable Integer minimumLauncherVersion, @Nullable Boolean hidden) { return new GameInstancePatch(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, @@ -355,8 +359,8 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept @Nullable Map downloads = null; @Nullable Map logging = null; @Nullable ReleaseType type = null; - @Nullable String time = null; - @Nullable String releaseTime = null; + @Nullable Instant time = null; + @Nullable Instant releaseTime = null; @Nullable Integer minimumLauncherVersion = null; @Nullable Boolean hidden = null; @@ -472,12 +476,18 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept } case "time" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - time = primitive.getAsString(); + try { + time = InstantTypeAdapter.deserializeToInstant(primitive.getAsString()); + } catch (Exception ignored) { + } } } case "releaseTime" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - releaseTime = primitive.getAsString(); + try { + releaseTime = InstantTypeAdapter.deserializeToInstant(primitive.getAsString()); + } catch (Exception ignored) { + } } } case "minimumLauncherVersion" -> { @@ -570,9 +580,9 @@ public JsonObject toJsonObject() { if (type != null) json.addProperty("type", type.name()); if (time != null) - json.addProperty("time", time); + json.addProperty("time", InstantTypeAdapter.serializeToString(time, ZoneOffset.UTC)); if (releaseTime != null) - json.addProperty("releaseTime", releaseTime); + json.addProperty("releaseTime", InstantTypeAdapter.serializeToString(releaseTime, ZoneOffset.UTC)); if (minimumLauncherVersion != null) json.addProperty("minimumLauncherVersion", minimumLauncherVersion); if (hidden != null) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 56d80227bf..afcbf8aa2e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -530,7 +530,7 @@ protected Map getConfigurations() { pair("${auth_uuid}", UUIDs.toCompactString(authInfo.getUUID())), pair("${version_name}", Optional.ofNullable(options.getVersionName()).orElse(manifest.id().toString())), pair("${profile_name}", Optional.ofNullable(options.getProfileName()).orElse("Minecraft")), - pair("${version_type}", Optional.ofNullable(options.getVersionType()).orElse(manifest.getType().getId())), + pair("${version_type}", Optional.ofNullable(options.getVersionType()).orElse(manifest.type() != null ? manifest.type().getId() : ReleaseType.UNKNOWN.getId())), pair("${game_directory}", FileUtils.getAbsolutePath(repository.getRunDirectory(manifest.id()))), pair("${user_type}", authInfo.getUserType()), pair("${assets_index_name}", manifest.getAssetIndex().getId()), diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java index f59b85615d..67f54cd09b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java @@ -108,11 +108,11 @@ public void execute() throws Exception { GameInstanceManifest version = repository.getInstanceManifest(instanceId); Optional mcbbsPatch = version.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst(); if (!update) { - GameInstancePatch patch = new GameInstancePatch(PATCH_NAME).setLibraries(manifest.getLibraries()); + GameInstancePatch patch = new GameInstancePatch(PATCH_NAME).withLibraries(manifest.getLibraries()); dependencies.add(repository.saveAsync(version.addPatch(patch))); } else if (mcbbsPatch.isPresent()) { // This mcbbs modpack was installed by HMCL. - GameInstancePatch patch = mcbbsPatch.get().setLibraries(manifest.getLibraries()); + GameInstancePatch patch = mcbbsPatch.get().withLibraries(manifest.getLibraries()); dependencies.add(repository.saveAsync(version.addPatch(patch))); } else { // This mcbbs modpack was installed by other launchers. From ccd891256b4c35cec389d9fa9ddb247ed5ef287c Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:35:43 +0800 Subject: [PATCH 53/85] refactor: update GameInstanceManifest constructor to use GameInstanceID for consistency --- .../java/org/jackhuang/hmcl/ui/GameCrashWindowTest.java | 3 ++- .../java/org/jackhuang/hmcl/game/GameInstanceManifest.java | 6 ------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/HMCL/src/test/java/org/jackhuang/hmcl/ui/GameCrashWindowTest.java b/HMCL/src/test/java/org/jackhuang/hmcl/ui/GameCrashWindowTest.java index 75b0b4c6fc..0fb1e46b02 100644 --- a/HMCL/src/test/java/org/jackhuang/hmcl/ui/GameCrashWindowTest.java +++ b/HMCL/src/test/java/org/jackhuang/hmcl/ui/GameCrashWindowTest.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.ui; import org.jackhuang.hmcl.JavaFXLauncher; +import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.LaunchOptions; import org.jackhuang.hmcl.java.JavaInfo; @@ -52,7 +53,7 @@ public void test() throws Exception { Path workingPath = Path.of(System.getProperty("user.dir")); GameCrashWindow window = new GameCrashWindow(process, ProcessListener.ExitType.APPLICATION_ERROR, null, - new GameInstanceManifest("Classic"), + new GameInstanceManifest(new GameInstanceID("Classic")), new LaunchOptions.Builder() .setJava(new JavaRuntime(workingPath, new JavaInfo(Platform.SYSTEM_PLATFORM, "16", null), false, false)) .setGameDir(workingPath) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 11e7bad5c9..039639c906 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -31,7 +31,6 @@ import java.io.IOException; import java.time.Instant; -import java.time.ZoneId; import java.time.ZoneOffset; import java.time.format.DateTimeParseException; import java.util.*; @@ -144,11 +143,6 @@ public GameInstanceManifest(GameInstanceID id) { null); } - /// Creates an empty root manifest with the given id. - public GameInstanceManifest(String id) { - this(new GameInstanceID(id)); - } - public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) throws JsonParseException { if (copyJson) { json = json.deepCopy(); From 22bd1cbf641f2e9cdb7d40e170e127b55845a708 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:43:17 +0800 Subject: [PATCH 54/85] refactor(manifest): Make game instance manifests immutable - replace mutating `set*` calls with `with*` updates across manifest consumers - align library, patch, argument, and download updates with record-style copy semantics - preserve existing behavior while removing in-place mutation paths - improve consistency for manifest transformations and reduce accidental state changes --- .../jackhuang/hmcl/util/NativePatcher.java | 6 +- .../hmcl/download/LibraryAnalyzer.java | 12 ++-- .../jackhuang/hmcl/download/MaintainTask.java | 10 ++-- .../hmcl/game/GameInstanceManifest.java | 58 ++++++++++--------- .../hmcl/game/VersionLibraryBuilder.java | 6 +- .../modpack/multimc/MultiMCInstancePatch.java | 12 ++-- .../multimc/MultiMCModpackInstallTask.java | 2 +- 7 files changed, 56 insertions(+), 50 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java index 29dd6f0378..723622c28e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java @@ -93,7 +93,7 @@ public static GameInstanceManifest patchNative(DefaultGameRepository repository, newLibraries.add(library); } } - return version.setLibraries(newLibraries); + return version.withLibraries(newLibraries); } final boolean useNativeGLFW = settings.get(GameSettings::useNativeGLFWProperty); @@ -102,7 +102,7 @@ public static GameInstanceManifest patchNative(DefaultGameRepository repository, if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && (useNativeGLFW || useNativeOpenAL) && gameVersion != null && GameVersionNumber.compare(gameVersion, "1.19") >= 0) { - version = version.setLibraries(version.getLibraries().stream() + version = version.withLibraries(version.getLibraries().stream() .filter(library -> { if (library.getClassifier() != null && library.getClassifier().startsWith("natives") && "org.lwjgl".equals(library.getGroupId())) { @@ -186,7 +186,7 @@ public static GameInstanceManifest patchNative(DefaultGameRepository repository, } } - return version.setLibraries(newLibraries); + return version.withLibraries(newLibraries); } /// @see Java Mesa Loader for Windows 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 d60feedf54..e5df0742e1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -115,7 +115,7 @@ private GameInstanceManifest removingMatchedLibrary(GameInstanceManifest version libraries.add(library); } } - return version.setLibraries(libraries); + return version.withLibraries(libraries); } private GameInstancePatch removingMatchedLibrary(GameInstancePatch patch, String libraryId) { @@ -142,11 +142,11 @@ private GameInstancePatch removingMatchedLibrary(GameInstancePatch patch, String */ public LibraryAnalyzer removeLibrary(String libraryId) { if (!has(libraryId)) return this; - version = removingMatchedLibrary(version, libraryId) - .setPatches(version.getPatches().stream() - .filter(patch -> !libraryId.equals(patch.getId())) - .map(patch -> removingMatchedLibrary(patch, libraryId)) - .collect(Collectors.toList())); + GameInstanceManifest manifest = removingMatchedLibrary(version, libraryId); + version = manifest.withPatches(version.getPatches().stream() + .filter(patch -> !libraryId.equals(patch.getId())) + .map(patch -> removingMatchedLibrary(patch, libraryId)) + .collect(Collectors.toList())); return this; } 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 6e1b9a07df..7ec5dbd578 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -84,7 +84,7 @@ public static GameInstanceManifest maintain(GameRepository repository, GameInsta && ("log4j-patch".equals(library.getArtifactId()) || "log4j-patch-beta9".equals(library.getArtifactId())) && "1.0".equals(library.getVersion()) && library.getDownload() == null) { - version = version.setLibraries(libraries.subList(1, libraries.size())); + version = version.withLibraries(libraries.subList(1, libraries.size())); } } @@ -95,7 +95,7 @@ public static GameInstanceManifest maintainPreservingPatches(GameRepository repo if (!version.isResolvedPreservingPatches()) throw new IllegalArgumentException("MaintainTask requires independent game version"); GameInstanceManifest newVersion = maintain(repository, version.resolve(repository)); - return newVersion.setPatches(version.getPatches()); + return newVersion.withPatches(version.getPatches()); } private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository repository, GameInstanceManifest version, boolean reorderTweakClass) { @@ -144,7 +144,7 @@ private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository } GameInstanceManifest ret = builder.build(); - return mainClass == null ? ret : ret.setMainClass(mainClass); + return mainClass == null ? ret : ret.withMainClass(mainClass); } private static GameInstanceManifest maintainGameWithCpwModLauncher(GameRepository repository, GameInstanceManifest version) { @@ -281,7 +281,7 @@ private static GameInstanceManifest maintainOptiFineLibrary(GameRepository repos } } - return version.setLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList())); + return version.withLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList())); } public static GameInstanceManifest unique(GameInstanceManifest version) { @@ -336,6 +336,6 @@ public static GameInstanceManifest unique(GameInstanceManifest version) { } } - return version.setLibraries(libraries); + return version.withLibraries(libraries); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 039639c906..a7e306c133 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -32,7 +32,6 @@ import java.io.IOException; import java.time.Instant; import java.time.ZoneOffset; -import java.time.format.DateTimeParseException; import java.util.*; @NotNullByDefault @@ -513,56 +512,80 @@ public GameInstanceManifest withMinecraftArguments(@Nullable String minecraftArg } /// Returns a manifest copy with the given structured arguments. - public GameInstanceManifest setArguments(@Nullable Arguments arguments) { + public GameInstanceManifest withArguments(@Nullable Arguments arguments) { + if (this.arguments == arguments) + return this; + Builder builder = new Builder(this); builder.setArguments(arguments); return builder.toManifest(); } /// Returns a manifest copy with the given main class. - public GameInstanceManifest setMainClass(@Nullable String mainClass) { + public GameInstanceManifest withMainClass(@Nullable String mainClass) { + if (Objects.equals(this.mainClass, mainClass)) + return this; + Builder builder = new Builder(this); builder.setMainClass(mainClass); return builder.toManifest(); } /// Returns a manifest copy with the given asset index. - public GameInstanceManifest setAssetIndex(@Nullable AssetIndexInfo assetIndex) { + public GameInstanceManifest withAssetIndex(@Nullable AssetIndexInfo assetIndex) { + if (this.assetIndex == assetIndex) + return this; + Builder builder = new Builder(this); builder.setAssetIndex(assetIndex); return builder.toManifest(); } /// Returns a manifest copy with the given Java version. - public GameInstanceManifest setJavaVersion(@Nullable GameJavaVersion javaVersion) { + public GameInstanceManifest withJavaVersion(@Nullable GameJavaVersion javaVersion) { + if (this.javaVersion == javaVersion) + return this; + Builder builder = new Builder(this); builder.setJavaVersion(javaVersion); return builder.toManifest(); } /// Returns a manifest copy with the given libraries. - public GameInstanceManifest setLibraries(@Nullable List libraries) { + public GameInstanceManifest withLibraries(@Nullable List libraries) { + if (this.libraries == libraries) + return this; + Builder builder = new Builder(this); builder.setLibraries(libraries); return builder.toManifest(); } /// Returns a manifest copy with the given downloads. - public GameInstanceManifest setDownload(@Nullable Map downloads) { + public GameInstanceManifest withDownloads(@Nullable Map downloads) { + if (this.downloads == downloads) + return this; + Builder builder = new Builder(this); builder.setDownloads(downloads); return builder.toManifest(); } /// Returns a manifest copy with the given logging metadata. - public GameInstanceManifest setLogging(@Nullable Map logging) { + public GameInstanceManifest withLogging(@Nullable Map logging) { + if (this.logging == logging) + return this; + Builder builder = new Builder(this); builder.setLogging(logging); return builder.toManifest(); } /// Returns a manifest copy with the given root flag. - public GameInstanceManifest setRoot(@Nullable Boolean root) { + public GameInstanceManifest withRoot(@Nullable Boolean root) { + if (Objects.equals(this.root, root)) + return this; + Builder builder = new Builder(this); builder.setRoot(root); return builder.toManifest(); @@ -578,11 +601,6 @@ public GameInstanceManifest withPatches(@Nullable List patche return builder.toManifest(); } - /// Returns a manifest copy with the given patches. - public GameInstanceManifest setPatches(@Nullable List patches) { - return withPatches(patches); - } - /// Returns a manifest copy with additional patches. public GameInstanceManifest addPatch(GameInstancePatch additional) { return addPatches(List.of(additional)); @@ -734,18 +752,6 @@ public JsonObject toJsonObject() { return json; } - private static @Nullable Instant parseInstant(@Nullable String value) { - if (value == null) { - return null; - } - - try { - return Instant.parse(value); - } catch (DateTimeParseException e) { - return null; - } - } - private static final class Builder { // @formatter:off private @Nullable GameInstanceID id; 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 878440a800..52b35e6e02 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java @@ -54,17 +54,17 @@ public GameInstanceManifest build() { // The official launcher will not parse the "arguments" property when it detects the presence of "mcArgs". // The "arguments" property with the "rule" is simply ignored here. this.mcArgs.addAll(this.game.stream().map(arg -> arg.toString(new HashMap<>(), new HashMap<>())).flatMap(Collection::stream).collect(Collectors.toList())); - ret = ret.setArguments(null); + ret = ret.withArguments(null); // Since $ will be escaped in linux, and our maintain of minecraftArgument will not cause escaping, // so we regenerate the minecraftArgument without escaping. ret = ret.withMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString()); } else { - ret = ret.setArguments(ret.getArguments() + ret = ret.withArguments(ret.getArguments() .map(args -> args.withGame(game)) .map(args -> jvmChanged ? args.withJvm(jvm) : args).orElse(new Arguments(game, jvmChanged ? jvm : null))); } - return ret.setLibraries(libraries); + return ret.withLibraries(libraries); } public boolean hasTweakClass(String tweakClass) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java index 90039b5f93..b8cf671f58 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java @@ -386,11 +386,11 @@ public static ResolvedInstance resolveArtifact(List patche } GameInstanceManifest version = new GameInstanceManifest(instanceId) - .setArguments(new Arguments().addGameArguments(minecraftArguments).addJVMArgumentsDirect(jvmArguments)) - .setMainClass(mainClass) - .setLibraries(libraries) - .setAssetIndex(assetIndex) - .setDownload(Collections.singletonMap(DownloadType.CLIENT, mainJar.getRawDownloadInfo())); + .withArguments(new Arguments().addGameArguments(minecraftArguments).addJVMArgumentsDirect(jvmArguments)) + .withMainClass(mainClass) + .withLibraries(libraries) + .withAssetIndex(assetIndex) + .withDownloads(Collections.singletonMap(DownloadType.CLIENT, mainJar.getRawDownloadInfo())); /* TODO: Official Version-Json can only store one pre-defined GameJavaVersion, including 8, 11, 16, 17 and 21. An array of all suitable java versions are NOT supported. @@ -403,7 +403,7 @@ public static ResolvedInstance resolveArtifact(List patche for (int i = javaMajors.length - 1; i >= 0; i--) { GameJavaVersion jv = GameJavaVersion.get(javaMajors[i]); if (jv != null) { - version = version.setJavaVersion(jv); + version = version.withJavaVersion(jv); break javaMajors; } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java index 4d68fbebd9..cdc404e6d5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java @@ -289,7 +289,7 @@ public void execute() throws Exception { dependencies.add(new GameLibrariesTask( dependencyManager, // TODO: check integrity of maven-only files when launching games? - version.setLibraries(Lang.merge(version.getLibraries(), artifact.getMavenOnlyFiles())), + version.withLibraries(Lang.merge(version.getLibraries(), artifact.getMavenOnlyFiles())), true )); From 6688aad79549510cf2530eb44bf8598d719131cf Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:46:24 +0800 Subject: [PATCH 55/85] refactor(manifest): change logging map to unmodifiable for immutability --- .../main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index a7e306c133..9651a9d13a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -253,7 +253,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } catch (IllegalArgumentException ignored) { } } - builder.logging = Map.copyOf(map); + builder.logging = Collections.unmodifiableMap(map); } } case "type" -> { From 4d5432ecd3bcb54ba4bbbb774ccc2be6b8ccf5ac Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:50:59 +0800 Subject: [PATCH 56/85] refactor: replace getMainClass and getJavaVersion methods with direct accessors for consistency --- .../jackhuang/hmcl/game/LauncherHelper.java | 4 ++-- .../hmcl/download/LibraryAnalyzer.java | 4 ++-- .../jackhuang/hmcl/download/MaintainTask.java | 2 +- .../download/fabric/FabricInstallTask.java | 2 +- .../hmcl/download/forge/ForgeInstallTask.java | 2 +- .../optifine/OptiFineInstallTask.java | 2 +- .../hmcl/download/quilt/QuiltInstallTask.java | 2 +- .../hmcl/game/GameInstanceManifest.java | 21 ------------------- .../hmcl/game/JavaVersionConstraint.java | 14 ++++++------- .../hmcl/launch/DefaultLauncher.java | 4 ++-- 10 files changed, 18 insertions(+), 39 deletions(-) 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 18d52d36a4..3df542a9d0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -455,7 +455,7 @@ private static Task checkGameState(HMCLGameRepository repository, G } if (targetJavaVersion == null) - targetJavaVersion = version.getJavaVersion(); + targetJavaVersion = version.javaVersion(); } if (targetJavaVersion != null && supportedVersions.contains(targetJavaVersion)) { @@ -529,7 +529,7 @@ private static Task checkGameState(HMCLGameRepository repository, G ? GameJavaVersion.getCleanroomJavaVersion(cleanroomVersion) : GameJavaVersion.JAVA_21; } else if (violatedMandatoryConstraints.contains(JavaVersionConstraint.GAME_JSON)) - gameJavaVersion = version.getJavaVersion(); + gameJavaVersion = version.javaVersion(); else if (violatedMandatoryConstraints.contains(JavaVersionConstraint.VANILLA)) gameJavaVersion = GameJavaVersion.getMinimumJavaVersion(gameVersion); else 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 e5df0742e1..ef07f00fa2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -97,7 +97,7 @@ public boolean hasModLoader() { } public boolean hasModLauncher() { - return LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(version.getMainClass()) || version.getPatches().stream().anyMatch( + return LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(version.mainClass()) || version.getPatches().stream().anyMatch( patch -> LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(patch.getMainClass()) ); } @@ -189,7 +189,7 @@ public static boolean isModded(GameRepository provider, GameInstanceManifest ver } catch (NoSuchGameInstanceException e) { return false; } - String mainClass = resolvedVersion.getMainClass(); + String mainClass = resolvedVersion.mainClass(); return mainClass != null && (LAUNCH_WRAPPER_MAIN.equals(mainClass) || mainClass.startsWith("net.minecraftforge") || mainClass.startsWith("net.neoforged") 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 7ec5dbd578..3652c6355d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -59,7 +59,7 @@ public static GameInstanceManifest maintain(GameRepository repository, GameInsta if (version.inheritsFrom() != null) throw new IllegalArgumentException("MaintainTask requires independent game version"); - String mainClass = version.resolve(repository).getMainClass(); + String mainClass = version.resolve(repository).mainClass(); if (mainClass != null && mainClass.equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN)) { version = maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(repository, unique(version), true), false); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java index 99e79f0461..681f1895dc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java @@ -66,7 +66,7 @@ public boolean doPreExecute() { @Override public void preExecute() throws Exception { - if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).getMainClass())) + if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).mainClass())) throw new UnsupportedInstallationException(FABRIC_NOT_COMPATIBLE_WITH_FORGE); } 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 a11ddffce8..ee885cdab1 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 @@ -99,7 +99,7 @@ public Collection> getDependencies() { @Override public void execute() throws IOException, VersionMismatchException, UnsupportedInstallationException { - String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass(); + String originalMainClass = version.resolve(dependencyManager.getGameRepository()).mainClass(); if (GameVersionNumber.compare("1.13", remote.getGameVersion()) <= 0) { // Forge 1.13 is not compatible with fabric. if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass)) 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 8cf1eb7f40..4ccccad024 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 @@ -123,7 +123,7 @@ public boolean isRelyingOnDependencies() { @Override public void execute() throws Exception { - String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass(); + String originalMainClass = version.resolve(dependencyManager.getGameRepository()).mainClass(); if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass)) throw new UnsupportedInstallationException(UnsupportedInstallationException.UNSUPPORTED_LAUNCH_WRAPPER); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java index 03ec4202ee..107b32e5e2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java @@ -65,7 +65,7 @@ public boolean doPreExecute() { @Override public void preExecute() throws Exception { - if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).getMainClass())) + if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).mainClass())) throw new UnsupportedInstallationException(FABRIC_NOT_COMPATIBLE_WITH_FORGE); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 9651a9d13a..8db3a9cb6c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -336,27 +336,6 @@ public Optional getArguments() { return Optional.ofNullable(arguments); } - /// Returns the main class. - /// - /// @return the main class, or `null` when absent - public @Nullable String getMainClass() { - return mainClass; - } - - /// Returns the patch version. - /// - /// @return always `null` for manifests - public @Nullable String getVersion() { - return null; - } - - /// Returns the preferred Java version. - /// - /// @return the preferred Java version, or `null` when absent - public @Nullable GameJavaVersion getJavaVersion() { - return javaVersion; - } - /// Returns whether the manifest is hidden. /// /// @return whether the manifest is hidden diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java index 94f06a962a..cc8be3f9b8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java @@ -37,7 +37,7 @@ public enum JavaVersionConstraint { @Override protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { // Give priority to the Java version requirements specified in the version JSON - return version == null || version.getJavaVersion() == null; + return version == null || version.javaVersion() == null; } @Override @@ -53,16 +53,16 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { if (version == null) return false; // We only checks for 1.7.10 and above, since 1.7.2 with Forge can only run on Java 7, but it is recorded Java 8 in game json, which is not correct. - return gameVersionNumber.compareTo("1.7.10") >= 0 && version.getJavaVersion() != null; + return gameVersionNumber.compareTo("1.7.10") >= 0 && version.javaVersion() != null; } @Override public VersionRange getJavaVersionRange(GameInstanceManifest version, LibraryAnalyzer analyzer) { String javaVersion; - if (Objects.requireNonNull(version.getJavaVersion()).majorVersion() >= 9) { - javaVersion = "" + version.getJavaVersion().majorVersion(); + if (Objects.requireNonNull(version.javaVersion()).majorVersion() >= 9) { + javaVersion = "" + version.javaVersion().majorVersion(); } else { - javaVersion = "1." + version.getJavaVersion().majorVersion(); + javaVersion = "1." + version.javaVersion().majorVersion(); } return VersionNumber.atLeast(javaVersion); } @@ -135,7 +135,7 @@ public VersionRange getJavaVersionRange(GameInstanceManifest vers protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { if (version == null) return false; - return super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer) && LAUNCH_WRAPPER_MAIN.equals(version.getMainClass()) && + return super.appliesToVersionImpl(gameVersionNumber, version, java, analyzer) && LAUNCH_WRAPPER_MAIN.equals(version.mainClass()) && version.getLibraries().stream() .filter(library -> "launchwrapper".equals(library.getArtifactId())) .anyMatch(library -> VersionNumber.asVersion(library.getVersion()).compareTo(VersionNumber.asVersion("1.13")) < 0); @@ -256,7 +256,7 @@ public final boolean appliesToVersion(@Nullable GameVersionNumber gameVersionNum protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nullable GameInstanceManifest version, @Nullable JavaRuntime java, @Nullable LibraryAnalyzer analyzer) { GameJavaVersion gameJavaVersion; - if (version == null || (gameJavaVersion = version.getJavaVersion()) == null) { + if (version == null || (gameJavaVersion = version.javaVersion()) == null) { return true; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index afcbf8aa2e..a7a69035bc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -351,11 +351,11 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { res.add("-javaagent:" + javaAgent); } - if (manifest.getMainClass() == null) { + if (manifest.mainClass() == null) { throw new IllegalStateException("Main class is null for instance " + manifest.id()); } - res.add(manifest.getMainClass()); + res.add(manifest.mainClass()); res.addAll(Arguments.parseStringArguments(manifest.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(ArrayList::new), configuration)); From 1de7128def894288b5b8bd96cc5d31dd4669b04e Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:52:52 +0800 Subject: [PATCH 57/85] refactor: rename VersionLibraryBuilder to GameInstanceLibraryBuilder for clarity --- .../jackhuang/hmcl/download/LibraryAnalyzer.java | 7 +++---- .../org/jackhuang/hmcl/download/MaintainTask.java | 6 +++--- ...uilder.java => GameInstanceLibraryBuilder.java} | 14 +++++++------- 3 files changed, 13 insertions(+), 14 deletions(-) rename HMCLCore/src/main/java/org/jackhuang/hmcl/game/{VersionLibraryBuilder.java => GameInstanceLibraryBuilder.java} (93%) 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 ef07f00fa2..65817a0819 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -288,12 +288,11 @@ protected String patchVersion(GameInstanceManifest gameVersion, String libraryVe return super.patchVersion(gameVersion, libraryVersion); } - private String scanVersion(GameInstanceManifest version) { - Optional optArgument = version.getArguments(); - if (!optArgument.isPresent()) { + private String scanVersion(GameInstanceManifest manifest) { + if (manifest.arguments() == null) { return null; } - List gameArguments = optArgument.get().game(); + List gameArguments = manifest.arguments().game(); if (gameArguments == null) { return null; } 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 3652c6355d..159665a2f2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -100,7 +100,7 @@ public static GameInstanceManifest maintainPreservingPatches(GameRepository repo private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository repository, GameInstanceManifest version, boolean reorderTweakClass) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); - VersionLibraryBuilder builder = new VersionLibraryBuilder(version); + GameInstanceLibraryBuilder builder = new GameInstanceLibraryBuilder(version); String mainClass = null; // Installing Forge will override the Minecraft arguments in json, so LiteLoader and OptiFine Tweaker are being re-added. @@ -149,7 +149,7 @@ private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository private static GameInstanceManifest maintainGameWithCpwModLauncher(GameRepository repository, GameInstanceManifest version) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); - VersionLibraryBuilder builder = new VersionLibraryBuilder(version); + GameInstanceLibraryBuilder builder = new GameInstanceLibraryBuilder(version); if (!libraryAnalyzer.has(FORGE)) return version; @@ -207,7 +207,7 @@ private static String updateIgnoreList(GameRepository repository, GameInstanceMa // Fix wrong configurations when launching 1.17+ with Forge. private static GameInstanceManifest maintainGameWithCpwBoostrapLauncher(GameRepository repository, GameInstanceManifest version) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); - VersionLibraryBuilder builder = new VersionLibraryBuilder(version); + GameInstanceLibraryBuilder builder = new GameInstanceLibraryBuilder(version); if (!libraryAnalyzer.has(FORGE) && !libraryAnalyzer.has(NEO_FORGE)) return version; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java similarity index 93% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java index 52b35e6e02..51a577990b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java @@ -30,7 +30,7 @@ * * @author huangyuhui */ -public final class VersionLibraryBuilder { +public final class GameInstanceLibraryBuilder { private final GameInstanceManifest version; private final List mcArgs; private final List game; @@ -39,12 +39,12 @@ public final class VersionLibraryBuilder { private final boolean useMcArgs; private boolean jvmChanged = false; - public VersionLibraryBuilder(GameInstanceManifest version) { - this.version = version; - this.libraries = new ArrayList<>(version.getLibraries()); - this.mcArgs = version.getMinecraftArguments().map(StringUtils::tokenize).map(ArrayList::new).orElse(null); - this.game = version.getArguments().map(Arguments::game).map(ArrayList::new).orElseGet(ArrayList::new); - this.jvm = new ArrayList<>(version.getArguments().map(Arguments::jvm).orElse(Arguments.DEFAULT_JVM_ARGUMENTS)); + public GameInstanceLibraryBuilder(GameInstanceManifest manifest) { + this.version = manifest; + this.libraries = new ArrayList<>(manifest.getLibraries()); + this.mcArgs = manifest.getMinecraftArguments().map(StringUtils::tokenize).map(ArrayList::new).orElse(null); + this.game = manifest.getArguments().map(Arguments::game).map(ArrayList::new).orElseGet(ArrayList::new); + this.jvm = new ArrayList<>(manifest.getArguments().map(Arguments::jvm).orElse(Arguments.DEFAULT_JVM_ARGUMENTS)); this.useMcArgs = mcArgs != null; } From 211819595a2733013ba340719f6eb2772be58ae0 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:54:51 +0800 Subject: [PATCH 58/85] refactor: replace legacy method calls with Optional for improved null safety --- .../hmcl/game/DefaultGameRepository.java | 4 ++-- .../hmcl/game/GameInstanceLibraryBuilder.java | 13 +++++------- .../hmcl/game/GameInstanceManifest.java | 21 +------------------ .../hmcl/launch/DefaultLauncher.java | 8 +++---- 4 files changed, 12 insertions(+), 34 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 77606df1e8..10f351b78f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -659,14 +659,14 @@ private List getInheritanceChain(GameInstanceManifest mani private GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest, Set resolvedSoFar) throws NoSuchGameInstanceException { GameInstanceManifest launchManifest; - GameInstanceManifest standaloneManifest = manifest.root() + GameInstanceManifest standaloneManifest = manifest.isRoot() ? manifest : addPatches( addPatches(new GameInstanceManifest(manifest.id()), List.of(manifest.toPatch())), manifest.patches()); if (manifest.inheritsFrom() == null) { - if (manifest.root()) { + if (manifest.isRoot()) { // TODO: Breaking change, require much testing on versions installed with external installer, other launchers, and all kinds of versions. launchManifest = manifest.patches() != null ? new GameInstanceManifest(manifest.id()).withPatches(manifest.patches()) : manifest; } else { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java index 51a577990b..ea6973e90a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java @@ -20,10 +20,7 @@ import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.platform.CommandBuilder; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; /** @@ -42,9 +39,9 @@ public final class GameInstanceLibraryBuilder { public GameInstanceLibraryBuilder(GameInstanceManifest manifest) { this.version = manifest; this.libraries = new ArrayList<>(manifest.getLibraries()); - this.mcArgs = manifest.getMinecraftArguments().map(StringUtils::tokenize).map(ArrayList::new).orElse(null); - this.game = manifest.getArguments().map(Arguments::game).map(ArrayList::new).orElseGet(ArrayList::new); - this.jvm = new ArrayList<>(manifest.getArguments().map(Arguments::jvm).orElse(Arguments.DEFAULT_JVM_ARGUMENTS)); + this.mcArgs = Optional.ofNullable(manifest.minecraftArguments()).map(StringUtils::tokenize).map(ArrayList::new).orElse(null); + this.game = Optional.ofNullable(manifest.arguments()).map(Arguments::game).map(ArrayList::new).orElseGet(ArrayList::new); + this.jvm = new ArrayList<>(Optional.ofNullable(manifest.arguments()).map(Arguments::jvm).orElse(Arguments.DEFAULT_JVM_ARGUMENTS)); this.useMcArgs = mcArgs != null; } @@ -60,7 +57,7 @@ public GameInstanceManifest build() { // so we regenerate the minecraftArgument without escaping. ret = ret.withMinecraftArguments(new CommandBuilder().addAllWithoutParsing(mcArgs).toString()); } else { - ret = ret.withArguments(ret.getArguments() + ret = ret.withArguments(Optional.ofNullable(ret.arguments()) .map(args -> args.withGame(game)) .map(args -> jvmChanged ? args.withJvm(jvm) : args).orElse(new Arguments(game, jvmChanged ? jvm : null))); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 8db3a9cb6c..55529bd0ea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -317,25 +317,6 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t return builder.toManifest(); } - @Override - public Boolean root() { - return root != null && root; - } - - /// Returns legacy game arguments. - /// - /// @return the legacy game arguments - public Optional getMinecraftArguments() { - return Optional.ofNullable(minecraftArguments); - } - - /// Returns structured game arguments. - /// - /// @return the structured game arguments - public Optional getArguments() { - return Optional.ofNullable(arguments); - } - /// Returns whether the manifest is hidden. /// /// @return whether the manifest is hidden @@ -347,7 +328,7 @@ public boolean isHidden() { /// /// @return whether the manifest is a root manifest public boolean isRoot() { - return root(); + return root != null && root; } /// Returns whether this manifest is already a standalone view. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index a7a69035bc..6f43783573 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -314,7 +314,7 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { configuration.put("${natives_directory}", nativeFolderPath); Path javaNativeFolder = FileUtils.toAbsolute(nativeFolder); - @Nullable List jvmArguments = manifest.getArguments().map(Arguments::jvm).orElse(null); + @Nullable List jvmArguments = Optional.ofNullable(manifest.arguments()).map(Arguments::jvm).orElse(null); if (jvmArguments != null) { for (Argument jvmArgument : jvmArguments) { @@ -357,11 +357,11 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { res.add(manifest.mainClass()); - res.addAll(Arguments.parseStringArguments(manifest.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(ArrayList::new), configuration)); + res.addAll(Arguments.parseStringArguments(Optional.ofNullable(manifest.minecraftArguments()).map(StringUtils::tokenize).orElseGet(ArrayList::new), configuration)); Map features = getFeatures(); - manifest.getArguments().map(Arguments::game).ifPresent(arguments -> res.addAll(Arguments.parseArguments(arguments, configuration, features))); - if (manifest.getMinecraftArguments().isPresent()) { + Optional.ofNullable(manifest.arguments()).map(Arguments::game).ifPresent(arguments -> res.addAll(Arguments.parseArguments(arguments, configuration, features))); + if (Optional.ofNullable(manifest.minecraftArguments()).isPresent()) { res.addAll(Arguments.parseArguments(this.getDefaultGameArguments(), configuration, features)); } if (argumentsFromAuthInfo != null && argumentsFromAuthInfo.game() != null && !argumentsFromAuthInfo.game().isEmpty()) From 9fcb10ec8e944e9e0b8c3b03c208491ea90afb88 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 20:57:44 +0800 Subject: [PATCH 59/85] refactor: simplify getDownloadInfo method for improved readability and null handling --- .../org/jackhuang/hmcl/game/GameInstanceManifest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 55529bd0ea..186629f820 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -371,11 +371,11 @@ public Map getDownloads() { /// @return client jar download information public DownloadInfo getDownloadInfo() { DownloadInfo client = downloads == null ? null : downloads.get(DownloadType.CLIENT); - String jarName = jar == null ? id.id() : jar.id(); - if (client == null) { - return new DownloadInfo(String.format("%s%s/%s.jar", Constants.DEFAULT_VERSION_DOWNLOAD_URL, jarName, jarName)); - } else { + if (client != null) { return client; + } else { + GameInstanceID jarInstanceId = Objects.requireNonNullElse(jar, id); + return new DownloadInfo(String.format("%s%s/%s.jar", Constants.DEFAULT_VERSION_DOWNLOAD_URL, jarInstanceId, jarInstanceId)); } } From 6be770e3fdd372979a3b210a1a3debbc823d3894 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:06:02 +0800 Subject: [PATCH 60/85] refactor: rename Instances and related classes to GameInstances for clarity --- .../jackhuang/hmcl/game/HMCLGameLauncher.java | 12 ++-- .../hmcl/game/HMCLGameRepository.java | 61 +++++++++---------- .../game/LocalizedRemoteAddonRepository.java | 2 +- .../hmcl/setting/GameDirectoryManager.java | 4 +- ...conType.java => GameInstanceIconType.java} | 20 +++--- .../jackhuang/hmcl/setting/GameSettings.java | 4 +- .../setting/LegacyGameSettingsMigrator.java | 42 ++++++------- .../org/jackhuang/hmcl/ui/Controllers.java | 22 +++---- .../org/jackhuang/hmcl/ui/InstallerItem.java | 22 +++---- .../hmcl/ui/download/DownloadPage.java | 16 ++--- .../hmcl/ui/download/VersionsPage.java | 32 +++++----- .../hmcl/ui/game/GameSettingsPage.java | 8 +-- .../AddonCheckUpdatesTask.java | 2 +- .../AddonUpdatesPage.java | 2 +- .../DataPackListPage.java | 2 +- .../DataPackListPageSkin.java | 2 +- .../DownloadListPage.java | 4 +- .../{versions => instances}/DownloadPage.java | 2 +- .../GameAdvancedListItem.java | 6 +- .../GameInstanceIconDialog.java | 36 +++++------ .../GameInstancePage.java} | 58 +++++++++--------- .../ui/{versions => instances}/GameItem.java | 2 +- .../{versions => instances}/GameListCell.java | 2 +- .../{versions => instances}/GameListItem.java | 2 +- .../{versions => instances}/GameListPage.java | 2 +- .../GameListPopupMenu.java | 2 +- .../HMCLLocalizedDownloadListPage.java | 2 +- .../InstallerListPage.java | 4 +- .../ui/{versions => instances}/Instances.java | 2 +- .../{versions => instances}/ModListPage.java | 4 +- .../ModListPageSkin.java | 11 ++-- .../ModTranslations.java | 2 +- .../ResourcePackListPage.java | 4 +- .../SchematicsPage.java | 4 +- .../WorldBackupTask.java | 2 +- .../WorldBackupsPage.java | 2 +- .../WorldExportPage.java | 2 +- .../WorldExportPageSkin.java | 2 +- .../WorldInfoPage.java | 2 +- .../WorldListPage.java | 4 +- .../WorldManagePage.java | 2 +- .../WorldManageUIUtils.java | 2 +- .../org/jackhuang/hmcl/ui/main/MainPage.java | 4 +- .../org/jackhuang/hmcl/ui/main/RootPage.java | 10 +-- .../terracotta/TerracottaControllerPage.java | 2 +- .../hmcl/ui/terracotta/TerracottaPage.java | 5 +- ....java => RefreshedGameInstancesEvent.java} | 4 +- .../hmcl/game/DefaultGameRepository.java | 2 +- .../hmcl/launch/DefaultLauncher.java | 14 ++--- 49 files changed, 229 insertions(+), 232 deletions(-) rename HMCL/src/main/java/org/jackhuang/hmcl/setting/{VersionIconType.java => GameInstanceIconType.java} (76%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/AddonCheckUpdatesTask.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/AddonUpdatesPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/DataPackListPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/DataPackListPageSkin.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/DownloadListPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/DownloadPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/GameAdvancedListItem.java (94%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/GameInstanceIconDialog.java (78%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions/VersionPage.java => instances/GameInstancePage.java} (88%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/GameItem.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/GameListCell.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/GameListItem.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/GameListPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/GameListPopupMenu.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/HMCLLocalizedDownloadListPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/InstallerListPage.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/Instances.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/ModListPage.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/ModListPageSkin.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/ModTranslations.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/ResourcePackListPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/SchematicsPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/WorldBackupTask.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/WorldBackupsPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/WorldExportPage.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/WorldExportPageSkin.java (98%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/WorldInfoPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/WorldListPage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/WorldManagePage.java (99%) rename HMCL/src/main/java/org/jackhuang/hmcl/ui/{versions => instances}/WorldManageUIUtils.java (99%) rename HMCLCore/src/main/java/org/jackhuang/hmcl/event/{RefreshedVersionsEvent.java => RefreshedGameInstancesEvent.java} (90%) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java index e7023e852e..56c62ecf21 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java @@ -42,16 +42,16 @@ */ public final class HMCLGameLauncher extends DefaultLauncher { - public HMCLGameLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { - this(repository, version, authInfo, options, null); + public HMCLGameLauncher(GameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions options) { + this(repository, manifest, authInfo, options, null); } - public HMCLGameLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { - this(repository, version, authInfo, options, listener, true); + public HMCLGameLauncher(GameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { + this(repository, manifest, authInfo, options, listener, true); } - public HMCLGameLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { - super(repository, version, authInfo, options, listener, daemon); + public HMCLGameLauncher(GameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { + super(repository, manifest, authInfo, options, listener, daemon); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index a0b1739850..58fd037fed 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -46,7 +46,7 @@ import org.jackhuang.hmcl.setting.ProxyType; import org.jackhuang.hmcl.setting.SettingFileUtils; import org.jackhuang.hmcl.setting.GameSettingsPresetID; -import org.jackhuang.hmcl.setting.VersionIconType; +import org.jackhuang.hmcl.setting.GameInstanceIconType; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.util.FileSaver; import org.jackhuang.hmcl.util.Lang; @@ -85,7 +85,7 @@ public final class HMCLGameRepository extends DefaultGameRepository { public record InstanceReference(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { } - /// Directory under the version root that stores HMCL-managed instance metadata. + /// Directory under the instance root that stores HMCL-managed instance metadata. private static final String INSTANCE_METADATA_DIRECTORY = ".hmcl"; /// Directory under the instance metadata directory that stores instance configuration files. @@ -140,7 +140,7 @@ public void setSelectedInstance(@Nullable GameInstanceID instanceId) { settings().setSelectedInstance(gameDirectory.getId(), instanceId); } - /// Refreshes the selected instance ID after versions are loaded. + /// Refreshes the selected instance ID after instances are loaded. public void refreshSelectedInstance() { @Nullable GameInstanceID selectedInstance = settings().getSelectedInstance(gameDirectory.getId()); @Nullable GameInstanceID refreshedInstance = selectedInstance; @@ -247,7 +247,7 @@ public void duplicateInstance(GameInstanceID srcId, GameInstanceID dstId, boolea Path srcDir = getInstanceRoot(srcId); Path dstDir = getInstanceRoot(dstId); - GameInstanceManifest fromVersion = getInstanceManifest(srcId); + GameInstanceManifest fromManifest = getInstanceManifest(srcId); List blackList = new ArrayList<>(ModAdviser.MODPACK_BLACK_LIST); blackList.add(srcId.id() + ".jar"); @@ -255,7 +255,7 @@ public void duplicateInstance(GameInstanceID srcId, GameInstanceID dstId, boolea if (!copySaves) blackList.add("saves"); - if (Files.exists(dstDir)) throw new IOException("Version exists"); + if (Files.exists(dstDir)) throw new IOException("Instance exists"); Files.createDirectories(dstDir); FileUtils.copyDirectory(srcDir, dstDir, path -> Modpack.acceptFile(path, blackList, null)); @@ -270,7 +270,7 @@ public void duplicateInstance(GameInstanceID srcId, GameInstanceID dstId, boolea } Files.copy(fromJson, toJson); - JsonUtils.writeToJsonFile(toJson, fromVersion.withId(dstId).withJar(dstId)); + JsonUtils.writeToJsonFile(toJson, fromManifest.withId(dstId).withJar(dstId)); boolean copyOriginalGameDir; try { @@ -304,7 +304,7 @@ private GameSettings.Instance copyInstanceGameSettings(GameInstanceID instanceId return copied; } - /// Returns the HMCL-managed metadata directory under the version root. + /// Returns the HMCL-managed metadata directory under the instance root. /// /// This directory stores instance-scoped files owned by HMCL. public Path getInstanceMetadataDirectory(GameInstanceID instanceId) { @@ -586,54 +586,54 @@ public void deleteIconFile(GameInstanceID instanceId) { public Image getInstanceIconImage(@Nullable GameInstanceID instanceId) { if (instanceId == null || !isLoaded()) - return VersionIconType.DEFAULT.getIcon(); + return GameInstanceIconType.DEFAULT.getIcon(); GameSettings.Instance setting = getInstanceGameSettings(instanceId); - VersionIconType iconType = setting != null ? Lang.requireNonNullElse(setting.iconProperty().getValue(), VersionIconType.DEFAULT) : VersionIconType.DEFAULT; + GameInstanceIconType iconType = setting != null ? Lang.requireNonNullElse(setting.iconProperty().getValue(), GameInstanceIconType.DEFAULT) : GameInstanceIconType.DEFAULT; - if (iconType == VersionIconType.DEFAULT) { - GameInstanceManifest version = getInstanceManifest(instanceId).resolve(this); + if (iconType == GameInstanceIconType.DEFAULT) { + GameInstanceManifest manifest = getResolvedInstanceManifest(instanceId).launchManifest(); Optional iconFile = getInstanceIconFile(instanceId); if (iconFile.isPresent()) { try { return FXUtils.loadImage(iconFile.get(), 64, 64, true, true); } catch (Exception e) { - LOG.warning("Failed to load version icon of " + instanceId, e); + LOG.warning("Failed to load instance icon of " + instanceId, e); } } - if (LibraryAnalyzer.isModded(this, version)) { - LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); + if (LibraryAnalyzer.isModded(this, manifest)) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(manifest, null); if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.FABRIC)) - return VersionIconType.FABRIC.getIcon(); + return GameInstanceIconType.FABRIC.getIcon(); else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.QUILT)) - return VersionIconType.QUILT.getIcon(); + return GameInstanceIconType.QUILT.getIcon(); else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.LEGACY_FABRIC)) - return VersionIconType.LEGACY_FABRIC.getIcon(); + return GameInstanceIconType.LEGACY_FABRIC.getIcon(); else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.NEO_FORGE)) - return VersionIconType.NEO_FORGE.getIcon(); + return GameInstanceIconType.NEO_FORGE.getIcon(); else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.FORGE)) - return VersionIconType.FORGE.getIcon(); + return GameInstanceIconType.FORGE.getIcon(); else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.CLEANROOM)) - return VersionIconType.CLEANROOM.getIcon(); + return GameInstanceIconType.CLEANROOM.getIcon(); else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.LITELOADER)) - return VersionIconType.CHICKEN.getIcon(); + return GameInstanceIconType.CHICKEN.getIcon(); else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.OPTIFINE)) - return VersionIconType.OPTIFINE.getIcon(); + return GameInstanceIconType.OPTIFINE.getIcon(); } - String gameVersion = getGameVersion(version).orElse(null); + String gameVersion = getGameVersion(manifest).orElse(null); if (gameVersion != null) { GameVersionNumber versionNumber = GameVersionNumber.asGameVersion(gameVersion); if (versionNumber.isAprilFools()) { - return VersionIconType.APRIL_FOOLS.getIcon(); + return GameInstanceIconType.APRIL_FOOLS.getIcon(); } else if (versionNumber instanceof GameVersionNumber.LegacySnapshot) { - return VersionIconType.COMMAND.getIcon(); + return GameInstanceIconType.COMMAND.getIcon(); } else if (versionNumber instanceof GameVersionNumber.Old) { - return VersionIconType.CRAFT_TABLE.getIcon(); + return GameInstanceIconType.CRAFT_TABLE.getIcon(); } } - return VersionIconType.GRASS.getIcon(); + return GameInstanceIconType.GRASS.getIcon(); } else { return iconType.getIcon(); } @@ -807,9 +807,8 @@ public boolean unmarkInstanceLaunchedAbnormally(GameInstanceID instanceId) { private static final String PROFILE = "{\"selectedProfile\": \"(Default)\",\"profiles\": {\"(Default)\": {\"name\": \"(Default)\"}},\"clientToken\": \"88888888-8888-8888-8888-888888888888\"}"; - // These version ids are forbidden because they may conflict with modpack configuration filenames - private static final Set FORBIDDEN_INSTANCE_IDS = new HashSet<>(Arrays.asList( - "modpack", "minecraftinstance", "manifest")); + // These instance ids are forbidden because they may conflict with modpack configuration filenames + private static final Set FORBIDDEN_INSTANCE_IDS = Set.of("modpack", "minecraftinstance", "manifest"); public static boolean isValidInstanceId(String id) { if (FORBIDDEN_INSTANCE_IDS.contains(id)) @@ -823,7 +822,7 @@ public static boolean isValidInstanceId(String id) { } /** - * Returns true if the given version id conflicts with an existing version. + * Returns true if the given instance id conflicts with an existing instance. */ public boolean instanceIdConflicts(String instanceId) { try { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java index 3c531b0dd6..08800ba861 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java @@ -20,7 +20,7 @@ import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.addon.RemoteAddon; import org.jackhuang.hmcl.addon.RemoteAddonRepository; -import org.jackhuang.hmcl.ui.versions.ModTranslations; +import org.jackhuang.hmcl.ui.instances.ModTranslations; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java index e662e8a67d..4e0ac50efc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameDirectoryManager.java @@ -23,7 +23,7 @@ import javafx.collections.ObservableList; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.RefreshedVersionsEvent; +import org.jackhuang.hmcl.event.RefreshedGameInstancesEvent; import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.util.PortablePath; @@ -200,7 +200,7 @@ public static void init() { }); selectedGameDirectory.set(currentGameDirectory != null ? currentGameDirectory : mergedGameDirectories.get(0)); - EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> { + EventBus.EVENT_BUS.channel(RefreshedGameInstancesEvent.class).registerWeak(event -> { runInFX(() -> { @Nullable HMCLGameRepository repository = selectedRepository.get(); if (repository != null && repository == event.getSource()) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionIconType.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameInstanceIconType.java similarity index 76% rename from HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionIconType.java rename to HMCL/src/main/java/org/jackhuang/hmcl/setting/GameInstanceIconType.java index ed8a24cd9d..369bd815ec 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionIconType.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameInstanceIconType.java @@ -21,7 +21,7 @@ import org.jackhuang.hmcl.addon.mod.ModLoaderType; import org.jackhuang.hmcl.ui.FXUtils; -public enum VersionIconType { +public enum GameInstanceIconType { DEFAULT("/assets/img/grass.png"), GRASS("/assets/img/grass.png"), @@ -42,21 +42,21 @@ public enum VersionIconType { // Please append new items at last - public static VersionIconType getIconType(ModLoaderType modLoaderType) { + public static GameInstanceIconType getIconType(ModLoaderType modLoaderType) { return switch (modLoaderType) { - case FORGE -> VersionIconType.FORGE; - case NEO_FORGE -> VersionIconType.NEO_FORGE; - case FABRIC -> VersionIconType.FABRIC; - case QUILT -> VersionIconType.QUILT; - case LITE_LOADER -> VersionIconType.CHICKEN; - case CLEANROOM -> VersionIconType.CLEANROOM; - default -> VersionIconType.COMMAND; + case FORGE -> GameInstanceIconType.FORGE; + case NEO_FORGE -> GameInstanceIconType.NEO_FORGE; + case FABRIC -> GameInstanceIconType.FABRIC; + case QUILT -> GameInstanceIconType.QUILT; + case LITE_LOADER -> GameInstanceIconType.CHICKEN; + case CLEANROOM -> GameInstanceIconType.CLEANROOM; + default -> GameInstanceIconType.COMMAND; }; } private final String resourceUrl; - VersionIconType(String resourceUrl) { + GameInstanceIconType(String resourceUrl) { this.resourceUrl = resourceUrl; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java index 60a2c57c45..287c468c19 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java @@ -152,10 +152,10 @@ public void setBackupOnNextSave(boolean backupOnNextSave) { /// The icon of the instance. @SerializedName("icon") - private final SettingProperty icon = newSettingProperty("icon", VersionIconType.DEFAULT); + private final SettingProperty icon = newSettingProperty("icon", GameInstanceIconType.DEFAULT); /// Returns the instance icon property. - public SettingProperty iconProperty() { + public SettingProperty iconProperty() { return icon; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java index 430cbd97f0..3b34a00764 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LegacyGameSettingsMigrator.java @@ -73,22 +73,22 @@ private enum GameDirectoryType { } /// Legacy `VersionIconType` ordinal order used by old local settings. - private static final VersionIconType @Unmodifiable [] LEGACY_VERSION_ICON_TYPES = { - VersionIconType.DEFAULT, - VersionIconType.GRASS, - VersionIconType.CHEST, - VersionIconType.CHICKEN, - VersionIconType.COMMAND, - VersionIconType.OPTIFINE, - VersionIconType.CRAFT_TABLE, - VersionIconType.FABRIC, - VersionIconType.FORGE, - VersionIconType.NEO_FORGE, - VersionIconType.FURNACE, - VersionIconType.QUILT, - VersionIconType.APRIL_FOOLS, - VersionIconType.CLEANROOM, - VersionIconType.LEGACY_FABRIC + private static final GameInstanceIconType @Unmodifiable [] LEGACY_VERSION_ICON_TYPES = { + GameInstanceIconType.DEFAULT, + GameInstanceIconType.GRASS, + GameInstanceIconType.CHEST, + GameInstanceIconType.CHICKEN, + GameInstanceIconType.COMMAND, + GameInstanceIconType.OPTIFINE, + GameInstanceIconType.CRAFT_TABLE, + GameInstanceIconType.FABRIC, + GameInstanceIconType.FORGE, + GameInstanceIconType.NEO_FORGE, + GameInstanceIconType.FURNACE, + GameInstanceIconType.QUILT, + GameInstanceIconType.APRIL_FOOLS, + GameInstanceIconType.CLEANROOM, + GameInstanceIconType.LEGACY_FABRIC }; /// Legacy `JavaVersionType` ordinal order used by old game settings. @@ -488,10 +488,10 @@ private static GraphicsAPI parseGraphicsBackend(JsonObject source, Renderer rend } /// Parses the legacy icon selection with frozen ordinal order. - private static VersionIconType parseLegacyVersionIconType(@Nullable JsonObject source) { + private static GameInstanceIconType parseLegacyVersionIconType(@Nullable JsonObject source) { JsonPrimitive primitive = JsonUtils.getPrimitive(source, "versionIcon"); if (primitive == null) { - return VersionIconType.DEFAULT; + return GameInstanceIconType.DEFAULT; } try { @@ -499,11 +499,11 @@ private static VersionIconType parseLegacyVersionIconType(@Nullable JsonObject s int index = primitive.getAsInt(); return index >= 0 && index < LEGACY_VERSION_ICON_TYPES.length ? LEGACY_VERSION_ICON_TYPES[index] - : VersionIconType.DEFAULT; + : GameInstanceIconType.DEFAULT; } String value = primitive.getAsString(); - for (VersionIconType iconType : LEGACY_VERSION_ICON_TYPES) { + for (GameInstanceIconType iconType : LEGACY_VERSION_ICON_TYPES) { if (iconType.name().equalsIgnoreCase(value)) { return iconType; } @@ -511,7 +511,7 @@ private static VersionIconType parseLegacyVersionIconType(@Nullable JsonObject s } catch (RuntimeException ignored) { } - return VersionIconType.DEFAULT; + return GameInstanceIconType.DEFAULT; } /// Reads an enum property from either ordinal or name. diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index 6bad25b1c3..2435f44fdf 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -59,9 +59,9 @@ import org.jackhuang.hmcl.ui.main.LauncherSettingsPage; import org.jackhuang.hmcl.ui.main.RootPage; import org.jackhuang.hmcl.ui.terracotta.TerracottaPage; -import org.jackhuang.hmcl.ui.versions.GameListPage; -import org.jackhuang.hmcl.ui.versions.VersionPage; -import org.jackhuang.hmcl.ui.versions.Instances; +import org.jackhuang.hmcl.ui.instances.GameListPage; +import org.jackhuang.hmcl.ui.instances.GameInstancePage; +import org.jackhuang.hmcl.ui.instances.Instances; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.i18n.I18n; @@ -106,7 +106,7 @@ public final class Controllers { private static Scene scene; private static Stage stage; - private static VersionPage versionPage; + private static GameInstancePage gameInstancePage; private static Lazy gameListPage = new Lazy<>(GameListPage::new); private static Lazy rootPage = new Lazy<>(RootPage::new); private static DecoratorController decorator; @@ -142,18 +142,18 @@ public static Stage getStage() { } @FXThread - public static VersionPage getVersionPage() { - if (versionPage == null) { - versionPage = new VersionPage(); + public static GameInstancePage getVersionPage() { + if (gameInstancePage == null) { + gameInstancePage = new GameInstancePage(); } - return versionPage; + return gameInstancePage; } @FXThread public static void prepareVersionPage() { - if (versionPage == null) { + if (gameInstancePage == null) { LOG.info("Prepare the version page"); - versionPage = FXUtils.prepareNode(new VersionPage()); + gameInstancePage = FXUtils.prepareNode(new GameInstancePage()); } } @@ -717,7 +717,7 @@ public static boolean isStopped() { public static void shutdown() { rootPage = null; - versionPage = null; + gameInstancePage = null; gameListPage = null; downloadPage = null; accountListPage = null; 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 b9d08f897d..a9938e7dec 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java @@ -35,7 +35,7 @@ import javafx.scene.input.MouseButton; import javafx.scene.layout.*; import org.jackhuang.hmcl.download.LibraryAnalyzer; -import org.jackhuang.hmcl.setting.VersionIconType; +import org.jackhuang.hmcl.setting.GameInstanceIconType; import org.jackhuang.hmcl.ui.construct.ImageContainer; import org.jackhuang.hmcl.ui.construct.RipplerContainer; import org.jackhuang.hmcl.util.i18n.I18n; @@ -54,7 +54,7 @@ */ public class InstallerItem extends Control { private final String id; - private final VersionIconType iconType; + private final GameInstanceIconType iconType; private final Style style; private final ObjectProperty versionProperty = new SimpleObjectProperty<>(this, "version", null); private final ObjectProperty resolvedStateProperty = new SimpleObjectProperty<>(this, "resolvedState", InstallableState.INSTANCE); @@ -92,15 +92,15 @@ public InstallerItem(String id, Style style) { this.style = style; iconType = switch (id) { - case "game" -> VersionIconType.GRASS; - case "fabric", "fabric-api" -> VersionIconType.FABRIC; - case "legacyfabric", "legacyfabric-api" -> VersionIconType.LEGACY_FABRIC; - case "forge" -> VersionIconType.FORGE; - case "cleanroom" -> VersionIconType.CLEANROOM; - case "liteloader" -> VersionIconType.CHICKEN; - case "optifine" -> VersionIconType.OPTIFINE; - case "quilt", "quilt-api" -> VersionIconType.QUILT; - case "neoforge" -> VersionIconType.NEO_FORGE; + case "game" -> GameInstanceIconType.GRASS; + case "fabric", "fabric-api" -> GameInstanceIconType.FABRIC; + case "legacyfabric", "legacyfabric-api" -> GameInstanceIconType.LEGACY_FABRIC; + case "forge" -> GameInstanceIconType.FORGE; + case "cleanroom" -> GameInstanceIconType.CLEANROOM; + case "liteloader" -> GameInstanceIconType.CHICKEN; + case "optifine" -> GameInstanceIconType.OPTIFINE; + case "quilt", "quilt-api" -> GameInstanceIconType.QUILT; + case "neoforge" -> GameInstanceIconType.NEO_FORGE; default -> null; }; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java index 997aabf666..a00e0f864a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java @@ -43,10 +43,10 @@ import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.ui.versions.DownloadListPage; -import org.jackhuang.hmcl.ui.versions.HMCLLocalizedDownloadListPage; -import org.jackhuang.hmcl.ui.versions.VersionPage; -import org.jackhuang.hmcl.ui.versions.Instances; +import org.jackhuang.hmcl.ui.instances.DownloadListPage; +import org.jackhuang.hmcl.ui.instances.HMCLLocalizedDownloadListPage; +import org.jackhuang.hmcl.ui.instances.GameInstancePage; +import org.jackhuang.hmcl.ui.instances.Instances; import org.jackhuang.hmcl.ui.wizard.Navigation; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardProvider; @@ -69,11 +69,11 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage { - public static final org.jackhuang.hmcl.ui.versions.DownloadPage.DownloadCallback FOR_MOD = + public static final org.jackhuang.hmcl.ui.instances.DownloadPage.DownloadCallback FOR_MOD = (downloadProvider, repository, version, mod, file) -> download(downloadProvider, repository, version, file, "mods"); - public static final org.jackhuang.hmcl.ui.versions.DownloadPage.DownloadCallback FOR_RESOURCE_PACK = + public static final org.jackhuang.hmcl.ui.instances.DownloadPage.DownloadCallback FOR_RESOURCE_PACK = (downloadProvider, repository, version, pack, file) -> download(downloadProvider, repository, version, file, "resourcepacks"); - public static final org.jackhuang.hmcl.ui.versions.DownloadPage.DownloadCallback FOR_SHADER = + public static final org.jackhuang.hmcl.ui.instances.DownloadPage.DownloadCallback FOR_SHADER = (downloadProvider, repository, version, shader, file) -> download(downloadProvider, repository, version, file, "shaderpacks"); private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("download"), -1)); @@ -135,7 +135,7 @@ public DownloadPage(GameInstanceID uploadInstance) { private static Supplier loadVersionFor(Supplier nodeSupplier) { return () -> { T node = nodeSupplier.get(); - if (node instanceof VersionPage.GameInstanceLoadable loadable) { + if (node instanceof GameInstancePage.GameInstanceLoadable loadable) { loadable.loadInstance(GameDirectoryManager.getSelectedRepository(), null); } return node; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index b50448370c..c9c4ba7592 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -45,7 +45,7 @@ import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion; import org.jackhuang.hmcl.download.quilt.QuiltAPIRemoteVersion; import org.jackhuang.hmcl.download.quilt.QuiltRemoteVersion; -import org.jackhuang.hmcl.setting.VersionIconType; +import org.jackhuang.hmcl.setting.GameInstanceIconType; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.FXUtils; @@ -238,21 +238,21 @@ public void updateItem(RemoteVersion remoteVersion, boolean empty) { switch (versionType) { case RELEASE -> { twoLineListItem.addTag(i18n("version.game.release")); - imageView.setImage(VersionIconType.GRASS.getIcon()); + imageView.setImage(GameInstanceIconType.GRASS.getIcon()); } case SNAPSHOT, PENDING, UNOBFUSCATED -> { if (versionType == RemoteVersion.Type.SNAPSHOT && GameVersionNumber.asGameVersion(remoteVersion.getGameVersion()).isAprilFools()) { twoLineListItem.addTag(i18n("version.game.april_fools")); - imageView.setImage(VersionIconType.APRIL_FOOLS.getIcon()); + imageView.setImage(GameInstanceIconType.APRIL_FOOLS.getIcon()); } else { twoLineListItem.addTag(i18n("version.game.snapshot")); - imageView.setImage(VersionIconType.COMMAND.getIcon()); + imageView.setImage(GameInstanceIconType.COMMAND.getIcon()); } } default -> { twoLineListItem.addTag(i18n("version.game.old")); - imageView.setImage(VersionIconType.CRAFT_TABLE.getIcon()); + imageView.setImage(GameInstanceIconType.CRAFT_TABLE.getIcon()); } } @@ -261,25 +261,25 @@ public void updateItem(RemoteVersion remoteVersion, boolean empty) { case UNSUPPORTED -> twoLineListItem.addTagWarning(i18n("version.game.support_status.unsupported")); } } else { - VersionIconType iconType; + GameInstanceIconType iconType; if (remoteVersion instanceof LiteLoaderRemoteVersion) - iconType = VersionIconType.CHICKEN; + iconType = GameInstanceIconType.CHICKEN; else if (remoteVersion instanceof OptiFineRemoteVersion) - iconType = VersionIconType.OPTIFINE; + iconType = GameInstanceIconType.OPTIFINE; else if (remoteVersion instanceof ForgeRemoteVersion) - iconType = VersionIconType.FORGE; + iconType = GameInstanceIconType.FORGE; else if (remoteVersion instanceof CleanroomRemoteVersion) - iconType = VersionIconType.CLEANROOM; + iconType = GameInstanceIconType.CLEANROOM; else if (remoteVersion instanceof NeoForgeRemoteVersion) - iconType = VersionIconType.NEO_FORGE; + iconType = GameInstanceIconType.NEO_FORGE; else if (remoteVersion instanceof LegacyFabricRemoteVersion || remoteVersion instanceof LegacyFabricAPIRemoteVersion) - iconType = VersionIconType.LEGACY_FABRIC; + iconType = GameInstanceIconType.LEGACY_FABRIC; else if (remoteVersion instanceof FabricRemoteVersion || remoteVersion instanceof FabricAPIRemoteVersion) - iconType = VersionIconType.FABRIC; + iconType = GameInstanceIconType.FABRIC; else if (remoteVersion instanceof QuiltRemoteVersion || remoteVersion instanceof QuiltAPIRemoteVersion) - iconType = VersionIconType.QUILT; + iconType = GameInstanceIconType.QUILT; else - iconType = VersionIconType.COMMAND; + iconType = GameInstanceIconType.COMMAND; imageView.setImage(iconType.getIcon()); String displayGameVersion = I18n.getDisplayVersion(GameVersionNumber.asGameVersion(remoteVersion.getGameVersion())); @@ -393,7 +393,7 @@ private static final class VersionsPageSkin extends SkinBase { RemoteVersionListCell dummyCell = new RemoteVersionListCell(control); dummyCell.twoLineListItem.setTitle("Dummy"); dummyCell.twoLineListItem.setSubtitle("Dummy"); - dummyCell.imageView.setImage(VersionIconType.GRASS.getIcon()); + dummyCell.imageView.setImage(GameInstanceIconType.GRASS.getIcon()); prepareNode(dummyCell.pane); list.setFixedCellSize(dummyCell.pane.prefHeight(-1)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java index af0608b2f1..7358590e49 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java @@ -50,8 +50,8 @@ import org.jackhuang.hmcl.ui.*; import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.ui.versions.GameInstanceIconDialog; -import org.jackhuang.hmcl.ui.versions.VersionPage; +import org.jackhuang.hmcl.ui.instances.GameInstanceIconDialog; +import org.jackhuang.hmcl.ui.instances.GameInstancePage; import org.jackhuang.hmcl.util.Holder; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.ServerAddress; @@ -78,7 +78,7 @@ /// @author Glavo @NotNullByDefault public final class GameSettingsPage extends StackPane - implements DecoratorPage, VersionPage.GameInstanceLoadable, PageAware { + implements DecoratorPage, GameInstancePage.GameInstanceLoadable, PageAware { private static final Object INHERIT_BUTTON_TOOLTIP_KEY = new Object(); private static final PseudoClass PSEUDO_OVERRIDDEN = PseudoClass.getPseudoClass("overridden"); @@ -2661,7 +2661,7 @@ private void onDeleteIcon() { repository.deleteIconFile(loadedInstanceId); GameSettings.Instance localGameSettings = repository.getInstanceGameSettingsOrCreate(loadedInstanceId); if (localGameSettings != null) { - localGameSettings.iconProperty().setValue(VersionIconType.DEFAULT); + localGameSettings.iconProperty().setValue(GameInstanceIconType.DEFAULT); } loadIcon(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonCheckUpdatesTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonCheckUpdatesTask.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonCheckUpdatesTask.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonCheckUpdatesTask.java index 47597d34a6..531bbaa94f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonCheckUpdatesTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonCheckUpdatesTask.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.addon.LocalAddonFile; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index b9c251715f..1f9fbb7f02 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXCheckBox; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DataPackListPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DataPackListPage.java index 76d130b17a..e2218bb99b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DataPackListPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import javafx.beans.property.BooleanProperty; import javafx.collections.ObservableList; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DataPackListPageSkin.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPageSkin.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DataPackListPageSkin.java index 40fc80daf5..bdbf7e1630 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DataPackListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DataPackListPageSkin.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXCheckBox; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java index a7fe4463a8..8f002ccd3a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXComboBox; @@ -68,7 +68,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor; -public class DownloadListPage extends Control implements DecoratorPage, VersionPage.GameInstanceLoadable { +public class DownloadListPage extends Control implements DecoratorPage, GameInstancePage.GameInstanceLoadable { protected final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(); private final BooleanProperty loading = new SimpleBooleanProperty(false); private final BooleanProperty failed = new SimpleBooleanProperty(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadPage.java index f0d89aab7d..85857bb4cd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXDialogLayout; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameAdvancedListItem.java similarity index 94% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameAdvancedListItem.java index d84d031e14..00c8f03e47 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameAdvancedListItem.java @@ -15,14 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import javafx.geometry.Pos; import org.jackhuang.hmcl.event.Event; import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.GameDirectoryManager; -import org.jackhuang.hmcl.setting.VersionIconType; +import org.jackhuang.hmcl.setting.GameInstanceIconType; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.WeakListenerHolder; import org.jackhuang.hmcl.ui.construct.AdvancedListItem; @@ -68,6 +68,6 @@ private void loadInstance(GameInstanceID instanceId) { setTitle(i18n("version.empty")); setSubtitle(i18n("version.empty.add")); - imageContainer.setImage(VersionIconType.DEFAULT.getIcon()); + imageContainer.setImage(GameInstanceIconType.DEFAULT.getIcon()); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameInstanceIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstanceIconDialog.java similarity index 78% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameInstanceIconDialog.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstanceIconDialog.java index c5613c0e03..fcd902f395 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameInstanceIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstanceIconDialog.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import javafx.scene.Node; import javafx.scene.image.ImageView; @@ -25,7 +25,7 @@ import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.GameSettings; -import org.jackhuang.hmcl.setting.VersionIconType; +import org.jackhuang.hmcl.setting.GameInstanceIconType; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; @@ -57,20 +57,20 @@ public GameInstanceIconDialog(HMCLGameRepository repository, GameInstanceID inst pane.getChildren().setAll( createCustomIcon(), - createIcon(VersionIconType.GRASS), - createIcon(VersionIconType.CHEST), - createIcon(VersionIconType.CHICKEN), - createIcon(VersionIconType.COMMAND), - createIcon(VersionIconType.APRIL_FOOLS), - createIcon(VersionIconType.OPTIFINE), - createIcon(VersionIconType.CRAFT_TABLE), - createIcon(VersionIconType.FABRIC), - createIcon(VersionIconType.LEGACY_FABRIC), - createIcon(VersionIconType.FORGE), - createIcon(VersionIconType.CLEANROOM), - createIcon(VersionIconType.NEO_FORGE), - createIcon(VersionIconType.FURNACE), - createIcon(VersionIconType.QUILT) + createIcon(GameInstanceIconType.GRASS), + createIcon(GameInstanceIconType.CHEST), + createIcon(GameInstanceIconType.CHICKEN), + createIcon(GameInstanceIconType.COMMAND), + createIcon(GameInstanceIconType.APRIL_FOOLS), + createIcon(GameInstanceIconType.OPTIFINE), + createIcon(GameInstanceIconType.CRAFT_TABLE), + createIcon(GameInstanceIconType.FABRIC), + createIcon(GameInstanceIconType.LEGACY_FABRIC), + createIcon(GameInstanceIconType.FORGE), + createIcon(GameInstanceIconType.CLEANROOM), + createIcon(GameInstanceIconType.NEO_FORGE), + createIcon(GameInstanceIconType.FURNACE), + createIcon(GameInstanceIconType.QUILT) ); } @@ -83,7 +83,7 @@ private void exploreIcon() { repository.setInstanceIconFile(instanceId, selectedFile); if (setting != null) { - setting.iconProperty().setValue(VersionIconType.DEFAULT); + setting.iconProperty().setValue(GameInstanceIconType.DEFAULT); } onAccept(); @@ -103,7 +103,7 @@ private Node createCustomIcon() { return container; } - private Node createIcon(VersionIconType type) { + private Node createIcon(GameInstanceIconType type) { ImageView imageView = new ImageView(type.getIcon()); imageView.setMouseTransparent(true); RipplerContainer container = new RipplerContainer(imageView); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstancePage.java similarity index 88% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstancePage.java index 64e61e05c6..6f9479cb42 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstancePage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXPopup; import javafx.application.Platform; @@ -28,7 +28,7 @@ import javafx.scene.layout.VBox; import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.EventPriority; -import org.jackhuang.hmcl.event.RefreshedVersionsEvent; +import org.jackhuang.hmcl.event.RefreshedGameInstancesEvent; import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.GameSettings; @@ -54,17 +54,17 @@ import static org.jackhuang.hmcl.ui.FXUtils.runInFX; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage { +public class GameInstancePage extends DecoratorAnimatedPage implements DecoratorPage { private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(); private final TabHeader tab; - private final TabHeader.Tab> versionSettingsTab = new TabHeader.Tab<>("versionSettingsTab"); + private final TabHeader.Tab> gameSettingsTab = new TabHeader.Tab<>("versionSettingsTab"); private final TabHeader.Tab installerListTab = new TabHeader.Tab<>("installerListTab"); private final TabHeader.Tab modListTab = new TabHeader.Tab<>("modListTab"); private final TabHeader.Tab worldListTab = new TabHeader.Tab<>("worldList"); private final TabHeader.Tab schematicsTab = new TabHeader.Tab<>("schematicsTab"); private final TabHeader.Tab resourcePackTab = new TabHeader.Tab<>("resourcePackTab"); private final TransitionPane transitionPane = new TransitionPane(); - private final BooleanProperty currentVersionUpgradable = new SimpleBooleanProperty(); + private final BooleanProperty currentInstanceUpgradable = new SimpleBooleanProperty(); private final ObjectProperty instanceReference = new SimpleObjectProperty<>(); private final WeakListenerHolder listenerHolder = new WeakListenerHolder(); @@ -78,16 +78,16 @@ public WorkingDirChangedEvent() { } } - public VersionPage() { - versionSettingsTab.setNodeSupplier(loadVersionFor(() -> new GameSettingsPage<>(GameSettings.Instance.class))); - installerListTab.setNodeSupplier(loadVersionFor(InstallerListPage::new)); - modListTab.setNodeSupplier(loadVersionFor(ModListPage::new)); - resourcePackTab.setNodeSupplier(loadVersionFor(ResourcePackListPage::new)); - worldListTab.setNodeSupplier(loadVersionFor(WorldListPage::new)); - schematicsTab.setNodeSupplier(loadVersionFor(SchematicsPage::new)); + public GameInstancePage() { + gameSettingsTab.setNodeSupplier(loadInstanceFor(() -> new GameSettingsPage<>(GameSettings.Instance.class))); + installerListTab.setNodeSupplier(loadInstanceFor(InstallerListPage::new)); + modListTab.setNodeSupplier(loadInstanceFor(ModListPage::new)); + resourcePackTab.setNodeSupplier(loadInstanceFor(ResourcePackListPage::new)); + worldListTab.setNodeSupplier(loadInstanceFor(WorldListPage::new)); + schematicsTab.setNodeSupplier(loadInstanceFor(SchematicsPage::new)); - tab = new TabHeader(transitionPane, versionSettingsTab, installerListTab, modListTab, resourcePackTab, worldListTab, schematicsTab); - tab.select(versionSettingsTab); + tab = new TabHeader(transitionPane, gameSettingsTab, installerListTab, modListTab, resourcePackTab, worldListTab, schematicsTab); + tab.select(gameSettingsTab); addEventHandler(Navigator.NavigationEvent.NAVIGATED, this::onNavigated); @@ -106,10 +106,10 @@ public VersionPage() { } }); - listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> checkSelectedVersion(), EventPriority.HIGHEST)); + listenerHolder.add(EventBus.EVENT_BUS.channel(RefreshedGameInstancesEvent.class).registerWeak(event -> checkSelectedInstnace(), EventPriority.HIGHEST)); } - private void checkSelectedVersion() { + private void checkSelectedInstnace() { runInFX(() -> { if (this.instanceReference.get() == null) return; HMCLGameRepository repository = this.instanceReference.get().repository(); @@ -124,11 +124,11 @@ private void checkSelectedVersion() { }); } - private Supplier loadVersionFor(Supplier nodeSupplier) { + private Supplier loadInstanceFor(Supplier nodeSupplier) { return () -> { T node = nodeSupplier.get(); if (instanceReference.get() != null) { - if (node instanceof VersionPage.GameInstanceLoadable loadable) { + if (node instanceof GameInstancePage.GameInstanceLoadable loadable) { @Nullable GameInstanceID instanceId = instanceReference.get().instanceId(); loadable.loadInstance(instanceReference.get().repository(), instanceId); } @@ -138,11 +138,11 @@ private Supplier loadVersionFor(Supplier nodeSupplier) { } public void showInstanceSettings() { - tab.select(versionSettingsTab, false); + tab.select(gameSettingsTab, false); } - public void setInstance(GameInstanceID version, HMCLGameRepository repository) { - this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, version)); + public void setInstance(GameInstanceID instanceId, HMCLGameRepository repository) { + this.instanceReference.set(new HMCLGameRepository.InstanceReference(repository, instanceId)); } public void loadInstance(GameInstanceID instanceId, HMCLGameRepository repository) { @@ -157,8 +157,8 @@ public void loadInstance(GameInstanceID instanceId, HMCLGameRepository repositor setInstance(instanceId, repository); preferredInstanceId = instanceId; - if (versionSettingsTab.isInitialized()) - versionSettingsTab.getNode().loadInstance(repository, instanceId); + if (gameSettingsTab.isInitialized()) + gameSettingsTab.getNode().loadInstance(repository, instanceId); if (installerListTab.isInitialized()) installerListTab.getNode().loadInstance(repository, instanceId); if (modListTab.isInitialized()) @@ -169,7 +169,7 @@ public void loadInstance(GameInstanceID instanceId, HMCLGameRepository repositor worldListTab.getNode().loadInstance(repository, instanceId); if (schematicsTab.isInitialized()) schematicsTab.getNode().loadInstance(repository, instanceId); - currentVersionUpgradable.set(repository.isModpack(instanceId)); + currentInstanceUpgradable.set(repository.isModpack(instanceId)); } private void onNavigated(Navigator.NavigationEvent event) { @@ -248,7 +248,7 @@ private void export() { private void rename() { Instances.renameInstance(getRepository(), getInstanceId()) - .thenApply(newVersionName -> this.preferredInstanceId = new GameInstanceID(newVersionName)); + .thenApply(newInstanceId -> this.preferredInstanceId = new GameInstanceID(newInstanceId)); } private void remove() { @@ -279,19 +279,19 @@ public ReadOnlyObjectProperty stateProperty() { return state.getReadOnlyProperty(); } - public static class Skin extends DecoratorAnimatedPageSkin { + public static class Skin extends DecoratorAnimatedPageSkin { /** * Constructor for all SkinBase instances. * * @param control The control for which this Skin should attach to. */ - protected Skin(VersionPage control) { + protected Skin(GameInstancePage control) { super(control); { AdvancedListBox sideBar = new AdvancedListBox() - .addNavigationDrawerTab(control.tab, control.versionSettingsTab, i18n("settings.game"), SVG.SETTINGS, SVG.SETTINGS_FILL) + .addNavigationDrawerTab(control.tab, control.gameSettingsTab, i18n("settings.game"), SVG.SETTINGS, SVG.SETTINGS_FILL) .addNavigationDrawerTab(control.tab, control.installerListTab, i18n("settings.tabs.installers"), SVG.DEPLOYED_CODE, SVG.DEPLOYED_CODE_FILL) .addNavigationDrawerTab(control.tab, control.modListTab, i18n("mods.manage"), SVG.EXTENSION, SVG.EXTENSION_FILL) .addNavigationDrawerTab(control.tab, control.resourcePackTab, i18n("resourcepack.manage"), SVG.TEXTURE) @@ -333,7 +333,7 @@ protected Skin(VersionPage control) { AdvancedListBox toolbar = new AdvancedListBox() .addNavigationDrawerItem(i18n("version.update"), SVG.UPDATE, control::updateGame, upgradeItem -> { - upgradeItem.visibleProperty().bind(control.currentVersionUpgradable); + upgradeItem.visibleProperty().bind(control.currentInstanceUpgradable); }) .addNavigationDrawerItem(i18n("version.launch.test"), SVG.ROCKET_LAUNCH, control::testGame) .addNavigationDrawerItem(i18n("settings.game.exploration"), SVG.FOLDER_OPEN, null, browseMenuItem -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameItem.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameItem.java index e17bc4ddf0..a5a9f6b02b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameItem.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import javafx.beans.property.*; import javafx.scene.image.Image; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListCell.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListCell.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java index 98afdaf595..67669c353f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListCell.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.*; import javafx.beans.property.SimpleStringProperty; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListItem.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListItem.java index 44e6be52cc..16ae0a4cc6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListItem.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPage.java index 624cba4c7c..c727003ab9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXListView; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java index cce882fd10..4b5fc05cb2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPopupMenu.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXListView; import com.jfoenix.controls.JFXPopup; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/HMCLLocalizedDownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/HMCLLocalizedDownloadListPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java index 2c1aa9b61f..d167eda0f7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/HMCLLocalizedDownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import org.jackhuang.hmcl.game.LocalizedRemoteAddonRepository; import org.jackhuang.hmcl.addon.RemoteAddonRepository; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java index 69417ad611..fd49f4a4ae 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import javafx.application.Platform; import javafx.scene.Node; @@ -44,7 +44,7 @@ import static org.jackhuang.hmcl.ui.FXUtils.runInFX; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class InstallerListPage extends ListPageBase implements VersionPage.GameInstanceLoadable { +public class InstallerListPage extends ListPageBase implements GameInstancePage.GameInstanceLoadable { private HMCLGameRepository repository; private GameInstanceID instanceId; private GameInstanceManifest version; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java index 1426ff0480..a5c005052d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import org.jackhuang.hmcl.setting.GameDirectoryManager; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ModListPage.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ModListPage.java index 328e746a00..5852840242 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ModListPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -53,7 +53,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.logging.Logger.LOG; -public final class ModListPage extends ListPageBase implements VersionPage.GameInstanceLoadable, PageAware { +public final class ModListPage extends ListPageBase implements GameInstancePage.GameInstanceLoadable, PageAware { private final BooleanProperty modded = new SimpleBooleanProperty(this, "modded", false); private final ReentrantLock lock = new ReentrantLock(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ModListPageSkin.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ModListPageSkin.java index 603ecbfdcd..da62b941cf 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ModListPageSkin.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.*; import javafx.animation.PauseTransition; @@ -42,10 +42,9 @@ import org.jackhuang.hmcl.addon.mod.LocalModFile; import org.jackhuang.hmcl.addon.mod.ModLoaderType; import org.jackhuang.hmcl.addon.repository.ModrinthRemoteAddonRepository; -import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.setting.DownloadProviders; -import org.jackhuang.hmcl.setting.VersionIconType; +import org.jackhuang.hmcl.setting.GameInstanceIconType; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; @@ -344,7 +343,7 @@ private Image loadIcon() { LOG.warning("Failed to load mod icons", e); } - return VersionIconType.getIconType(this.localModFile.getModLoaderType()).getIcon(); + return GameInstanceIconType.getIconType(this.localModFile.getModLoaderType()).getIcon(); } public void loadIcon(ImageContainer imageContainer, @Nullable WeakReference> current) { @@ -360,7 +359,7 @@ public void loadIcon(ImageContainer imageContainer, @Nullable WeakReference(imageFuture); } - imageContainer.setImage(VersionIconType.getIconType(localModFile.getModLoaderType()).getIcon()); + imageContainer.setImage(GameInstanceIconType.getIconType(localModFile.getModLoaderType()).getIcon()); imageFuture.thenAcceptAsync(image -> { if (current != null) { ObjectProperty infoObjectProperty = current.get(); @@ -544,7 +543,7 @@ final class ModInfoListCell extends MDListCell { content.setMouseTransparent(true); setSelectable(); - imageContainer.setImage(VersionIconType.COMMAND.getIcon()); + imageContainer.setImage(GameInstanceIconType.COMMAND.getIcon()); FXUtils.installFastTooltip(restoreButton, i18n("mods.restore")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModTranslations.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ModTranslations.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModTranslations.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ModTranslations.java index e7bb8328d4..c19d97f729 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModTranslations.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ModTranslations.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import org.jackhuang.hmcl.addon.RemoteAddonRepository; import org.jackhuang.hmcl.util.Pair; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ResourcePackListPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ResourcePackListPage.java index aec26b544a..3d377e3162 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/ResourcePackListPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.*; import javafx.animation.PauseTransition; @@ -75,7 +75,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.logging.Logger.LOG; -public final class ResourcePackListPage extends ListPageBase implements VersionPage.GameInstanceLoadable { +public final class ResourcePackListPage extends ListPageBase implements GameInstancePage.GameInstanceLoadable { private static final String TIP_KEY = "resourcePackWarning"; private static @Nullable String getWarning(ResourcePackFile.Compatibility compatibility) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/SchematicsPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/SchematicsPage.java index 04a38507a3..24617c50e5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/SchematicsPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXDialogLayout; @@ -63,7 +63,7 @@ /** * @author Glavo */ -public final class SchematicsPage extends ListPageBase implements VersionPage.GameInstanceLoadable { +public final class SchematicsPage extends ListPageBase implements GameInstancePage.GameInstanceLoadable { private static String translateAuthorName(String author) { if (I18n.isUseChinese() && "hsds".equals(author)) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldBackupTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldBackupTask.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldBackupTask.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldBackupTask.java index 79d704bd90..a51e956a6d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldBackupTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldBackupTask.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import org.jackhuang.hmcl.game.World; import org.jackhuang.hmcl.task.Task; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldBackupsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldBackupsPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldBackupsPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldBackupsPage.java index 76a41ce665..7fae552882 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldBackupsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldBackupsPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import javafx.beans.property.BooleanProperty; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldExportPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldExportPage.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldExportPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldExportPage.java index d7fee2c151..d0161eb609 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldExportPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldExportPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldExportPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldExportPageSkin.java similarity index 98% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldExportPageSkin.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldExportPageSkin.java index 1430b4b41c..2255c4da10 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldExportPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldExportPageSkin.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXTextField; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldInfoPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldInfoPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldInfoPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldInfoPage.java index 2c508aebae..b04162855a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldInfoPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldInfoPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXTextField; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldListPage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldListPage.java index a9ab987e0c..bab9f30a0e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldListPage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXCheckBox; @@ -63,7 +63,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.logging.Logger.LOG; -public final class WorldListPage extends ListPageBase implements VersionPage.GameInstanceLoadable { +public final class WorldListPage extends ListPageBase implements GameInstancePage.GameInstanceLoadable { private final BooleanProperty showAll = new SimpleBooleanProperty(this, "showAll", false); private Path savesDir; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManagePage.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManagePage.java index 211ad1f541..d807d4b819 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManagePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManagePage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import com.jfoenix.controls.JFXPopup; import javafx.application.Platform; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManageUIUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManageUIUtils.java similarity index 99% rename from HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManageUIUtils.java rename to HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManageUIUtils.java index c99e083d2d..179946b4ba 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldManageUIUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManageUIUtils.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.jackhuang.hmcl.ui.versions; +package org.jackhuang.hmcl.ui.instances; import javafx.stage.FileChooser; import org.jackhuang.hmcl.game.World; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index 0eaa34e226..f9b17611ea 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -62,8 +62,8 @@ import org.jackhuang.hmcl.ui.construct.MessageDialogPane; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; -import org.jackhuang.hmcl.ui.versions.GameListPopupMenu; -import org.jackhuang.hmcl.ui.versions.Instances; +import org.jackhuang.hmcl.ui.instances.GameListPopupMenu; +import org.jackhuang.hmcl.ui.instances.Instances; import org.jackhuang.hmcl.upgrade.RemoteVersion; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.upgrade.UpdateHandler; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 4fc8000056..3a672e8c73 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -22,7 +22,7 @@ import javafx.scene.layout.Region; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.event.EventBus; -import org.jackhuang.hmcl.event.RefreshedVersionsEvent; +import org.jackhuang.hmcl.event.RefreshedGameInstancesEvent; import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; @@ -47,9 +47,9 @@ import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider; import org.jackhuang.hmcl.ui.nbt.NBTEditorPage; import org.jackhuang.hmcl.ui.nbt.NBTFileType; -import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem; -import org.jackhuang.hmcl.ui.versions.GameListPopupMenu; -import org.jackhuang.hmcl.ui.versions.Instances; +import org.jackhuang.hmcl.ui.instances.GameAdvancedListItem; +import org.jackhuang.hmcl.ui.instances.GameListPopupMenu; +import org.jackhuang.hmcl.ui.instances.Instances; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; @@ -75,7 +75,7 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage { private MainPage mainPage = null; public RootPage() { - EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class) + EventBus.EVENT_BUS.channel(RefreshedGameInstancesEvent.class) .register(event -> onRefreshedVersions((HMCLGameRepository) event.getSource())); HMCLGameRepository repository = GameDirectoryManager.getSelectedRepository(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java index 2b558e8dba..67e88371dd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java @@ -51,7 +51,7 @@ import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.TransitionPane; import org.jackhuang.hmcl.ui.construct.*; -import org.jackhuang.hmcl.ui.versions.Instances; +import org.jackhuang.hmcl.ui.instances.Instances; import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.i18n.LocaleUtils; import org.jackhuang.hmcl.util.io.FileUtils; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java index 5e0e8f727b..6ec2a39f71 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java @@ -38,10 +38,9 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.main.MainPage; -import org.jackhuang.hmcl.ui.versions.GameListPopupMenu; -import org.jackhuang.hmcl.ui.versions.Instances; +import org.jackhuang.hmcl.ui.instances.GameListPopupMenu; +import org.jackhuang.hmcl.ui.instances.Instances; import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.StringUtils; import static org.jackhuang.hmcl.setting.SettingsManager.userState; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedGameInstancesEvent.java similarity index 90% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedGameInstancesEvent.java index a8c36ff822..b7e6bccaa2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedVersionsEvent.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/event/RefreshedGameInstancesEvent.java @@ -26,14 +26,14 @@ * * @author huangyuhui */ -public final class RefreshedVersionsEvent extends Event { +public final class RefreshedGameInstancesEvent extends Event { /** * Constructor. * * @param source {@link GameRepository} */ - public RefreshedVersionsEvent(Object source) { + public RefreshedGameInstancesEvent(Object source) { super(source); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 10f351b78f..b4eeedf0c8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -117,7 +117,7 @@ public void refresh() { refreshImpl(); loaded = true; - EventBus.EVENT_BUS.fireEvent(new RefreshedVersionsEvent(this)); + EventBus.EVENT_BUS.fireEvent(new RefreshedGameInstancesEvent(this)); } protected void refreshImpl() { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 6f43783573..4ef8e3af8b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -52,18 +52,18 @@ public class DefaultLauncher extends Launcher { private final LibraryAnalyzer analyzer; - public DefaultLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options) { - this(repository, version, authInfo, options, null); + public DefaultLauncher(GameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions options) { + this(repository, manifest, authInfo, options, null); } - public DefaultLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { - this(repository, version, authInfo, options, listener, true); + public DefaultLauncher(GameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions options, ProcessListener listener) { + this(repository, manifest, authInfo, options, listener, true); } - public DefaultLauncher(GameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { - super(repository, version, authInfo, options, listener, daemon); + public DefaultLauncher(GameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions options, ProcessListener listener, boolean daemon) { + super(repository, manifest, authInfo, options, listener, daemon); - this.analyzer = LibraryAnalyzer.analyze(version, repository.getGameVersion(version).orElse(null)); + this.analyzer = LibraryAnalyzer.analyze(manifest, repository.getGameVersion(manifest).orElse(null)); } private Command generateCommandLine(Path nativeFolder) throws IOException { From 680993b3b9bb38e0b5ffa285c3b1a45e0b17ad64 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:19:26 +0800 Subject: [PATCH 61/85] refactor: rename GameInstanceManifest references to manifest for consistency --- .../jackhuang/hmcl/game/LauncherHelper.java | 30 +++--- .../org/jackhuang/hmcl/java/JavaManager.java | 12 +-- .../jackhuang/hmcl/setting/GameSettings.java | 10 +- .../jackhuang/hmcl/ui/GameCrashWindow.java | 12 +-- .../ui/download/AdditionalInstallersPage.java | 10 +- .../UpdateInstallerWizardProvider.java | 10 +- .../hmcl/ui/game/GameSettingsPage.java | 10 +- .../hmcl/ui/instances/InstallerListPage.java | 14 +-- .../hmcl/ui/instances/Instances.java | 8 +- .../jackhuang/hmcl/util/NativePatcher.java | 28 +++--- .../download/DefaultDependencyManager.java | 36 ++++---- .../hmcl/download/DependencyManager.java | 8 +- .../hmcl/download/LibraryAnalyzer.java | 28 +++--- .../jackhuang/hmcl/download/MaintainTask.java | 92 +++++++++---------- .../cleanroom/CleanroomInstallTask.java | 20 ++-- .../download/fabric/FabricAPIInstallTask.java | 8 +- .../download/fabric/FabricInstallTask.java | 10 +- .../hmcl/download/forge/ForgeInstallTask.java | 30 +++--- .../download/forge/ForgeNewInstallTask.java | 24 ++--- .../download/forge/ForgeOldInstallTask.java | 8 +- .../hmcl/download/game/GameDownloadTask.java | 12 +-- .../hmcl/download/game/GameInstallTask.java | 8 +- .../hmcl/download/game/GameLibrariesTask.java | 26 +++--- .../game/GameVerificationFixTask.java | 10 +- .../LegacyFabricAPIInstallTask.java | 8 +- .../legacyfabric/LegacyFabricInstallTask.java | 8 +- .../liteloader/LiteLoaderInstallTask.java | 8 +- .../neoforge/NeoForgeInstallTask.java | 8 +- .../neoforge/NeoForgeOldInstallTask.java | 24 ++--- .../optifine/OptiFineInstallTask.java | 24 ++--- .../download/quilt/QuiltAPIInstallTask.java | 8 +- .../hmcl/download/quilt/QuiltInstallTask.java | 10 +- .../hmcl/game/GameInstanceLibraryBuilder.java | 6 +- .../hmcl/game/JavaVersionConstraint.java | 12 +-- .../mcbbs/McbbsModpackLocalInstallTask.java | 8 +- .../modpack/multimc/MultiMCInstancePatch.java | 10 +- .../multimc/MultiMCModpackInstallTask.java | 20 ++-- 37 files changed, 309 insertions(+), 309 deletions(-) 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 3df542a9d0..ce23c7d740 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -391,13 +391,13 @@ public void onStop(boolean success, TaskExecutor executor) { executor.start(); } - private static Task checkGameState(HMCLGameRepository repository, GameSettings.Effective setting, GameInstanceManifest version) { - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version, repository.getGameVersion(version).orElse(null)); + private static Task checkGameState(HMCLGameRepository repository, GameSettings.Effective setting, GameInstanceManifest manifest) { + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(manifest, repository.getGameVersion(manifest).orElse(null)); GameVersionNumber gameVersion = GameVersionNumber.asGameVersion(analyzer.getVersion(LibraryAnalyzer.LibraryType.MINECRAFT)); Task getJavaTask = Task.supplyAsync(() -> { try { - return setting.getJava(gameVersion, version); + return setting.getJava(gameVersion, manifest); } catch (InterruptedException e) { throw new CancellationException(); } @@ -455,7 +455,7 @@ private static Task checkGameState(HMCLGameRepository repository, G } if (targetJavaVersion == null) - targetJavaVersion = version.javaVersion(); + targetJavaVersion = manifest.javaVersion(); } if (targetJavaVersion != null && supportedVersions.contains(targetJavaVersion)) { @@ -485,8 +485,8 @@ private static Task checkGameState(HMCLGameRepository repository, G if (java != null) { for (JavaVersionConstraint constraint : JavaVersionConstraint.ALL) { - if (constraint.appliesToVersion(gameVersion, version, java, analyzer)) { - if (!constraint.checkJava(gameVersion, version, java, analyzer)) { + if (constraint.appliesToVersion(gameVersion, manifest, java, analyzer)) { + if (!constraint.checkJava(gameVersion, manifest, java, analyzer)) { if (constraint.isMandatory()) { violatedMandatoryConstraints.add(constraint); } else { @@ -502,7 +502,7 @@ private static Task checkGameState(HMCLGameRepository repository, G Runnable breakAction = () -> future.completeExceptionally(new CancellationException("Launch operation was cancelled by user")); if (java == null || !violatedMandatoryConstraints.isEmpty()) { - JavaRuntime suggestedJava = JavaManager.findSuitableJava(gameVersion, version); + JavaRuntime suggestedJava = JavaManager.findSuitableJava(gameVersion, manifest); if (suggestedJava != null) { FXUtils.runInFX(() -> { Controllers.confirm(i18n("launch.advice.java.auto"), i18n("message.warning"), () -> { @@ -529,7 +529,7 @@ private static Task checkGameState(HMCLGameRepository repository, G ? GameJavaVersion.getCleanroomJavaVersion(cleanroomVersion) : GameJavaVersion.JAVA_21; } else if (violatedMandatoryConstraints.contains(JavaVersionConstraint.GAME_JSON)) - gameJavaVersion = version.javaVersion(); + gameJavaVersion = manifest.javaVersion(); else if (violatedMandatoryConstraints.contains(JavaVersionConstraint.VANILLA)) gameJavaVersion = GameJavaVersion.getMinimumJavaVersion(gameVersion); else @@ -655,14 +655,14 @@ else if (violatedMandatoryConstraints.contains(JavaVersionConstraint.VANILLA)) // Forge 2760~2773 will crash game with LiteLoader. boolean hasForge2760 = forgeVersion != null && (forgeVersion.compareTo("1.12.2-14.23.5.2760") >= 0) && (forgeVersion.compareTo("1.12.2-14.23.5.2773") < 0); - boolean hasLiteLoader = version.getLibraries().stream().anyMatch(it -> it.is("com.mumfrey", "liteloader")); + boolean hasLiteLoader = manifest.getLibraries().stream().anyMatch(it -> it.is("com.mumfrey", "liteloader")); if (hasForge2760 && hasLiteLoader && gameVersion.compareTo("1.12.2") == 0) { suggestions.add(i18n("launch.advice.forge2760_liteloader")); } // OptiFine 1.14.4 is not compatible with Forge 28.2.2 and later versions. boolean hasForge28_2_2 = forgeVersion != null && (forgeVersion.compareTo("1.14.4-28.2.2") >= 0); - boolean hasOptiFine = version.getLibraries().stream().anyMatch(it -> it.is("optifine", "OptiFine")); + boolean hasOptiFine = manifest.getLibraries().stream().anyMatch(it -> it.is("optifine", "OptiFine")); if (hasForge28_2_2 && hasOptiFine && gameVersion.compareTo("1.14.4") == 0) { suggestions.add(i18n("launch.advice.forge28_2_2_optifine")); } @@ -803,7 +803,7 @@ private final class HMCLProcessListener implements ProcessListener { private final ReentrantLock lock = new ReentrantLock(); private final HMCLGameRepository repository; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final LaunchOptions launchOptions; private ManagedProcess process; private volatile boolean lwjgl; @@ -815,9 +815,9 @@ private final class HMCLProcessListener implements ProcessListener { private Thread submitLogThread; private LinkedBlockingQueue logBuffer; - public HMCLProcessListener(HMCLGameRepository repository, GameInstanceManifest version, AuthInfo authInfo, LaunchOptions launchOptions, CountDownLatch launchingLatch, boolean detectWindow) { + public HMCLProcessListener(HMCLGameRepository repository, GameInstanceManifest manifest, AuthInfo authInfo, LaunchOptions launchOptions, CountDownLatch launchingLatch, boolean detectWindow) { this.repository = repository; - this.version = version; + this.manifest = manifest; this.launchOptions = launchOptions; this.launchingLatch = launchingLatch; this.detectWindow = detectWindow; @@ -1000,8 +1000,8 @@ public void onExit(int exitCode, ExitType exitType) { } if (exitType != ExitType.NORMAL) { - repository.markInstanceLaunchedAbnormally(version.id()); - runLater(() -> new GameCrashWindow(process, exitType, repository, version, launchOptions, logs).show()); + repository.markInstanceLaunchedAbnormally(manifest.id()); + runLater(() -> new GameCrashWindow(process, exitType, repository, manifest, launchOptions, logs).show()); } checkExit(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 278cc57108..96b33964e9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -315,13 +315,13 @@ private static JavaRuntime chooseJava(@Nullable JavaRuntime java1, JavaRuntime j } @Nullable - public static JavaRuntime findSuitableJava(GameVersionNumber gameVersion, GameInstanceManifest version) throws InterruptedException { - return findSuitableJava(getAllJava(), gameVersion, version); + public static JavaRuntime findSuitableJava(GameVersionNumber gameVersion, GameInstanceManifest manifest) throws InterruptedException { + return findSuitableJava(getAllJava(), gameVersion, manifest); } @Nullable - public static JavaRuntime findSuitableJava(Collection javaRuntimes, GameVersionNumber gameVersion, GameInstanceManifest version) { - LibraryAnalyzer analyzer = version != null ? LibraryAnalyzer.analyze(version, gameVersion != null ? gameVersion.toString() : null) : null; + public static JavaRuntime findSuitableJava(Collection javaRuntimes, GameVersionNumber gameVersion, GameInstanceManifest manifest) { + LibraryAnalyzer analyzer = manifest != null ? LibraryAnalyzer.analyze(manifest, gameVersion != null ? gameVersion.toString() : null) : null; boolean forceX86 = Architecture.SYSTEM_ARCH == Architecture.ARM64 && (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS || OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) @@ -342,8 +342,8 @@ public static JavaRuntime findSuitableJava(Collection javaRuntimes, boolean violationSuggested = false; for (JavaVersionConstraint constraint : JavaVersionConstraint.ALL) { - if (constraint.appliesToVersion(gameVersion, version, java, analyzer)) { - if (!constraint.checkJava(gameVersion, version, java, analyzer)) { + if (constraint.appliesToVersion(gameVersion, manifest, java, analyzer)) { + if (!constraint.checkJava(gameVersion, manifest, java, analyzer)) { if (constraint.isMandatory()) { violationMandatory = true; } else { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java index 287c468c19..20e7fa5f5b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java @@ -916,11 +916,11 @@ public void setJavaAutoSelected() { } /// Finds the effective Java runtime. - public @Nullable JavaRuntime getJava(@Nullable GameVersionNumber gameVersion, @Nullable GameInstanceManifest version) throws InterruptedException { + public @Nullable JavaRuntime getJava(@Nullable GameVersionNumber gameVersion, @Nullable GameInstanceManifest manifest) throws InterruptedException { JavaVersionType javaVersionType = getInheritable(GameSettings::javaTypeProperty); switch (javaVersionType) { case AUTO: - return JavaManager.findSuitableJava(gameVersion, version); + return JavaManager.findSuitableJava(gameVersion, manifest); case CUSTOM: try { return JavaManager.getJava(Path.of(getInheritable(GameSettings::customJavaPathProperty))); @@ -930,7 +930,7 @@ public void setJavaAutoSelected() { case VERSION: { String javaVersion = getInheritable(GameSettings::customJavaVersionProperty); if (StringUtils.isBlank(javaVersion)) { - return JavaManager.findSuitableJava(gameVersion, version); + return JavaManager.findSuitableJava(gameVersion, manifest); } int majorVersion = -1; @@ -948,13 +948,13 @@ public void setJavaAutoSelected() { Collection allJava = JavaManager.getAllJava().stream() .filter(it -> it.getParsedVersion() == finalMajorVersion) .collect(Collectors.toList()); - return JavaManager.findSuitableJava(allJava, gameVersion, version); + return JavaManager.findSuitableJava(allJava, gameVersion, manifest); } case DETECTED: { DetectedJava detectedJava = getInheritable(GameSettings::detectedJavaProperty); String javaVersion = detectedJava.version(); if (StringUtils.isBlank(javaVersion)) { - return JavaManager.findSuitableJava(gameVersion, version); + return JavaManager.findSuitableJava(gameVersion, manifest); } if (StringUtils.isNotBlank(detectedJava.pathHash())) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java index c443ca564b..5037480bfe 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/GameCrashWindow.java @@ -73,7 +73,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class GameCrashWindow extends Stage { - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final String memory; private final String total_memory; private final String java; @@ -91,16 +91,16 @@ public class GameCrashWindow extends Stage { private final List logs; - public GameCrashWindow(ManagedProcess managedProcess, ProcessListener.ExitType exitType, DefaultGameRepository repository, GameInstanceManifest version, LaunchOptions launchOptions, List logs) { + public GameCrashWindow(ManagedProcess managedProcess, ProcessListener.ExitType exitType, DefaultGameRepository repository, GameInstanceManifest manifest, LaunchOptions launchOptions, List logs) { Themes.applyNativeDarkMode(this); this.managedProcess = managedProcess; this.exitType = exitType; this.repository = repository; - this.version = version; + this.manifest = manifest; this.launchOptions = launchOptions; this.logs = logs; - this.analyzer = LibraryAnalyzer.analyze(version, repository.getGameVersion(version).orElse(null)); + this.analyzer = LibraryAnalyzer.analyze(manifest, repository.getGameVersion(manifest).orElse(null)); memory = Optional.ofNullable(launchOptions.getMaxMemory()).map(i -> i + " " + i18n("settings.memory.unit.mib")).orElse("-"); @@ -142,7 +142,7 @@ private void analyzeCrashReport() { return pair(CrashReportAnalyzer.analyze(rawLog), crashReport != null ? CrashReportAnalyzer.findKeywordsFromCrashReport(crashReport) : new HashSet<>()); }), Task.supplyAsync(() -> { - Path latestLog = repository.getRunDirectory(version.id()).resolve("logs/latest.log"); + Path latestLog = repository.getRunDirectory(manifest.id()).resolve("logs/latest.log"); if (!Files.isReadable(latestLog)) { return pair(new HashSet(), new HashSet()); } @@ -345,7 +345,7 @@ private final class View extends VBox { TwoLineListItem version = new TwoLineListItem(); version.getStyleClass().setAll("two-line-item-second-large"); version.setTitle(i18n("game.version")); - version.setSubtitle(GameCrashWindow.this.version.id().toString()); + version.setSubtitle(GameCrashWindow.this.manifest.id().toString()); TwoLineListItem total_memory = new TwoLineListItem(); total_memory.getStyleClass().setAll("two-line-item-second-large"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java index 6575f93d91..d46bfcc5ed 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java @@ -39,15 +39,15 @@ class AdditionalInstallersPage extends AbstractInstallersPage { protected final BooleanProperty compatible = new SimpleBooleanProperty(); protected final HMCLGameRepository repository; protected final String gameVersion; - protected final GameInstanceManifest version; + protected final GameInstanceManifest manifest; - public AdditionalInstallersPage(String gameVersion, GameInstanceManifest version, WizardController controller, HMCLGameRepository repository, DownloadProvider downloadProvider) { + public AdditionalInstallersPage(String gameVersion, GameInstanceManifest manifest, WizardController controller, HMCLGameRepository repository, DownloadProvider downloadProvider) { super(controller, gameVersion, downloadProvider); this.gameVersion = gameVersion; - this.version = version; + this.manifest = manifest; this.repository = repository; - txtName.setText(version.id().toString()); + txtName.setText(manifest.id().toString()); txtName.setEditable(false); for (InstallerItem library : group.getLibraries()) { @@ -80,7 +80,7 @@ private String getVersion(String id) { @Override protected void reload() { - GameInstanceManifest standalone = repository.resolve(version).standaloneManifest(); + GameInstanceManifest standalone = repository.resolve(manifest).standaloneManifest(); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(standalone, repository.getGameVersion(standalone).orElse(null)); String game = analyzer.getVersion(MINECRAFT).orElse(null); String currentGameVersion = Lang.nonNull(getVersion("game"), game); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java index 4566f0a3ac..00d6c06465 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/UpdateInstallerWizardProvider.java @@ -49,15 +49,15 @@ public final class UpdateInstallerWizardProvider implements WizardProvider { private final HMCLGameRepository repository; private final DefaultDependencyManager dependencyManager; private final String gameVersion; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final String libraryId; private final String oldLibraryVersion; private final DownloadProvider downloadProvider; - public UpdateInstallerWizardProvider(@NotNull HMCLGameRepository repository, @NotNull String gameVersion, @NotNull GameInstanceManifest version, @NotNull String libraryId, @Nullable String oldLibraryVersion) { + public UpdateInstallerWizardProvider(@NotNull HMCLGameRepository repository, @NotNull String gameVersion, @NotNull GameInstanceManifest manifest, @NotNull String libraryId, @Nullable String oldLibraryVersion) { this.repository = repository; this.gameVersion = gameVersion; - this.version = version; + this.manifest = manifest; this.libraryId = libraryId; this.oldLibraryVersion = oldLibraryVersion; this.downloadProvider = DownloadProviders.getDownloadProvider(); @@ -76,7 +76,7 @@ public Object finish(SettingsMap settings) { // We remove library but not save it, // so if installation failed will not break down current version. - Task ret = Task.supplyAsync(() -> version); + Task ret = Task.supplyAsync(() -> manifest); var hints = new ArrayList(); for (Object value : settings.asStringMap().values()) { if (value instanceof RemoteVersion remoteVersion) { @@ -103,7 +103,7 @@ public Node createPage(WizardController controller, int step, SettingsMap settin controller.onFinish(); } else if ("game".equals(libraryId)) { String newGameVersion = ((RemoteVersion) settings.get(libraryId)).getSelfVersion(); - controller.onNext(new AdditionalInstallersPage(newGameVersion, version, controller, repository, downloadProvider)); + controller.onNext(new AdditionalInstallersPage(newGameVersion, manifest, controller, repository, downloadProvider)); } else { Controllers.confirm(i18n("install.change_version.confirm", i18n("install.installer." + libraryId), oldLibraryVersion, ((RemoteVersion) settings.get(libraryId)).getSelfVersion()), i18n("install.change_version"), controller::onFinish, controller::onCancel); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java index 7358590e49..26a2fe263d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java @@ -2622,17 +2622,17 @@ private void initJavaSubtitle() { if (JavaManager.isInitialized()) { GameVersionNumber gameVersionNumber = this.currentGameVersionNumber.get(); - GameInstanceManifest version; + GameInstanceManifest manifest; if (this.instanceId == null) { - version = null; + manifest = null; } else { - version = repository != null && loadedInstanceId != null ? repository.getResolvedInstanceManifest(loadedInstanceId).launchManifest() : null; + manifest = repository != null && loadedInstanceId != null ? repository.getResolvedInstanceManifest(loadedInstanceId).launchManifest() : null; } try { JavaRuntime java = effectiveSetting != null - ? effectiveSetting.getJava(gameVersionNumber, version) - : resolveEffectiveSetting(setting).getJava(gameVersionNumber, version); + ? effectiveSetting.getJava(gameVersionNumber, manifest) + : resolveEffectiveSetting(setting).getJava(gameVersionNumber, manifest); if (java != null) { javaSublist.setDescription(java.getBinary().toString()); } else { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java index fd49f4a4ae..528cc93814 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java @@ -47,7 +47,7 @@ public class InstallerListPage extends ListPageBase implements GameInstancePage.GameInstanceLoadable { private HMCLGameRepository repository; private GameInstanceID instanceId; - private GameInstanceManifest version; + private GameInstanceManifest manifest; private String gameVersion; { @@ -66,11 +66,11 @@ protected Skin createDefaultSkin() { public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID instanceId) { this.repository = repository; this.instanceId = instanceId; - this.version = repository.getInstanceManifest(instanceId); + this.manifest = repository.getInstanceManifest(instanceId); this.gameVersion = null; CompletableFuture.supplyAsync(() -> { - gameVersion = repository.getGameVersion(version).orElse(null); + gameVersion = repository.getGameVersion(manifest).orElse(null); return LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); }).thenAcceptAsync(analyzer -> { @@ -100,10 +100,10 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID } item.setOnInstall(() -> { - Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(repository, gameVersion, version, libraryId, libraryVersion)); + Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(repository, gameVersion, manifest, libraryId, libraryVersion)); }); - item.setOnRemove(() -> repository.getDependency().removeLibraryAsync(version, libraryId) + item.setOnRemove(() -> repository.getDependency().removeLibraryAsync(manifest, libraryId) .thenComposeAsync(repository::saveAsync) .withComposeAsync(repository.refreshAsync()) .withRunAsync(Schedulers.javafx(), () -> loadInstance(this.repository, this.instanceId)) @@ -125,7 +125,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID InstallerItem installerItem = new InstallerItem(libraryId, InstallerItem.Style.LIST_ITEM); installerItem.versionProperty().set(new InstallerItem.InstalledState(libraryVersion, false, false)); - installerItem.setOnRemove(() -> repository.getDependency().removeLibraryAsync(version, libraryId) + installerItem.setOnRemove(() -> repository.getDependency().removeLibraryAsync(manifest, libraryId) .thenComposeAsync(repository::saveAsync) .withComposeAsync(repository.refreshAsync()) .withRunAsync(Schedulers.javafx(), () -> loadInstance(this.repository, this.instanceId)) @@ -144,7 +144,7 @@ public void installOffline() { } private void doInstallOffline(Path file) { - Task task = repository.getDependency().installLibraryAsync(version, file) + Task task = repository.getDependency().installLibraryAsync(manifest, file) .thenComposeAsync(repository::saveAsync) .thenComposeAsync(repository.refreshAsync()); task.setName(i18n("install.installer.install_offline")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java index a5c005052d..d2bf00095f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java @@ -169,10 +169,10 @@ public static void openFolder(HMCLGameRepository repository, GameInstanceID inst } public static void installFromJson(HMCLGameRepository repository, Path file) { - GameInstanceManifest version; + GameInstanceManifest manifest; try { - version = JsonUtils.fromJsonFile(file, GameInstanceManifest.class); - if (version == null) { + manifest = JsonUtils.fromJsonFile(file, GameInstanceManifest.class); + if (manifest == null) { throw new IllegalArgumentException("Missing game manifest"); } } catch (Exception e) { @@ -186,7 +186,7 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { GameInstanceID instanceId = new GameInstanceID(result); DefaultDependencyManager dependencyManager = repository.getDependency(); - GameInstanceManifest newVersion = version.withId(instanceId).withJar(instanceId); + GameInstanceManifest newVersion = manifest.withId(instanceId).withJar(instanceId); Controllers.taskDialog( Task.allOf(new GameDownloadTask(dependencyManager, null, newVersion), diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java index 723622c28e..bf6ecc8e13 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java @@ -64,8 +64,8 @@ private static Map getNatives(Platform platform) { } // https://github.com/LWJGL/lwjgl3/issues/1111 - public static boolean needPatchMemoryUtil(GameInstanceManifest version, int javaVersion) { - return javaVersion >= 25 && javaVersion <= 26 && version.getLibraries().stream().anyMatch(library -> + public static boolean needPatchMemoryUtil(GameInstanceManifest manifest, int javaVersion) { + return javaVersion >= 25 && javaVersion <= 26 && manifest.getLibraries().stream().anyMatch(library -> "org.lwjgl".equals(library.getGroupId()) && "lwjgl".equals(library.getArtifactId()) && "3.4.1".equals(library.getVersion()) @@ -74,16 +74,16 @@ public static boolean needPatchMemoryUtil(GameInstanceManifest version, int java } public static GameInstanceManifest patchNative(DefaultGameRepository repository, - GameInstanceManifest version, String gameVersion, + GameInstanceManifest manifest, String gameVersion, JavaRuntime javaVersion, GameSettings.Effective settings, List javaArguments) { if (settings.get(GameSettings::useCustomNativesProperty)) { if (gameVersion != null && GameVersionNumber.compare(gameVersion, "1.19") < 0) - return version; + return manifest; ArrayList newLibraries = new ArrayList<>(); - for (Library library : version.getLibraries()) { + for (Library library : manifest.getLibraries()) { if (!library.appliesToCurrentEnvironment()) continue; @@ -93,7 +93,7 @@ public static GameInstanceManifest patchNative(DefaultGameRepository repository, newLibraries.add(library); } } - return version.withLibraries(newLibraries); + return manifest.withLibraries(newLibraries); } final boolean useNativeGLFW = settings.get(GameSettings::useNativeGLFWProperty); @@ -102,7 +102,7 @@ public static GameInstanceManifest patchNative(DefaultGameRepository repository, if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && (useNativeGLFW || useNativeOpenAL) && gameVersion != null && GameVersionNumber.compare(gameVersion, "1.19") >= 0) { - version = version.withLibraries(version.getLibraries().stream() + manifest = manifest.withLibraries(manifest.getLibraries().stream() .filter(library -> { if (library.getClassifier() != null && library.getClassifier().startsWith("natives") && "org.lwjgl".equals(library.getGroupId())) { @@ -125,25 +125,25 @@ public static GameInstanceManifest patchNative(DefaultGameRepository repository, GameVersionNumber gameVersionNumber = gameVersion != null ? GameVersionNumber.asGameVersion(gameVersion) : null; if (settings.get(GameSettings::notPatchNativesProperty)) - return version; + return manifest; if (arch.isX86() && (os == OperatingSystem.WINDOWS || os == OperatingSystem.LINUX || os == OperatingSystem.MACOS)) - return version; + return manifest; if (arch == Architecture.ARM64 && (os == OperatingSystem.MACOS || os == OperatingSystem.WINDOWS) && gameVersionNumber != null && gameVersionNumber.compareTo("1.19") >= 0) - return version; + return manifest; Map replacements = getNatives(javaVersion.getPlatform()); if (replacements.isEmpty()) { LOG.warning("No alternative native library provided for platform " + javaVersion.getPlatform()); - return version; + return manifest; } boolean lwjglVersionChanged = false; ArrayList newLibraries = new ArrayList<>(); - for (Library library : version.getLibraries()) { + for (Library library : manifest.getLibraries()) { if (!library.appliesToCurrentEnvironment()) continue; @@ -172,7 +172,7 @@ public static GameInstanceManifest patchNative(DefaultGameRepository repository, } if (lwjglVersionChanged) { - ModManager modManager = repository.getModManager(version.id()); + ModManager modManager = repository.getModManager(manifest.id()); try { for (LocalModFile mod : modManager.getLocalFiles()) { if ("sodium".equals(mod.getId())) { @@ -186,7 +186,7 @@ public static GameInstanceManifest patchNative(DefaultGameRepository repository, } } - return version.withLibraries(newLibraries); + return manifest.withLibraries(newLibraries); } /// @see Java Mesa Loader for Windows 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 dfbd12aa37..d780f76b92 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -79,35 +79,35 @@ public GameBuilder newGameBuilder() { } @Override - public Task checkGameCompletionAsync(GameInstanceManifest version, boolean integrityCheck) { + public Task checkGameCompletionAsync(GameInstanceManifest manifest, boolean integrityCheck) { return Task.allOf( Task.composeAsync(() -> { - Path versionJar = repository.getInstanceJar(version); + Path versionJar = repository.getInstanceJar(manifest); return Files.notExists(versionJar) || FileUtils.size(versionJar) == 0L - ? new GameDownloadTask(this, null, version) + ? new GameDownloadTask(this, null, manifest) : null; - }).thenComposeAsync(checkPatchCompletionAsync(version, integrityCheck)), - new GameAssetDownloadTask(this, version, GameAssetDownloadTask.DOWNLOAD_INDEX_IF_NECESSARY, integrityCheck) + }).thenComposeAsync(checkPatchCompletionAsync(manifest, integrityCheck)), + new GameAssetDownloadTask(this, manifest, GameAssetDownloadTask.DOWNLOAD_INDEX_IF_NECESSARY, integrityCheck) .setSignificance(Task.TaskSignificance.MODERATE), - new GameLibrariesTask(this, version, integrityCheck) + new GameLibrariesTask(this, manifest, integrityCheck) ); } @Override - public Task checkLibraryCompletionAsync(GameInstanceManifest version, boolean integrityCheck) { - return new GameLibrariesTask(this, version, integrityCheck, version.getLibraries()); + public Task checkLibraryCompletionAsync(GameInstanceManifest manifest, boolean integrityCheck) { + return new GameLibrariesTask(this, manifest, integrityCheck, manifest.getLibraries()); } @Override - public Task checkPatchCompletionAsync(GameInstanceManifest version, boolean integrityCheck) { + public Task checkPatchCompletionAsync(GameInstanceManifest manifest, boolean integrityCheck) { return Task.composeAsync(() -> { List> tasks = new ArrayList<>(0); - String gameVersion = repository.getGameVersion(version).orElse(null); + String gameVersion = repository.getGameVersion(manifest).orElse(null); if (gameVersion == null) return null; - GameInstanceManifest original = repository.getInstanceManifest(version.id()); + GameInstanceManifest original = repository.getInstanceManifest(manifest.id()); GameInstanceManifest standalone = repository.resolve(original).standaloneManifest(); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(standalone, gameVersion); @@ -127,17 +127,17 @@ public Task checkPatchCompletionAsync(GameInstanceManifest version, boolean i .map(GameInstancePatch::getVersion) .orElse(null)); - boolean needsReInstallation = version.getLibraries().stream() + boolean needsReInstallation = manifest.getLibraries().stream() .anyMatch(library -> !library.hasDownloadURL() && "optifine".equals(library.getGroupId()) - && GameLibrariesTask.shouldDownloadLibrary(repository, version, library, integrityCheck)); + && GameLibrariesTask.shouldDownloadLibrary(repository, manifest, library, integrityCheck)); if (needsReInstallation) { Library installer = new Library(new Artifact("optifine", "OptiFine", gameVersion + "_" + optifinePatchVersion, "installer")); - if (GameLibrariesTask.shouldDownloadLibrary(repository, version, installer, integrityCheck)) { + if (GameLibrariesTask.shouldDownloadLibrary(repository, manifest, installer, integrityCheck)) { tasks.add(installLibraryAsync(gameVersion, original, "optifine", optifinePatchVersion)); } else { - tasks.add(OptiFineInstallTask.install(this, original, repository.getLibraryFile(version, installer))); + tasks.add(OptiFineInstallTask.install(this, original, repository.getLibraryFile(manifest, installer))); } } } @@ -210,16 +210,16 @@ public static class UnsupportedLibraryInstallerException extends Exception { * Remove installed library. * Will try to remove libraries and patches. * - * @param version not resolved version + * @param manifest not resolved instance manifest * @param libraryId forge/liteloader/optifine/fabric * @return task to remove the specified library */ - public Task removeLibraryAsync(GameInstanceManifest version, String libraryId) { + public Task removeLibraryAsync(GameInstanceManifest manifest, String libraryId) { // MaintainTask requires version that does not inherits from any version. // If we want to remove a library in dependent version, we should keep the dependents not changed // So resolving this game version to preserve all information in this version.json is necessary. return Task.supplyAsync(() -> { - GameInstanceManifest independentVersion = repository.resolve(version).standaloneManifest(); + GameInstanceManifest independentVersion = repository.resolve(manifest).standaloneManifest(); String gameVersion = repository.getGameVersion(independentVersion).orElse(null); return LibraryAnalyzer.analyze(independentVersion, gameVersion).removeLibrary(libraryId).build(); }); 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 87e00bb1a7..16a6f43263 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java @@ -46,7 +46,7 @@ public interface DependencyManager { * * @return the task to check game completion. */ - Task checkGameCompletionAsync(GameInstanceManifest version, boolean integrityCheck); + Task checkGameCompletionAsync(GameInstanceManifest manifest, boolean integrityCheck); /** * Check if libraries of this version in complete. @@ -54,17 +54,17 @@ public interface DependencyManager { * * @return the task to check game completion. */ - Task checkLibraryCompletionAsync(GameInstanceManifest version, boolean integrityCheck); + Task checkLibraryCompletionAsync(GameInstanceManifest manifest, 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 manifest the version to be checked * @param integrityCheck check if some libraries are corrupt. * @return the task to check patches completion. */ - Task checkPatchCompletionAsync(GameInstanceManifest version, boolean integrityCheck); + Task checkPatchCompletionAsync(GameInstanceManifest manifest, 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 65817a0819..de58d4697f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -34,11 +34,11 @@ import static org.jackhuang.hmcl.util.Pair.pair; public final class LibraryAnalyzer implements Iterable { - private GameInstanceManifest version; + private GameInstanceManifest manifest; private final Map> libraries; - private LibraryAnalyzer(GameInstanceManifest version, Map> libraries) { - this.version = version; + private LibraryAnalyzer(GameInstanceManifest manifest, Map> libraries) { + this.manifest = manifest; this.libraries = libraries; } @@ -60,7 +60,7 @@ public Optional getLibrary(LibraryType type) { * 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; + return manifest.hasPatch(type) ? LibraryMark.LibraryStatus.CLEAR : LibraryMark.LibraryStatus.JUST_EXISTED; } @NotNull @@ -97,17 +97,17 @@ public boolean hasModLoader() { } public boolean hasModLauncher() { - return LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(version.mainClass()) || version.getPatches().stream().anyMatch( + return LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(manifest.mainClass()) || manifest.getPatches().stream().anyMatch( patch -> LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(patch.getMainClass()) ); } - private GameInstanceManifest removingMatchedLibrary(GameInstanceManifest version, String libraryId) { + private GameInstanceManifest removingMatchedLibrary(GameInstanceManifest manifest, String libraryId) { LibraryType type = LibraryType.fromPatchId(libraryId); - if (type == null) return version; + if (type == null) return manifest; List libraries = new ArrayList<>(); - List rawLibraries = version.getLibraries(); + List rawLibraries = manifest.getLibraries(); for (Library library : rawLibraries) { if (type.matchLibrary(library, rawLibraries)) { // skip @@ -115,7 +115,7 @@ private GameInstanceManifest removingMatchedLibrary(GameInstanceManifest version libraries.add(library); } } - return version.withLibraries(libraries); + return manifest.withLibraries(libraries); } private GameInstancePatch removingMatchedLibrary(GameInstancePatch patch, String libraryId) { @@ -142,8 +142,8 @@ private GameInstancePatch removingMatchedLibrary(GameInstancePatch patch, String */ public LibraryAnalyzer removeLibrary(String libraryId) { if (!has(libraryId)) return this; - GameInstanceManifest manifest = removingMatchedLibrary(version, libraryId); - version = manifest.withPatches(version.getPatches().stream() + GameInstanceManifest manifest = removingMatchedLibrary(this.manifest, libraryId); + this.manifest = manifest.withPatches(this.manifest.getPatches().stream() .filter(patch -> !libraryId.equals(patch.getId())) .map(patch -> removingMatchedLibrary(patch, libraryId)) .collect(Collectors.toList())); @@ -151,7 +151,7 @@ public LibraryAnalyzer removeLibrary(String libraryId) { } public GameInstanceManifest build() { - return version; + return manifest; } public static LibraryAnalyzer analyze(GameInstanceManifest manifest, String gameVersion) { @@ -182,10 +182,10 @@ public static LibraryAnalyzer analyze(GameInstanceManifest manifest, String game return new LibraryAnalyzer(manifest, libraries); } - public static boolean isModded(GameRepository provider, GameInstanceManifest version) { + public static boolean isModded(GameRepository provider, GameInstanceManifest manifest) { GameInstanceManifest resolvedVersion; try { - resolvedVersion = version.resolve(provider); + resolvedVersion = manifest.resolve(provider); } catch (NoSuchGameInstanceException e) { return false; } 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 159665a2f2..f9d2ec6951 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -40,42 +40,42 @@ public class MaintainTask extends Task { private final GameRepository repository; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; - public MaintainTask(GameRepository repository, GameInstanceManifest version) { + public MaintainTask(GameRepository repository, GameInstanceManifest manifest) { this.repository = repository; - this.version = version; + this.manifest = manifest; - if (version.inheritsFrom() != null) + if (manifest.inheritsFrom() != null) throw new IllegalArgumentException("MaintainTask requires independent game version"); } @Override public void execute() { - setResult(maintain(repository, version)); + setResult(maintain(repository, manifest)); } - public static GameInstanceManifest maintain(GameRepository repository, GameInstanceManifest version) { - if (version.inheritsFrom() != null) + public static GameInstanceManifest maintain(GameRepository repository, GameInstanceManifest manifest) { + if (manifest.inheritsFrom() != null) throw new IllegalArgumentException("MaintainTask requires independent game version"); - String mainClass = version.resolve(repository).mainClass(); + String mainClass = manifest.resolve(repository).mainClass(); if (mainClass != null && mainClass.equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN)) { - version = maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(repository, unique(version), true), false); + manifest = maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(repository, unique(manifest), true), false); } else if (mainClass != null && mainClass.equals(LibraryAnalyzer.MOD_LAUNCHER_MAIN)) { // Forge 1.13 and OptiFine - version = maintainOptiFineLibrary(repository, maintainGameWithCpwModLauncher(repository, unique(version)), true); + manifest = maintainOptiFineLibrary(repository, maintainGameWithCpwModLauncher(repository, unique(manifest)), true); } else if (mainClass != null && mainClass.equals(LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN)) { // Forge 1.17 - version = maintainGameWithCpwBoostrapLauncher(repository, unique(version)); + manifest = maintainGameWithCpwBoostrapLauncher(repository, unique(manifest)); } else { // Vanilla Minecraft does not need maintain // Fabric does not need maintain, nothing compatible with fabric now. - version = maintainOptiFineLibrary(repository, unique(version), false); + manifest = maintainOptiFineLibrary(repository, unique(manifest), false); } - List libraries = version.getLibraries(); + List libraries = manifest.getLibraries(); if (!libraries.isEmpty()) { // HMCL once use log4j-patch to prevent virus. But now, we only modify log4j2.xml. // Therefore, we remove this library. @@ -84,23 +84,23 @@ public static GameInstanceManifest maintain(GameRepository repository, GameInsta && ("log4j-patch".equals(library.getArtifactId()) || "log4j-patch-beta9".equals(library.getArtifactId())) && "1.0".equals(library.getVersion()) && library.getDownload() == null) { - version = version.withLibraries(libraries.subList(1, libraries.size())); + manifest = manifest.withLibraries(libraries.subList(1, libraries.size())); } } - return version; + return manifest; } - public static GameInstanceManifest maintainPreservingPatches(GameRepository repository, GameInstanceManifest version) { - if (!version.isResolvedPreservingPatches()) + public static GameInstanceManifest maintainPreservingPatches(GameRepository repository, GameInstanceManifest manifest) { + if (!manifest.isResolvedPreservingPatches()) throw new IllegalArgumentException("MaintainTask requires independent game version"); - GameInstanceManifest newVersion = maintain(repository, version.resolve(repository)); - return newVersion.withPatches(version.getPatches()); + GameInstanceManifest newVersion = maintain(repository, manifest.resolve(repository)); + return newVersion.withPatches(manifest.getPatches()); } - private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository repository, GameInstanceManifest version, boolean reorderTweakClass) { - LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); - GameInstanceLibraryBuilder builder = new GameInstanceLibraryBuilder(version); + private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository repository, GameInstanceManifest manifest, boolean reorderTweakClass) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(manifest, null); + GameInstanceLibraryBuilder builder = new GameInstanceLibraryBuilder(manifest); String mainClass = null; // Installing Forge will override the Minecraft arguments in json, so LiteLoader and OptiFine Tweaker are being re-added. @@ -147,20 +147,20 @@ private static GameInstanceManifest maintainGameWithLaunchWrapper(GameRepository return mainClass == null ? ret : ret.withMainClass(mainClass); } - private static GameInstanceManifest maintainGameWithCpwModLauncher(GameRepository repository, GameInstanceManifest version) { - LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); - GameInstanceLibraryBuilder builder = new GameInstanceLibraryBuilder(version); + private static GameInstanceManifest maintainGameWithCpwModLauncher(GameRepository repository, GameInstanceManifest manifest) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(manifest, null); + GameInstanceLibraryBuilder builder = new GameInstanceLibraryBuilder(manifest); - if (!libraryAnalyzer.has(FORGE)) return version; + if (!libraryAnalyzer.has(FORGE)) return manifest; if (libraryAnalyzer.has(OPTIFINE)) { Library hmclTransformerDiscoveryService = new Library(new Artifact("org.jackhuang.hmcl", "transformer-discovery-service", "1.0")); - Optional optiFine = version.getLibraries().stream().filter(library -> library.is("optifine", "OptiFine")).findAny(); - boolean libraryExisting = version.getLibraries().stream().anyMatch(library -> library.is("org.jackhuang.hmcl", "transformer-discovery-service")); + Optional optiFine = manifest.getLibraries().stream().filter(library -> library.is("optifine", "OptiFine")).findAny(); + boolean libraryExisting = manifest.getLibraries().stream().anyMatch(library -> library.is("org.jackhuang.hmcl", "transformer-discovery-service")); optiFine.ifPresent(library -> { builder.addJvmArgument("-Dhmcl.transformer.candidates=${library_directory}/" + library.getPath()); if (!libraryExisting) builder.addLibrary(hmclTransformerDiscoveryService); - Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService); + Path libraryPath = repository.getLibraryFile(manifest, hmclTransformerDiscoveryService); try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar")) { Files.createDirectories(libraryPath.getParent()); Files.copy(Objects.requireNonNull(input, "Bundled HMCLTransformerDiscoveryService is missing."), libraryPath, StandardCopyOption.REPLACE_EXISTING); @@ -173,7 +173,7 @@ private static GameInstanceManifest maintainGameWithCpwModLauncher(GameRepositor return builder.build(); } - private static String updateIgnoreList(GameRepository repository, GameInstanceManifest version, String ignoreList) { + private static String updateIgnoreList(GameRepository repository, GameInstanceManifest manifest, String ignoreList) { String[] ignores = ignoreList.split(","); List newIgnoreList = new ArrayList<>(); @@ -181,12 +181,12 @@ private static String updateIgnoreList(GameRepository repository, GameInstanceMa // we need to manually ignore ${primary_jar}. newIgnoreList.add("${primary_jar}"); - Path libraryDirectory = repository.getLibrariesDirectory(version).toAbsolutePath().normalize(); + Path libraryDirectory = repository.getLibrariesDirectory(manifest).toAbsolutePath().normalize(); // The default ignoreList is too loose and may cause some problems, we replace them with the absolute version. // For example, if "client-extra" is in ignoreList, and game directory contains "client-extra" component, all // libraries will be ignored, which is not expected. - for (String classpathName : repository.getClasspath(version)) { + for (String classpathName : repository.getClasspath(manifest)) { Path classpathFile = Paths.get(classpathName).toAbsolutePath(); String fileName = classpathFile.getFileName().toString(); if (Stream.of(ignores).anyMatch(fileName::contains)) { @@ -205,11 +205,11 @@ private static String updateIgnoreList(GameRepository repository, GameInstanceMa } // Fix wrong configurations when launching 1.17+ with Forge. - private static GameInstanceManifest maintainGameWithCpwBoostrapLauncher(GameRepository repository, GameInstanceManifest version) { - LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); - GameInstanceLibraryBuilder builder = new GameInstanceLibraryBuilder(version); + private static GameInstanceManifest maintainGameWithCpwBoostrapLauncher(GameRepository repository, GameInstanceManifest manifest) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(manifest, null); + GameInstanceLibraryBuilder builder = new GameInstanceLibraryBuilder(manifest); - if (!libraryAnalyzer.has(FORGE) && !libraryAnalyzer.has(NEO_FORGE)) return version; + if (!libraryAnalyzer.has(FORGE) && !libraryAnalyzer.has(NEO_FORGE)) return manifest; Optional bslVersion = libraryAnalyzer.getVersion(BOOTSTRAP_LAUNCHER); @@ -224,7 +224,7 @@ private static GameInstanceManifest maintainGameWithCpwBoostrapLauncher(GameRepo if (jvmArg instanceof StringArgument) { String jvmArgStr = jvmArg.toString(); if (jvmArgStr.startsWith("-DignoreList=")) { - jvm.set(i, new StringArgument("-DignoreList=" + updateIgnoreList(repository, version, jvmArgStr.substring("-DignoreList=".length())))); + jvm.set(i, new StringArgument("-DignoreList=" + updateIgnoreList(repository, manifest, jvmArgStr.substring("-DignoreList=".length())))); } } } @@ -247,20 +247,20 @@ private static GameInstanceManifest maintainGameWithCpwBoostrapLauncher(GameRepo return builder.build(); } - private static GameInstanceManifest maintainOptiFineLibrary(GameRepository repository, GameInstanceManifest version, boolean remove) { - LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version, null); - List libraries = new ArrayList<>(version.getLibraries()); + private static GameInstanceManifest maintainOptiFineLibrary(GameRepository repository, GameInstanceManifest manifest, boolean remove) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(manifest, null); + List libraries = new ArrayList<>(manifest.getLibraries()); if (libraryAnalyzer.has(OPTIFINE)) { if (libraryAnalyzer.has(LITELOADER) || libraryAnalyzer.has(FORGE)) { // If forge or LiteLoader installed, OptiFine Forge Tweaker is needed. // And we should load the installer jar instead of patch jar. if (repository != null) { - for (int i = 0; i < version.getLibraries().size(); ++i) { + for (int i = 0; i < manifest.getLibraries().size(); ++i) { Library library = libraries.get(i); if (library.is("optifine", "OptiFine")) { Library newLibrary = new Library(new Artifact("optifine", "OptiFine", library.getVersion(), "installer")); - if (Files.exists(repository.getLibraryFile(version, newLibrary))) { + if (Files.exists(repository.getLibraryFile(manifest, newLibrary))) { libraries.set(i, null); // OptiFine should be loaded after Forge in classpath. // Although we have altered priority of OptiFine higher than Forge, @@ -281,15 +281,15 @@ private static GameInstanceManifest maintainOptiFineLibrary(GameRepository repos } } - return version.withLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList())); + return manifest.withLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList())); } - public static GameInstanceManifest unique(GameInstanceManifest version) { + public static GameInstanceManifest unique(GameInstanceManifest manifest) { List libraries = new ArrayList<>(); SimpleMultimap> multimap = new SimpleMultimap<>(HashMap::new, ArrayList::new); - for (Library library : version.getLibraries()) { + for (Library library : manifest.getLibraries()) { String id = library.getGroupId() + ":" + library.getArtifactId(); VersionNumber number = VersionNumber.asVersion(library.getVersion()); String serialized = JsonUtils.GSON.toJson(library); @@ -336,6 +336,6 @@ public static GameInstanceManifest unique(GameInstanceManifest version) { } } - return version.withLibraries(libraries); + return manifest.withLibraries(libraries); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java index b14369e74b..a7d7ff8273 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/cleanroom/CleanroomInstallTask.java @@ -42,24 +42,24 @@ public final class CleanroomInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final CleanroomRemoteVersion remote; private Path installer; private FileDownloadTask dependent; private Task task; private String selfVersion; - public CleanroomInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, CleanroomRemoteVersion remoteVersion) { + public CleanroomInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, CleanroomRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; setSignificance(TaskSignificance.MODERATE); } - public CleanroomInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, String selfVersion, Path installer) { + public CleanroomInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, String selfVersion, Path installer) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.selfVersion = selfVersion; this.remote = null; this.installer = installer; @@ -113,14 +113,14 @@ public Collection> getDependencies() { @Override public void execute() throws IOException, VersionMismatchException, UnsupportedInstallationException { if (selfVersion == null) { - task = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer).thenApplyAsync((version) -> version.withId(LibraryAnalyzer.LibraryType.CLEANROOM.getPatchId())); + task = new ForgeNewInstallTask(dependencyManager, manifest, remote.getSelfVersion(), installer).thenApplyAsync((version) -> version.withId(LibraryAnalyzer.LibraryType.CLEANROOM.getPatchId())); } else { - task = new ForgeNewInstallTask(dependencyManager, version, selfVersion, installer).thenApplyAsync((version) -> version.withId(LibraryAnalyzer.LibraryType.CLEANROOM.getPatchId())); + task = new ForgeNewInstallTask(dependencyManager, manifest, selfVersion, installer).thenApplyAsync((version) -> version.withId(LibraryAnalyzer.LibraryType.CLEANROOM.getPatchId())); } } - public static Task install(DefaultDependencyManager dependencyManager, GameInstanceManifest version, Path installer) throws IOException, VersionMismatchException { - Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); + public static Task install(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, Path installer) throws IOException, VersionMismatchException { + Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(manifest); if (gameVersion.isEmpty()) throw new IOException(); try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { String installProfileText = Files.readString(fs.getPath("install_profile.json")); @@ -129,7 +129,7 @@ public static Task install(DefaultDependencyManager dependenc ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class); if (!gameVersion.get().equals(profile.getMinecraft())) throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get()); - return new CleanroomInstallTask(dependencyManager, version, modifyVersion(profile.getVersion()), installer); + return new CleanroomInstallTask(dependencyManager, manifest, modifyVersion(profile.getVersion()), installer); } else { throw new IOException(); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java index 105623aa8a..6e3c67e852 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricAPIInstallTask.java @@ -36,13 +36,13 @@ public final class FabricAPIInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final FabricAPIRemoteVersion remote; private final List> dependencies = new ArrayList<>(1); - public FabricAPIInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, FabricAPIRemoteVersion remoteVersion) { + public FabricAPIInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, FabricAPIRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; } @@ -60,7 +60,7 @@ public boolean isRelyingOnDependencies() { public void execute() throws IOException { dependencies.add(new FileDownloadTask( remote.getVersion().file().url(), - dependencyManager.getGameRepository().getModsDirectory(version.id()).resolve("fabric-api-" + remote.getVersion().version() + ".jar"), + dependencyManager.getGameRepository().getModsDirectory(manifest.id()).resolve("fabric-api-" + remote.getVersion().version() + ".jar"), remote.getVersion().file().getIntegrityCheck()) ); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java index 681f1895dc..470767a261 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java @@ -45,14 +45,14 @@ public final class FabricInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final FabricRemoteVersion remote; private final GetTask launchMetaTask; private final List> dependencies = new ArrayList<>(1); - public FabricInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, FabricRemoteVersion remoteVersion) { + public FabricInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, FabricRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; launchMetaTask = new GetTask(dependencyManager.getDownloadProvider().injectURLsWithCandidates(remoteVersion.getUrls())); @@ -66,7 +66,7 @@ public boolean doPreExecute() { @Override public void preExecute() throws Exception { - if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).mainClass())) + if (!Objects.equals("net.minecraft.client.main.Main", manifest.resolve(dependencyManager.getGameRepository()).mainClass())) throw new UnsupportedInstallationException(FABRIC_NOT_COMPATIBLE_WITH_FORGE); } @@ -93,7 +93,7 @@ public void execute() throws IOException { setResult(getPatch(fabricInfo, remote.getGameVersion(), remote.getSelfVersion())); - dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, manifest, true, getResult().getLibraries())); } private GameInstancePatch getPatch(FabricInfo fabricInfo, String gameVersion, String loaderVersion) { 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 ee885cdab1..bff618bfb8 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 @@ -46,15 +46,15 @@ public final class ForgeInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private Path installer; private final ForgeRemoteVersion remote; private FileDownloadTask dependent; private Task dependency; - public ForgeInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, ForgeRemoteVersion remoteVersion) { + public ForgeInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, ForgeRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; setSignificance(TaskSignificance.MODERATE); } @@ -99,31 +99,31 @@ public Collection> getDependencies() { @Override public void execute() throws IOException, VersionMismatchException, UnsupportedInstallationException { - String originalMainClass = version.resolve(dependencyManager.getGameRepository()).mainClass(); + String originalMainClass = manifest.resolve(dependencyManager.getGameRepository()).mainClass(); if (GameVersionNumber.compare("1.13", remote.getGameVersion()) <= 0) { // Forge 1.13 is not compatible with fabric. if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass)) throw new UnsupportedInstallationException(UNSUPPORTED_LAUNCH_WRAPPER); } - if (detectForgeInstallerType(dependencyManager, version, installer)) - dependency = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer); + if (detectForgeInstallerType(dependencyManager, manifest, installer)) + dependency = new ForgeNewInstallTask(dependencyManager, manifest, remote.getSelfVersion(), installer); else - dependency = new ForgeOldInstallTask(dependencyManager, version, remote.getSelfVersion(), installer); + dependency = new ForgeOldInstallTask(dependencyManager, manifest, remote.getSelfVersion(), installer); } /** * Detect Forge installer type. * * @param dependencyManager game repository - * @param version instance manifest + * @param manifest instance manifest * @param installer the Forge installer, either the new or old one. * @return true for new, false for old * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. * @throws VersionMismatchException if required game version of installer does not match the actual one. */ - public static boolean detectForgeInstallerType(DependencyManager dependencyManager, GameInstanceManifest version, Path installer) throws IOException, VersionMismatchException { - Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); + public static boolean detectForgeInstallerType(DependencyManager dependencyManager, GameInstanceManifest manifest, Path installer) throws IOException, VersionMismatchException { + Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(manifest); if (!gameVersion.isPresent()) throw new IOException(); try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { String installProfileText = Files.readString(fs.getPath("install_profile.json")); @@ -149,14 +149,14 @@ public static boolean detectForgeInstallerType(DependencyManager dependencyManag * This method will try to identify this installer whether it is in old or new format. * * @param dependencyManager game repository - * @param version instance manifest + * @param manifest instance manifest * @param installer the Forge installer, either the new or old one. * @return the task to install library * @throws IOException if unable to read compressed content of installer file, or installer file is corrupted, or the installer is not the one we want. * @throws VersionMismatchException if required game version of installer does not match the actual one. */ - public static Task install(DefaultDependencyManager dependencyManager, GameInstanceManifest version, Path installer) throws IOException, VersionMismatchException { - Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(version); + public static Task install(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, Path installer) throws IOException, VersionMismatchException { + Optional gameVersion = dependencyManager.getGameRepository().getGameVersion(manifest); if (!gameVersion.isPresent()) throw new IOException(); try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) { String installProfileText = Files.readString(fs.getPath("install_profile.json")); @@ -165,12 +165,12 @@ public static Task install(DefaultDependencyManager dependenc ForgeNewInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeNewInstallProfile.class); if (!gameVersion.get().equals(profile.getMinecraft())) throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get()); - return new ForgeNewInstallTask(dependencyManager, version, modifyVersion(gameVersion.get(), profile.getVersion()), installer); + return new ForgeNewInstallTask(dependencyManager, manifest, modifyVersion(gameVersion.get(), profile.getVersion()), installer); } else if (installProfile.containsKey("install") && installProfile.containsKey("versionInfo")) { ForgeInstallProfile profile = JsonUtils.fromNonNullJson(installProfileText, ForgeInstallProfile.class); if (!gameVersion.get().equals(profile.getInstall().getMinecraft())) throw new VersionMismatchException(profile.getInstall().getMinecraft(), gameVersion.get()); - return new ForgeOldInstallTask(dependencyManager, version, modifyVersion(gameVersion.get(), profile.getInstall().getPath().getVersion().replaceAll("(?i)forge", "")), installer); + return new ForgeOldInstallTask(dependencyManager, manifest, modifyVersion(gameVersion.get(), profile.getInstall().getPath().getVersion().replaceAll("(?i)forge", "")), installer); } else { throw new IOException(); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java index 21fe758743..53f96e34e2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java @@ -116,7 +116,7 @@ public void execute() throws Exception { return; } - Path jar = gameRepository.getArtifactFile(version, processor.getJar()); + Path jar = gameRepository.getArtifactFile(manifest, processor.getJar()); if (!Files.isRegularFile(jar)) throw new FileNotFoundException("Game processor file not found, should be downloaded in preprocess"); @@ -134,7 +134,7 @@ public void execute() throws Exception { List classpath = new ArrayList<>(processor.getClasspath().size() + 1); for (Artifact artifact : processor.getClasspath()) { - Path file = gameRepository.getArtifactFile(version, artifact); + Path file = gameRepository.getArtifactFile(manifest, artifact); if (!Files.isRegularFile(file)) throw new Exception("Game processor dependency missing"); classpath.add(file.toString()); @@ -195,7 +195,7 @@ public void execute() throws Exception { private final DefaultDependencyManager dependencyManager; private final DefaultGameRepository gameRepository; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final Path installer; private final List> dependents = new ArrayList<>(1); private final List> dependencies = new ArrayList<>(1); @@ -208,10 +208,10 @@ public void execute() throws Exception { private Path tempDir; private AtomicInteger processorDoneCount = new AtomicInteger(0); - public ForgeNewInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, String selfVersion, Path installer) { + public ForgeNewInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, String selfVersion, Path installer) { this.dependencyManager = dependencyManager; this.gameRepository = dependencyManager.getGameRepository(); - this.version = version; + this.manifest = manifest; this.installer = installer; this.selfVersion = selfVersion; @@ -268,7 +268,7 @@ private String parseLiteral(String literal, Map parseOptions(List args, Map vars) { @@ -409,11 +409,11 @@ public void execute() throws Exception { } vars.put("SIDE", "client"); - vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(version))); - vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(version))); + vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(manifest))); + vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(manifest))); vars.put("ROOT", FileUtils.getAbsolutePath(gameRepository.getBaseDirectory())); vars.put("INSTALLER", installer.toAbsolutePath().toString()); - vars.put("LIBRARY_DIR", FileUtils.getAbsolutePath(gameRepository.getLibrariesDirectory(version))); + vars.put("LIBRARY_DIR", FileUtils.getAbsolutePath(gameRepository.getLibrariesDirectory(manifest))); updateProgress(0, processors.size()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java index b9269af72e..1e2430dcc8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java @@ -39,14 +39,14 @@ public class ForgeOldInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final Path installer; private final String selfVersion; private final List> dependencies = new ArrayList<>(1); - ForgeOldInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, String selfVersion, Path installer) { + ForgeOldInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, String selfVersion, Path installer) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.installer = installer; this.selfVersion = selfVersion; @@ -73,7 +73,7 @@ public void execute() throws Exception { // unpack the universal jar in the installer file. Library forgeLibrary = new Library(installProfile.getInstall().getPath()); - Path forgeFile = dependencyManager.getGameRepository().getLibraryFile(version, forgeLibrary); + Path forgeFile = dependencyManager.getGameRepository().getLibraryFile(manifest, forgeLibrary); Files.createDirectories(forgeFile.getParent()); ZipEntry forgeEntry = zipFile.getEntry(installProfile.getInstall().getFilePath()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java index 22a3239a9e..5bb59fa647 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java @@ -35,13 +35,13 @@ public final class GameDownloadTask extends Task { private final DefaultDependencyManager dependencyManager; private final String gameVersion; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final List> dependencies = new ArrayList<>(); - public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, GameInstanceManifest version) { + public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, GameInstanceManifest manifest) { this.dependencyManager = dependencyManager; this.gameVersion = gameVersion; - this.version = version.resolve(dependencyManager.getGameRepository()); + this.manifest = manifest.resolve(dependencyManager.getGameRepository()); setSignificance(TaskSignificance.MODERATE); } @@ -53,12 +53,12 @@ public Collection> getDependencies() { @Override public void execute() { - Path jar = dependencyManager.getGameRepository().getInstanceJar(version); + Path jar = dependencyManager.getGameRepository().getInstanceJar(manifest); var task = new FileDownloadTask( - dependencyManager.getDownloadProvider().injectURLWithCandidates(version.getDownloadInfo().getUrl()), + dependencyManager.getDownloadProvider().injectURLWithCandidates(manifest.getDownloadInfo().getUrl()), jar, - FileDownloadTask.IntegrityCheck.of(CacheRepository.SHA1, version.getDownloadInfo().getSha1())); + FileDownloadTask.IntegrityCheck.of(CacheRepository.SHA1, manifest.getDownloadInfo().getSha1())); task.setCaching(true); task.setCacheRepository(dependencyManager.getCacheRepository()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java index b864a2ae94..3c54bb9101 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java @@ -35,15 +35,15 @@ public class GameInstallTask extends Task { private final DefaultGameRepository gameRepository; private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final GameRemoteVersion remote; private final VersionJsonDownloadTask downloadTask; private final List> dependencies = new ArrayList<>(1); - public GameInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, GameRemoteVersion remoteVersion) { + public GameInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, GameRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; this.gameRepository = dependencyManager.getGameRepository(); - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; this.downloadTask = new VersionJsonDownloadTask(remoteVersion.getGameVersion(), dependencyManager); } @@ -72,7 +72,7 @@ public void execute() throws Exception { GameInstancePatch.PRIORITY_MC).withJar(null); setResult(patch); - GameInstanceManifest version = new GameInstanceManifest(this.version.id()).addPatch(patch); + GameInstanceManifest version = new GameInstanceManifest(this.manifest.id()).addPatch(patch); dependencies.add(Task.allOf( new GameDownloadTask(dependencyManager, remote.getGameVersion(), version), Task.allOf( 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 3633c5b350..d4c9151bc3 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 @@ -55,7 +55,7 @@ public final class GameLibrariesTask extends Task { private final AbstractDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final boolean integrityCheck; private final List libraries; private final List> dependencies = new ArrayList<>(); @@ -64,21 +64,21 @@ public final class GameLibrariesTask extends Task { * Constructor. * * @param dependencyManager the dependency manager that can provides {@link GameRepository} - * @param version the game version + * @param manifest the game version */ - public GameLibrariesTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean integrityCheck) { - this(dependencyManager, version, integrityCheck, version.resolve(dependencyManager.getGameRepository()).getLibraries()); + public GameLibrariesTask(AbstractDependencyManager dependencyManager, GameInstanceManifest manifest, boolean integrityCheck) { + this(dependencyManager, manifest, integrityCheck, manifest.resolve(dependencyManager.getGameRepository()).getLibraries()); } /** * Constructor. * * @param dependencyManager the dependency manager that can provides {@link GameRepository} - * @param version the game version + * @param manifest the game version */ - public GameLibrariesTask(AbstractDependencyManager dependencyManager, GameInstanceManifest version, boolean integrityCheck, List libraries) { + public GameLibrariesTask(AbstractDependencyManager dependencyManager, GameInstanceManifest manifest, boolean integrityCheck, List libraries) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.integrityCheck = integrityCheck; this.libraries = libraries; @@ -91,8 +91,8 @@ public List> getDependencies() { return dependencies; } - public static boolean shouldDownloadLibrary(GameRepository gameRepository, GameInstanceManifest version, Library library, boolean integrityCheck) { - Path file = gameRepository.getLibraryFile(version, library); + public static boolean shouldDownloadLibrary(GameRepository gameRepository, GameInstanceManifest manifest, Library library, boolean integrityCheck) { + Path file = gameRepository.getLibraryFile(manifest, library); if (!Files.isRegularFile(file)) return true; if (!integrityCheck) { @@ -161,9 +161,9 @@ public void execute() throws IOException { } } - Path file = gameRepository.getLibraryFile(version, library); - if ("optifine".equals(library.getGroupId()) && Files.exists(file) && GameVersionNumber.asGameVersion(gameRepository.getGameVersion(version).orElse(null)).compareTo("1.20.4") == 0) { - String forgeVersion = LibraryAnalyzer.analyze(version, "1.20.4") + Path file = gameRepository.getLibraryFile(manifest, library); + if ("optifine".equals(library.getGroupId()) && Files.exists(file) && GameVersionNumber.asGameVersion(gameRepository.getGameVersion(manifest).orElse(null)).compareTo("1.20.4") == 0) { + String forgeVersion = LibraryAnalyzer.analyze(manifest, "1.20.4") .getVersion(LibraryAnalyzer.LibraryType.FORGE) .orElse(null); if (forgeVersion != null && LibraryAnalyzer.FORGE_OPTIFINE_BROKEN_RANGE.contains(VersionNumber.asVersion(forgeVersion))) { @@ -182,7 +182,7 @@ public void execute() throws IOException { } } - if (shouldDownloadLibrary(gameRepository, version, library, integrityCheck) && (library.hasDownloadURL() || !"optifine".equals(library.getGroupId()))) { + if (shouldDownloadLibrary(gameRepository, manifest, library, integrityCheck) && (library.hasDownloadURL() || !"optifine".equals(library.getGroupId()))) { dependencies.add(new LibraryDownloadTask(dependencyManager, file, library).withCounter("hmcl.install.libraries")); } else { dependencyManager.getCacheRepository().tryCacheLibrary(library, file); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java index 7c606b8e4d..32d40f7418 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java @@ -40,13 +40,13 @@ public final class GameVerificationFixTask extends Task { private final DefaultDependencyManager dependencyManager; private final String gameVersion; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final List> dependencies = new ArrayList<>(); - public GameVerificationFixTask(DefaultDependencyManager dependencyManager, String gameVersion, GameInstanceManifest version) { + public GameVerificationFixTask(DefaultDependencyManager dependencyManager, String gameVersion, GameInstanceManifest manifest) { this.dependencyManager = dependencyManager; this.gameVersion = gameVersion; - this.version = version; + this.manifest = manifest; setSignificance(TaskSignificance.MODERATE); } @@ -58,8 +58,8 @@ public Collection> getDependencies() { @Override public void execute() throws IOException { - Path jar = dependencyManager.getGameRepository().getInstanceJar(version); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version, gameVersion); + Path jar = dependencyManager.getGameRepository().getInstanceJar(manifest); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(manifest, gameVersion); if (Files.exists(jar) && GameVersionNumber.compare(gameVersion, "1.6") < 0 && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) { try (FileSystem fs = CompressingUtils.createWritableZipFileSystem(jar, StandardCharsets.UTF_8)) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java index d831788c44..d11361ff76 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricAPIInstallTask.java @@ -31,13 +31,13 @@ public final class LegacyFabricAPIInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final LegacyFabricAPIRemoteVersion remote; private final List> dependencies = new ArrayList<>(1); - public LegacyFabricAPIInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, LegacyFabricAPIRemoteVersion remoteVersion) { + public LegacyFabricAPIInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, LegacyFabricAPIRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; } @@ -55,7 +55,7 @@ public boolean isRelyingOnDependencies() { public void execute() throws IOException { dependencies.add(new FileDownloadTask( remote.getVersion().file().url(), - dependencyManager.getGameRepository().getModsDirectory(version.id()).resolve("legacy-fabric-api-" + remote.getVersion().version() + ".jar"), + dependencyManager.getGameRepository().getModsDirectory(manifest.id()).resolve("legacy-fabric-api-" + remote.getVersion().version() + ".jar"), remote.getVersion().file().getIntegrityCheck()) ); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricInstallTask.java index c404e0c752..ff0b6ef650 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/legacyfabric/LegacyFabricInstallTask.java @@ -39,14 +39,14 @@ public final class LegacyFabricInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final LegacyFabricRemoteVersion remote; private final GetTask launchMetaTask; private final List> dependencies = new ArrayList<>(1); - public LegacyFabricInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, LegacyFabricRemoteVersion remoteVersion) { + public LegacyFabricInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, LegacyFabricRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; launchMetaTask = new GetTask(dependencyManager.getDownloadProvider().injectURLsWithCandidates(remoteVersion.getUrls())); @@ -77,7 +77,7 @@ public boolean isRelyingOnDependencies() { public void execute() { setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), FabricInstallTask.FabricInfo.class), remote.getGameVersion(), remote.getSelfVersion())); - dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, manifest, true, getResult().getLibraries())); } private GameInstancePatch getPatch(FabricInstallTask.FabricInfo legacyFabricInfo, String gameVersion, String loaderVersion) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java index 2dc44f65e5..985de491c2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java @@ -36,14 +36,14 @@ public final class LiteLoaderInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final LiteLoaderRemoteVersion remote; private final List> dependents = new ArrayList<>(); private final List> dependencies = new ArrayList<>(1); - public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, LiteLoaderRemoteVersion remoteVersion) { + public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, LiteLoaderRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; } @@ -74,7 +74,7 @@ public void execute() { .withLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration ); - dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, manifest, true, getResult().getLibraries())); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java index ee961fe7c8..b5f6bc6f59 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java @@ -40,7 +40,7 @@ public final class NeoForgeInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final NeoForgeRemoteVersion remoteVersion; @@ -50,9 +50,9 @@ public final class NeoForgeInstallTask extends Task { private Task dependency; - public NeoForgeInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, NeoForgeRemoteVersion remoteVersion) { + public NeoForgeInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, NeoForgeRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remoteVersion = remoteVersion; } @@ -97,7 +97,7 @@ public Collection> getDependencies() { @Override public void execute() throws Exception { - dependency = install(dependencyManager, version, installer); + dependency = install(dependencyManager, manifest, installer); } public static Task install(DefaultDependencyManager dependencyManager, GameInstanceManifest version, Path installer) throws IOException, VersionMismatchException { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java index 5590f536bb..d05d3b4e0e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java @@ -110,7 +110,7 @@ public void execute() throws Exception { return; } - Path jar = gameRepository.getArtifactFile(version, processor.getJar()); + Path jar = gameRepository.getArtifactFile(manifest, processor.getJar()); if (!Files.isRegularFile(jar)) throw new FileNotFoundException("Game processor file not found, should be downloaded in preprocess"); @@ -128,7 +128,7 @@ public void execute() throws Exception { List classpath = new ArrayList<>(processor.getClasspath().size() + 1); for (Artifact artifact : processor.getClasspath()) { - Path file = gameRepository.getArtifactFile(version, artifact); + Path file = gameRepository.getArtifactFile(manifest, artifact); if (!Files.isRegularFile(file)) throw new Exception("Game processor dependency missing"); classpath.add(file.toString()); @@ -173,7 +173,7 @@ public void execute() throws Exception { private final DefaultDependencyManager dependencyManager; private final DefaultGameRepository gameRepository; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final Path installer; private final List> dependents = new ArrayList<>(1); private final List> dependencies = new ArrayList<>(1); @@ -186,10 +186,10 @@ public void execute() throws Exception { private Path tempDir; private AtomicInteger processorDoneCount = new AtomicInteger(0); - NeoForgeOldInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, String selfVersion, Path installer) { + NeoForgeOldInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, String selfVersion, Path installer) { this.dependencyManager = dependencyManager; this.gameRepository = dependencyManager.getGameRepository(); - this.version = version; + this.manifest = manifest; this.installer = installer; this.selfVersion = selfVersion; @@ -246,7 +246,7 @@ private String parseLiteral(String literal, Map parseOptions(List args, Map vars) { @@ -387,11 +387,11 @@ public void execute() throws Exception { } vars.put("SIDE", "client"); - vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(version))); - vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(version))); + vars.put("MINECRAFT_JAR", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(manifest))); + vars.put("MINECRAFT_VERSION", FileUtils.getAbsolutePath(gameRepository.getInstanceJar(manifest))); vars.put("ROOT", FileUtils.getAbsolutePath(gameRepository.getBaseDirectory())); vars.put("INSTALLER", installer.toAbsolutePath().toString()); - vars.put("LIBRARY_DIR", FileUtils.getAbsolutePath(gameRepository.getLibrariesDirectory(version))); + vars.put("LIBRARY_DIR", FileUtils.getAbsolutePath(gameRepository.getLibrariesDirectory(manifest))); updateProgress(0, processors.size()); 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 4ccccad024..8e39854b17 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 @@ -52,7 +52,7 @@ public final class OptiFineInstallTask extends Task { private final DefaultGameRepository gameRepository; private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final OptiFineRemoteVersion remote; private final Path installer; private final List> dependents = new ArrayList<>(0); @@ -62,14 +62,14 @@ public final class OptiFineInstallTask extends Task { private final Library optiFineLibrary; private final Library optiFineInstallerLibrary; - public OptiFineInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, OptiFineRemoteVersion remoteVersion) { - this(dependencyManager, version, remoteVersion, null); + public OptiFineInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, OptiFineRemoteVersion remoteVersion) { + this(dependencyManager, manifest, remoteVersion, null); } - public OptiFineInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, OptiFineRemoteVersion remoteVersion, Path installer) { + public OptiFineInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, OptiFineRemoteVersion remoteVersion, Path installer) { this.dependencyManager = dependencyManager; this.gameRepository = dependencyManager.getGameRepository(); - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; this.installer = installer; @@ -123,14 +123,14 @@ public boolean isRelyingOnDependencies() { @Override public void execute() throws Exception { - String originalMainClass = version.resolve(dependencyManager.getGameRepository()).mainClass(); + String originalMainClass = manifest.resolve(dependencyManager.getGameRepository()).mainClass(); if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass)) throw new UnsupportedInstallationException(UnsupportedInstallationException.UNSUPPORTED_LAUNCH_WRAPPER); List libraries = new ArrayList<>(4); libraries.add(optiFineLibrary); - Path optiFineInstallerLibraryPath = gameRepository.getLibraryFile(version, optiFineInstallerLibrary); + Path optiFineInstallerLibraryPath = gameRepository.getLibraryFile(manifest, optiFineInstallerLibrary); FileUtils.copyFile(dest, optiFineInstallerLibraryPath); try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(optiFineInstallerLibraryPath)) { @@ -140,14 +140,14 @@ public void execute() throws Exception { // Install launch wrapper modified by OptiFine boolean hasLaunchWrapper = false; try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest)) { - Path optiFineLibraryPath = gameRepository.getLibraryFile(version, optiFineLibrary); + Path optiFineLibraryPath = gameRepository.getLibraryFile(manifest, optiFineLibrary); if (Files.exists(fs.getPath("optifine/Patcher.class"))) { String[] command = { JavaRuntime.getDefault().getBinary().toString(), "-cp", dest.toString(), "optifine.Patcher", - gameRepository.getInstanceJar(version).toAbsolutePath().normalize().toString(), + gameRepository.getInstanceJar(manifest).toAbsolutePath().normalize().toString(), dest.toString(), optiFineLibraryPath.toString() }; @@ -165,7 +165,7 @@ public void execute() throws Exception { Path launchWrapper2 = fs.getPath("launchwrapper-2.0.jar"); if (Files.exists(launchWrapper2)) { Library launchWrapper = new Library(new Artifact("optifine", "launchwrapper", "2.0")); - Path launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper); + Path launchWrapperFile = gameRepository.getLibraryFile(manifest, launchWrapper); Files.createDirectories(launchWrapperFile.toAbsolutePath().getParent()); FileUtils.copyFile(launchWrapper2, launchWrapperFile); hasLaunchWrapper = true; @@ -180,7 +180,7 @@ public void execute() throws Exception { Library launchWrapper = new Library(new Artifact("optifine", "launchwrapper-of", launchWrapperVersion)); if (Files.exists(launchWrapperJar)) { - Path launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper); + Path launchWrapperFile = gameRepository.getLibraryFile(manifest, launchWrapper); Files.createDirectories(launchWrapperFile.toAbsolutePath().getParent()); FileUtils.copyFile(launchWrapperJar, launchWrapperFile); @@ -216,7 +216,7 @@ public void execute() throws Exception { libraries )); - dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, manifest, true, getResult().getLibraries())); } /** diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java index 5def4a4690..1bacd55e61 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltAPIInstallTask.java @@ -36,13 +36,13 @@ public final class QuiltAPIInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final QuiltAPIRemoteVersion remote; private final List> dependencies = new ArrayList<>(1); - public QuiltAPIInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, QuiltAPIRemoteVersion remoteVersion) { + public QuiltAPIInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, QuiltAPIRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; } @@ -60,7 +60,7 @@ public boolean isRelyingOnDependencies() { public void execute() throws IOException { dependencies.add(new FileDownloadTask( remote.getVersion().file().url(), - dependencyManager.getGameRepository().getModsDirectory(version.id()).resolve("quilt-api-" + remote.getVersion().version() + ".jar"), + dependencyManager.getGameRepository().getModsDirectory(manifest.id()).resolve("quilt-api-" + remote.getVersion().version() + ".jar"), remote.getVersion().file().getIntegrityCheck()) ); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java index 107b32e5e2..31cd37672d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/quilt/QuiltInstallTask.java @@ -44,14 +44,14 @@ public final class QuiltInstallTask extends Task { private final DefaultDependencyManager dependencyManager; - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final QuiltRemoteVersion remote; private final GetTask launchMetaTask; private final List> dependencies = new ArrayList<>(1); - public QuiltInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest version, QuiltRemoteVersion remoteVersion) { + public QuiltInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, QuiltRemoteVersion remoteVersion) { this.dependencyManager = dependencyManager; - this.version = version; + this.manifest = manifest; this.remote = remoteVersion; launchMetaTask = new GetTask(dependencyManager.getDownloadProvider().injectURLsWithCandidates(remoteVersion.getUrls())); @@ -65,7 +65,7 @@ public boolean doPreExecute() { @Override public void preExecute() throws Exception { - if (!Objects.equals("net.minecraft.client.main.Main", version.resolve(dependencyManager.getGameRepository()).mainClass())) + if (!Objects.equals("net.minecraft.client.main.Main", manifest.resolve(dependencyManager.getGameRepository()).mainClass())) throw new UnsupportedInstallationException(FABRIC_NOT_COMPATIBLE_WITH_FORGE); } @@ -88,7 +88,7 @@ public boolean isRelyingOnDependencies() { public void execute() { setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), QuiltInfo.class), remote.getGameVersion(), remote.getSelfVersion())); - dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, version, true, getResult().getLibraries())); + dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, manifest, true, getResult().getLibraries())); } private GameInstancePatch getPatch(QuiltInfo quiltInfo, String gameVersion, String loaderVersion) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java index ea6973e90a..60c6ebbbb1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceLibraryBuilder.java @@ -28,7 +28,7 @@ * @author huangyuhui */ public final class GameInstanceLibraryBuilder { - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final List mcArgs; private final List game; private final List jvm; @@ -37,7 +37,7 @@ public final class GameInstanceLibraryBuilder { private boolean jvmChanged = false; public GameInstanceLibraryBuilder(GameInstanceManifest manifest) { - this.version = manifest; + this.manifest = manifest; this.libraries = new ArrayList<>(manifest.getLibraries()); this.mcArgs = Optional.ofNullable(manifest.minecraftArguments()).map(StringUtils::tokenize).map(ArrayList::new).orElse(null); this.game = Optional.ofNullable(manifest.arguments()).map(Arguments::game).map(ArrayList::new).orElseGet(ArrayList::new); @@ -46,7 +46,7 @@ public GameInstanceLibraryBuilder(GameInstanceManifest manifest) { } public GameInstanceManifest build() { - GameInstanceManifest ret = version; + GameInstanceManifest ret = manifest; if (useMcArgs) { // The official launcher will not parse the "arguments" property when it detects the presence of "mcArgs". // The "arguments" property with the "rule" is simply ignored here. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java index cc8be3f9b8..6b2d0333f3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/JavaVersionConstraint.java @@ -57,12 +57,12 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul } @Override - public VersionRange getJavaVersionRange(GameInstanceManifest version, LibraryAnalyzer analyzer) { + public VersionRange getJavaVersionRange(GameInstanceManifest manifest, LibraryAnalyzer analyzer) { String javaVersion; - if (Objects.requireNonNull(version.javaVersion()).majorVersion() >= 9) { - javaVersion = "" + version.javaVersion().majorVersion(); + if (Objects.requireNonNull(manifest.javaVersion()).majorVersion() >= 9) { + javaVersion = "" + manifest.javaVersion().majorVersion(); } else { - javaVersion = "1." + version.javaVersion().majorVersion(); + javaVersion = "1." + manifest.javaVersion().majorVersion(); } return VersionNumber.atLeast(javaVersion); } @@ -116,7 +116,7 @@ protected boolean appliesToVersionImpl(GameVersionNumber gameVersionNumber, @Nul } @Override - public VersionRange getJavaVersionRange(GameInstanceManifest version, LibraryAnalyzer analyzer) { + public VersionRange getJavaVersionRange(GameInstanceManifest manifest, LibraryAnalyzer analyzer) { if (analyzer == null || !analyzer.has(LibraryAnalyzer.LibraryType.CLEANROOM)) return VersionRange.all(); @@ -243,7 +243,7 @@ public VersionRange getGameVersionRange() { return gameVersionRange; } - public VersionRange getJavaVersionRange(GameInstanceManifest version, LibraryAnalyzer analyzer) { + public VersionRange getJavaVersionRange(GameInstanceManifest manifest, LibraryAnalyzer analyzer) { return javaVersionRange; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java index 67f54cd09b..996153c2b8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java @@ -105,15 +105,15 @@ public List> getDependencies() { @Override public void execute() throws Exception { - GameInstanceManifest version = repository.getInstanceManifest(instanceId); - Optional mcbbsPatch = version.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst(); + GameInstanceManifest instanceManifest = repository.getInstanceManifest(instanceId); + Optional mcbbsPatch = instanceManifest.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst(); if (!update) { GameInstancePatch patch = new GameInstancePatch(PATCH_NAME).withLibraries(manifest.getLibraries()); - dependencies.add(repository.saveAsync(version.addPatch(patch))); + dependencies.add(repository.saveAsync(instanceManifest.addPatch(patch))); } else if (mcbbsPatch.isPresent()) { // This mcbbs modpack was installed by HMCL. GameInstancePatch patch = mcbbsPatch.get().withLibraries(manifest.getLibraries()); - dependencies.add(repository.saveAsync(version.addPatch(patch))); + dependencies.add(repository.saveAsync(instanceManifest.addPatch(patch))); } else { // This mcbbs modpack was installed by other launchers. // TODO: maintain libraries. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java index b8cf671f58..7391621fc7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCInstancePatch.java @@ -199,7 +199,7 @@ public static MultiMCInstancePatch read(String componentID, String text) { } public static final class ResolvedInstance { - private final GameInstanceManifest version; + private final GameInstanceManifest manifest; private final String gameVersion; @@ -208,16 +208,16 @@ public static final class ResolvedInstance { private final List jarModFileNames; private final List mavenOnlyFiles; - public ResolvedInstance(GameInstanceManifest version, String gameVersion, Library mainJar, List jarModFileNames, List mavenOnlyFiles) { - this.version = version; + public ResolvedInstance(GameInstanceManifest manifest, String gameVersion, Library mainJar, List jarModFileNames, List mavenOnlyFiles) { + this.manifest = manifest; this.gameVersion = gameVersion; this.mainJar = mainJar; this.jarModFileNames = jarModFileNames; this.mavenOnlyFiles = mavenOnlyFiles; } - public GameInstanceManifest getVersion() { - return version; + public GameInstanceManifest getManifest() { + return manifest; } public String getGameVersion() { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java index cdc404e6d5..08ea5babbe 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java @@ -251,21 +251,21 @@ public void execute() throws Exception { if (Files.exists(libraries)) FileUtils.copyDirectory(libraries, repository.getInstanceRoot(instanceId).resolve("libraries")); - for (Library library : artifact.getVersion().getLibraries()) { + for (Library library : artifact.getManifest().getLibraries()) { if ("local".equals(library.getHint())) { /* TODO: Determine whether we should erase community fields, like 'hint' and 'filename' from version json. Retain them will facilitate compatibility, as some embedded libraries may check where their JAR is. Meanwhile, potential compatibility issue with other launcher which never supports these fields might occur. Here, we make the file stored twice, to keep maximum compatibility. */ - Path from = repository.getLibraryFile(artifact.getVersion(), library); - Path target = repository.getLibraryFile(artifact.getVersion(), library.withoutCommunityFields()); + Path from = repository.getLibraryFile(artifact.getManifest(), library); + Path target = repository.getLibraryFile(artifact.getManifest(), library.withoutCommunityFields()); Files.createDirectories(target.getParent()); Files.copy(from, target, StandardCopyOption.REPLACE_EXISTING); } } try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLMultiMCBootstrap-1.0.jar")) { - Path libraryPath = repository.getLibraryFile(artifact.getVersion(), MultiMCInstancePatch.BOOTSTRAP_LIBRARY); + Path libraryPath = repository.getLibraryFile(artifact.getManifest(), MultiMCInstancePatch.BOOTSTRAP_LIBRARY); Files.createDirectories(libraryPath.getParent()); Files.copy(Objects.requireNonNull(input, "Bundled HMCLMultiMCBootstrap is missing."), libraryPath, StandardCopyOption.REPLACE_EXISTING); @@ -282,14 +282,14 @@ public void execute() throws Exception { // Stage #5: Assemble game files. { - GameInstanceManifest version = artifact.getVersion(); + GameInstanceManifest instanceManifest = artifact.getManifest(); - dependencies.add(repository.saveAsync(artifact.getVersion())); - dependencies.add(new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true)); + dependencies.add(repository.saveAsync(artifact.getManifest())); + dependencies.add(new GameAssetDownloadTask(dependencyManager, instanceManifest, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true)); dependencies.add(new GameLibrariesTask( dependencyManager, // TODO: check integrity of maven-only files when launching games? - version.withLibraries(Lang.merge(version.getLibraries(), artifact.getMavenOnlyFiles())), + instanceManifest.withLibraries(Lang.merge(instanceManifest.getLibraries(), artifact.getMavenOnlyFiles())), true )); @@ -301,9 +301,9 @@ public void execute() throws Exception { Objects.equals(gameVersion, mainJarArtifact.getVersion()) && "client".equals(mainJarArtifact.getClassifier()) ) { - dependencies.add(new GameDownloadTask(dependencyManager, gameVersion, version)); + dependencies.add(new GameDownloadTask(dependencyManager, gameVersion, instanceManifest)); } else { - dependencies.add(new GameDownloadTask(dependencyManager, null, version)); + dependencies.add(new GameDownloadTask(dependencyManager, null, instanceManifest)); } } From dfa97a33533642dee4231596ba2688ad6a26db08 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:21:24 +0800 Subject: [PATCH 62/85] refactor: rename versionJson to instanceJson for consistency --- HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java index 07be77f54a..efb5092eef 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java @@ -75,9 +75,9 @@ public static CompletableFuture exportLogs( zipper.putTextFile(Logger.filterForbiddenToken(launchScript), OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "launch.bat" : "launch.sh"); for (GameInstanceID id : instances) { - Path versionJson = repository.getInstanceJson(id); - if (Files.exists(versionJson)) { - zipper.putFile(versionJson, id + ".json"); + Path instanceJson = repository.getInstanceJson(id); + if (Files.exists(instanceJson)) { + zipper.putFile(instanceJson, id + ".json"); } } } catch (IOException e) { From 0c4d569169bd63afbbdec11b6e3a5a28bdd5789f Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:25:05 +0800 Subject: [PATCH 63/85] refactor: rename version references to instance for consistency --- .../org/jackhuang/hmcl/ui/Controllers.java | 6 ++--- .../hmcl/ui/instances/DownloadListPage.java | 24 +++++++++---------- .../HMCLLocalizedDownloadListPage.java | 4 ++-- .../hmcl/ui/instances/Instances.java | 6 ++--- .../org/jackhuang/hmcl/ui/main/RootPage.java | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index 2435f44fdf..2a1b68d163 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -142,7 +142,7 @@ public static Stage getStage() { } @FXThread - public static GameInstancePage getVersionPage() { + public static GameInstancePage getGameInstancePage() { if (gameInstancePage == null) { gameInstancePage = new GameInstancePage(); } @@ -150,9 +150,9 @@ public static GameInstancePage getVersionPage() { } @FXThread - public static void prepareVersionPage() { + public static void prepareGameInstancePage() { if (gameInstancePage == null) { - LOG.info("Prepare the version page"); + LOG.info("Prepare the game instance page"); gameInstancePage = FXUtils.prepareNode(new GameInstancePage()); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java index 8f002ccd3a..6ed4142842 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java @@ -72,7 +72,7 @@ public class DownloadListPage extends Control implements DecoratorPage, GameInst protected final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(); private final BooleanProperty loading = new SimpleBooleanProperty(false); private final BooleanProperty failed = new SimpleBooleanProperty(false); - private final boolean versionSelection; + private final boolean instanceSelection; private final ObjectProperty instanceReference = new SimpleObjectProperty<>(); private final IntegerProperty pageOffset = new SimpleIntegerProperty(0); private final IntegerProperty pageCount = new SimpleIntegerProperty(-1); @@ -96,10 +96,10 @@ public DownloadListPage(RemoteAddonRepository repository) { this(repository, null, false); } - public DownloadListPage(RemoteAddonRepository repository, DownloadPage.DownloadCallback callback, boolean versionSelection) { + public DownloadListPage(RemoteAddonRepository repository, DownloadPage.DownloadCallback callback, boolean instanceSelection) { this.repository = repository; this.callback = callback; - this.versionSelection = versionSelection; + this.instanceSelection = instanceSelection; this.downloadProvider = DownloadProviders.getDownloadProvider(); } @@ -123,7 +123,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID search("", null, 0, "", RemoteAddonRepository.SortType.POPULARITY); } - if (versionSelection) { + if (instanceSelection) { instances.setAll(repository.getDisplayInstanceManifests() .map(GameInstanceManifest::id) .toList()); @@ -218,7 +218,7 @@ protected String getLocalizedOfficialPage() { } protected HMCLGameRepository.InstanceReference getInstanceReference() { - if (versionSelection) { + if (instanceSelection) { @Nullable GameInstanceID instanceId = selectedInstance.get(); return new HMCLGameRepository.InstanceReference(instanceReference.get().repository(), instanceId); } else { @@ -273,18 +273,18 @@ protected ModDownloadListPageSkin(DownloadListPage control) { { int rowIndex = 0; - if (control.versionSelection || !control.downloadSources.isEmpty()) { + if (control.instanceSelection || !control.downloadSources.isEmpty()) { searchPane.addRow(rowIndex); int columns = 0; Node lastNode = null; - if (control.versionSelection) { - JFXComboBox versionsComboBox = new JFXComboBox<>(); - versionsComboBox.setMaxWidth(Double.MAX_VALUE); - Bindings.bindContent(versionsComboBox.getItems(), control.instances); - selectedItemPropertyFor(versionsComboBox).bindBidirectional(control.selectedInstance); + if (control.instanceSelection) { + JFXComboBox instancesComboBox = new JFXComboBox<>(); + instancesComboBox.setMaxWidth(Double.MAX_VALUE); + Bindings.bindContent(instancesComboBox.getItems(), control.instances); + selectedItemPropertyFor(instancesComboBox).bindBidirectional(control.selectedInstance); searchPane.add(new Label(i18n("version")), columns++, rowIndex); - searchPane.add(lastNode = versionsComboBox, columns++, rowIndex); + searchPane.add(lastNode = instancesComboBox, columns++, rowIndex); } if (control.downloadSources.getSize() > 1) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java index d167eda0f7..266524f980 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java @@ -64,8 +64,8 @@ public static DownloadListPage ofShaderPack(DownloadPage.DownloadCallback callba return page; } - private HMCLLocalizedDownloadListPage(DownloadPage.DownloadCallback callback, boolean versionSelection, RemoteAddonRepository.Type type, CurseForgeRemoteAddonRepository curseForge, ModrinthRemoteAddonRepository modrinth) { - super(null, callback, versionSelection); + private HMCLLocalizedDownloadListPage(DownloadPage.DownloadCallback callback, boolean instanceSelection, RemoteAddonRepository.Type type, CurseForgeRemoteAddonRepository curseForge, ModrinthRemoteAddonRepository modrinth) { + super(null, callback, instanceSelection); repository = new Repository(type, curseForge, modrinth); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java index d2bf00095f..47cb76f3d8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java @@ -389,9 +389,9 @@ public static void modifyGlobalSettings(HMCLGameRepository repository) { } public static void modifyGameSettings(HMCLGameRepository repository, GameInstanceID instanceId) { - Controllers.getVersionPage().setInstance(instanceId, repository); - Controllers.getVersionPage().showInstanceSettings(); + Controllers.getGameInstancePage().setInstance(instanceId, repository); + Controllers.getGameInstancePage().showInstanceSettings(); // VersionPage.loadVersion will be invoked after navigation - Controllers.navigate(Controllers.getVersionPage()); + Controllers.navigate(Controllers.getGameInstancePage()); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 3a672e8c73..c94fb35340 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -167,7 +167,7 @@ protected Skin(RootPage control) { return Lang.indexWhere(list, instance -> instance.id().equals(currentId)); }, it -> getSkinnable().getMainPage().getRepository().setSelectedInstance(it.id())); if (AnimationUtils.isAnimationEnabled()) { - FXUtils.prepareOnMouseEnter(gameListItem, Controllers::prepareVersionPage); + FXUtils.prepareOnMouseEnter(gameListItem, Controllers::prepareGameInstancePage); } FXUtils.onSecondaryButtonClicked(gameListItem, () -> showGameListPopupMenu(gameListItem)); From 4a5f950e4394eee60975f5d14fb8e4d86811d9a7 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:26:49 +0800 Subject: [PATCH 64/85] refactor: rename versionSelection to instanceSelection for consistency --- .../HMCLLocalizedDownloadListPage.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java index 266524f980..23c43e3e94 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java @@ -30,36 +30,36 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public final class HMCLLocalizedDownloadListPage extends DownloadListPage { - public static DownloadListPage ofMod(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.MOD, CurseForgeRemoteAddonRepository.MODS, ModrinthRemoteAddonRepository.MODS); + public static DownloadListPage ofMod(DownloadPage.DownloadCallback callback, boolean instanceSelection) { + return new HMCLLocalizedDownloadListPage(callback, instanceSelection, RemoteAddonRepository.Type.MOD, CurseForgeRemoteAddonRepository.MODS, ModrinthRemoteAddonRepository.MODS); } - public static DownloadListPage ofCurseForgeMod(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.MOD, CurseForgeRemoteAddonRepository.MODS, null); + public static DownloadListPage ofCurseForgeMod(DownloadPage.DownloadCallback callback, boolean instanceSelection) { + return new HMCLLocalizedDownloadListPage(callback, instanceSelection, RemoteAddonRepository.Type.MOD, CurseForgeRemoteAddonRepository.MODS, null); } - public static DownloadListPage ofModrinthMod(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.MOD, null, ModrinthRemoteAddonRepository.MODS); + public static DownloadListPage ofModrinthMod(DownloadPage.DownloadCallback callback, boolean instanceSelection) { + return new HMCLLocalizedDownloadListPage(callback, instanceSelection, RemoteAddonRepository.Type.MOD, null, ModrinthRemoteAddonRepository.MODS); } - public static DownloadListPage ofModPack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.MODPACK, CurseForgeRemoteAddonRepository.MODPACKS, ModrinthRemoteAddonRepository.MODPACKS); + public static DownloadListPage ofModPack(DownloadPage.DownloadCallback callback, boolean instanceSelection) { + return new HMCLLocalizedDownloadListPage(callback, instanceSelection, RemoteAddonRepository.Type.MODPACK, CurseForgeRemoteAddonRepository.MODPACKS, ModrinthRemoteAddonRepository.MODPACKS); } - public static DownloadListPage ofResourcePack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.RESOURCE_PACK, CurseForgeRemoteAddonRepository.RESOURCE_PACKS, ModrinthRemoteAddonRepository.RESOURCE_PACKS); + public static DownloadListPage ofResourcePack(DownloadPage.DownloadCallback callback, boolean instanceSelection) { + return new HMCLLocalizedDownloadListPage(callback, instanceSelection, RemoteAddonRepository.Type.RESOURCE_PACK, CurseForgeRemoteAddonRepository.RESOURCE_PACKS, ModrinthRemoteAddonRepository.RESOURCE_PACKS); } - public static DownloadListPage ofCurseForgeResourcePack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.RESOURCE_PACK, CurseForgeRemoteAddonRepository.RESOURCE_PACKS, null); + public static DownloadListPage ofCurseForgeResourcePack(DownloadPage.DownloadCallback callback, boolean instanceSelection) { + return new HMCLLocalizedDownloadListPage(callback, instanceSelection, RemoteAddonRepository.Type.RESOURCE_PACK, CurseForgeRemoteAddonRepository.RESOURCE_PACKS, null); } - public static DownloadListPage ofModrinthResourcePack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - return new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.RESOURCE_PACK, null, ModrinthRemoteAddonRepository.RESOURCE_PACKS); + public static DownloadListPage ofModrinthResourcePack(DownloadPage.DownloadCallback callback, boolean instanceSelection) { + return new HMCLLocalizedDownloadListPage(callback, instanceSelection, RemoteAddonRepository.Type.RESOURCE_PACK, null, ModrinthRemoteAddonRepository.RESOURCE_PACKS); } - public static DownloadListPage ofShaderPack(DownloadPage.DownloadCallback callback, boolean versionSelection) { - var page = new HMCLLocalizedDownloadListPage(callback, versionSelection, RemoteAddonRepository.Type.SHADER_PACK, CurseForgeRemoteAddonRepository.SHADERS, ModrinthRemoteAddonRepository.SHADER_PACKS); + public static DownloadListPage ofShaderPack(DownloadPage.DownloadCallback callback, boolean instanceSelection) { + var page = new HMCLLocalizedDownloadListPage(callback, instanceSelection, RemoteAddonRepository.Type.SHADER_PACK, CurseForgeRemoteAddonRepository.SHADERS, ModrinthRemoteAddonRepository.SHADER_PACKS); page.supportChinese.set(false); return page; } From 3bf312095ab1d3fbfece02de477e4e60c753747f Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:41:03 +0800 Subject: [PATCH 65/85] refactor: update GameInstanceManifest.Resolved parameters for clarity --- .../java/org/jackhuang/hmcl/game/DefaultGameRepository.java | 2 +- .../java/org/jackhuang/hmcl/game/GameInstanceManifest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index b4eeedf0c8..7fcbf5889e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -709,7 +709,7 @@ private GameInstanceManifest.Resolved resolve(GameInstanceManifest manifest, standaloneManifest = standaloneManifest.withJar(launchManifest.jar()); } - return new GameInstanceManifest.Resolved(launchManifest, standaloneManifest); + return new GameInstanceManifest.Resolved(manifest, launchManifest, standaloneManifest); } private static GameInstanceManifest addPatches(GameInstanceManifest manifest, @Nullable Collection additional) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 186629f820..771d0e6949 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -66,7 +66,8 @@ public record GameInstanceManifest( /// @param launchManifest the final manifest data used by launch-time consumers /// @param standaloneManifest the standalone manifest data with pending patches preserved @NotNullByDefault - public record Resolved(GameInstanceManifest launchManifest, + public record Resolved(GameInstanceManifest unresolved, + GameInstanceManifest launchManifest, GameInstanceManifest standaloneManifest) { /// Creates a resolved manifest view. From 44a519d1f9133e0a47a95e17003dbca049597ace Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:42:31 +0800 Subject: [PATCH 66/85] refactor: remove obsolete version.cannot_read entries from localization files --- HMCL/src/main/resources/assets/lang/I18N.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_ar.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_es.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_ja.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_lzh.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_ru.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_uk.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_zh.properties | 1 - HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 1 - 9 files changed, 9 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index e1365723a8..9b45c3be3a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1802,7 +1802,6 @@ update.preview.subtitle=Enable this option to receive new versions of HMCL early version=Games version.name=Instance Name -version.cannot_read=Failed to parse the game instance, installation cannot continue. version.empty=No Instances version.empty.add=Add new instance version.empty.launch=No available instances.\nYou can go to the "Download" page to get the game, or switch the game directory in the "All Instances" page. diff --git a/HMCL/src/main/resources/assets/lang/I18N_ar.properties b/HMCL/src/main/resources/assets/lang/I18N_ar.properties index 58144270e0..8e3314eab2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ar.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ar.properties @@ -1568,7 +1568,6 @@ update.preview.subtitle=فعّل هذا الخيار لاستقبال إصدار version=الألعاب version.name=اسم النسخة -version.cannot_read=فشل تحليل نسخة اللعبة، لا يمكن متابعة التثبيت. version.empty=لا توجد نسخ version.empty.add=إضافة نسخة جديدة version.empty.launch=لا توجد نسخ متاحة.\nيمكنك الانتقال إلى صفحة "التنزيل" للحصول على اللعبة، أو تغيير مجلد اللعبة في صفحة "جميع النسخ". diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 6e93385e37..b141caba87 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -1381,7 +1381,6 @@ update.preview.subtitle=Activa esta opción para recibir nuevas versiones del la version=Juegos version.name=Nombre de instancia -version.cannot_read=No se ha podido analizar la instancia del juego, la instalación no puede continuar. version.empty=No hay instancias version.empty.add=Añadir una instancia version.empty.hint=No hay instancias de Minecraft aquí.\nPuedes intentar cambiar a otro directorio del juego o hacer clic aquí para descargar una. diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index e8c7856aa8..a54ed75ebc 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -903,7 +903,6 @@ update.no_browser=ブラウザを開くことができません。リンクが update.tooltip=更新 version=ゲーム -version.cannot_read=ゲームのバージョンが見つかりません。自動インストールを続行できません。 version.empty=ゲームなし version.empty.add=起動構成を作成 version.empty.hint=インストールされているゲームバージョンはありません。新しいゲームをダウンロードするには、ここをクリックしてください。 diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 7c8c5eb4f3..530a12a44b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -1210,7 +1210,6 @@ update.preview.subtitle=啟此,君可先得 HMCL 之新,試之於未行。 version=戲 version.name=戲例之名 -version.cannot_read=不能閲戲例,無自裝矣 version.empty=無游戲例 version.empty.add=入引頁以裝戲 version.empty.hint=無畢裝者。\n君且更他戲夾,抑擊斯以往戲之引頁。 diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index e9093c8454..8e6619dc13 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -1368,7 +1368,6 @@ update.tooltip=Обновить version=Игры version.name=Название сборки -version.cannot_read=Невозможно найти версию игры. Невозможно продолжить автоматическую установку. version.empty=Нет сборок version.empty.add=Установить новую сборок version.empty.hint=Нет сборок.\nВы можете попробовать перейти в другую папку с игрой или нажать здесь, чтобы скачать игру. diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 3607e586eb..fa992f1288 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -1321,7 +1321,6 @@ update.tooltip=Оновити version=Ігри version.name=Назва екземпляра -version.cannot_read=Не вдалося розібрати екземпляр гри, встановлення не може продовжуватися. version.empty=Немає екземплярів version.empty.add=Додати новий екземпляр version.empty.hint=Тут немає екземплярів Minecraft. Ви можете спробувати переключитися на інший ігровий каталог або натиснути тут, щоб завантажити один. diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 69a467a554..926aaf4a43 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1589,7 +1589,6 @@ update.preview.subtitle=啟用此選項,你將可以提前取得 HMCL 的新 version=遊戲 version.name=遊戲實例名稱 -version.cannot_read=讀取遊戲實例失敗,無法進行自動安裝 version.empty=沒有遊戲實例 version.empty.add=進入下載頁安裝遊戲 version.empty.launch=沒有可啟動的遊戲。\n你可以前往「下載」頁面下載新遊戲,或在「實例清單」切換遊戲目錄。 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 bdf7c024f4..22b183a1d0 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1594,7 +1594,6 @@ update.preview.subtitle=启用此选项,你将可以提前获取 HMCL 的新 version=游戏 version.name=游戏实例名称 -version.cannot_read=读取游戏实例失败,无法进行自动安装 version.empty=没有游戏实例 version.empty.add=进入下载页安装游戏 version.empty.launch=没有可启动的游戏。\n你可以前往下载页面下载新游戏,或在“实例列表”中切换游戏文件夹。 From d638a53c59b962923fa0b654f05b53a1187ba5db Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:45:08 +0800 Subject: [PATCH 67/85] refactor: rename VersionJsonDownloadTask to GameInstanceJsonDownloadTask for consistency --- .../jackhuang/hmcl/download/forge/ForgeNewInstallTask.java | 4 ++-- .../org/jackhuang/hmcl/download/game/GameInstallTask.java | 4 ++-- ...sonDownloadTask.java => GameInstanceJsonDownloadTask.java} | 4 ++-- .../hmcl/download/neoforge/NeoForgeOldInstallTask.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/{VersionJsonDownloadTask.java => GameInstanceJsonDownloadTask.java} (93%) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java index 53f96e34e2..50b18ac8a3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java @@ -22,7 +22,7 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.forge.ForgeNewInstallProfile.Processor; import org.jackhuang.hmcl.download.game.GameLibrariesTask; -import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask; +import org.jackhuang.hmcl.download.game.GameInstanceJsonDownloadTask; import org.jackhuang.hmcl.game.Artifact; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.DownloadInfo; @@ -355,7 +355,7 @@ private Task patchDownloadMojangMappingsTask(Processor processor, Map { DownloadInfo mappings = fromNonNullJson(json, GameInstanceManifest.class) .getDownloads().get(DownloadType.CLIENT_MAPPINGS); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java index 3c54bb9101..8459751071 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstallTask.java @@ -37,7 +37,7 @@ public class GameInstallTask extends Task { private final DefaultDependencyManager dependencyManager; private final GameInstanceManifest manifest; private final GameRemoteVersion remote; - private final VersionJsonDownloadTask downloadTask; + private final GameInstanceJsonDownloadTask downloadTask; private final List> dependencies = new ArrayList<>(1); public GameInstallTask(DefaultDependencyManager dependencyManager, GameInstanceManifest manifest, GameRemoteVersion remoteVersion) { @@ -45,7 +45,7 @@ public GameInstallTask(DefaultDependencyManager dependencyManager, GameInstanceM this.gameRepository = dependencyManager.getGameRepository(); this.manifest = manifest; this.remote = remoteVersion; - this.downloadTask = new VersionJsonDownloadTask(remoteVersion.getGameVersion(), dependencyManager); + this.downloadTask = new GameInstanceJsonDownloadTask(remoteVersion.getGameVersion(), dependencyManager); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstanceJsonDownloadTask.java similarity index 93% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstanceJsonDownloadTask.java index 112295a150..046159feda 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameInstanceJsonDownloadTask.java @@ -32,14 +32,14 @@ * * @author huangyuhui */ -public final class VersionJsonDownloadTask extends Task { +public final class GameInstanceJsonDownloadTask extends Task { private final String gameVersion; private final DefaultDependencyManager dependencyManager; private final List> dependents = new ArrayList<>(1); private final List> dependencies = new ArrayList<>(1); private final VersionList gameVersionList; - public VersionJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) { + public GameInstanceJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) { this.gameVersion = gameVersion; this.dependencyManager = dependencyManager; this.gameVersionList = dependencyManager.getVersionList("game"); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java index d05d3b4e0e..3919125371 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java @@ -23,7 +23,7 @@ import org.jackhuang.hmcl.download.forge.ForgeNewInstallProfile; import org.jackhuang.hmcl.download.forge.ForgeNewInstallProfile.Processor; import org.jackhuang.hmcl.download.game.GameLibrariesTask; -import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask; +import org.jackhuang.hmcl.download.game.GameInstanceJsonDownloadTask; import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; @@ -333,7 +333,7 @@ private Task patchDownloadMojangMappingsTask(Processor processor, Map { DownloadInfo mappings = fromNonNullJson(json, GameInstanceManifest.class) .getDownloads().get(DownloadType.CLIENT_MAPPINGS); From 8d526e0abe75192c6580f9f1ba127fb5468a165b Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:48:36 +0800 Subject: [PATCH 68/85] refactor: add Gson TypeAdapter for GameInstanceID serialization --- .../jackhuang/hmcl/game/GameInstanceID.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java index c4dc6b5ff5..2b93994ee9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceID.java @@ -17,9 +17,20 @@ */ package org.jackhuang.hmcl.game; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; @NotNullByDefault +@JsonAdapter(GameInstanceID.Adapter.class) +@JsonSerializable public record GameInstanceID(String id) implements Comparable { public GameInstanceID { if (id.isEmpty()) { @@ -36,4 +47,22 @@ public int compareTo(GameInstanceID that) { public String toString() { return id; } + + static final class Adapter extends TypeAdapter<@Nullable GameInstanceID> { + + @Override + public @Nullable GameInstanceID read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + return new GameInstanceID(in.nextString()); + } + + @Override + public void write(JsonWriter out, @Nullable GameInstanceID value) throws IOException { + out.value(value != null ? value.id() : null); + } + } } From 4ba7f470b2e73203991d8474be904aba8c8b0bbd Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 21:51:43 +0800 Subject: [PATCH 69/85] refactor: update getInstallTask method signatures to accept GameInstanceID directly --- .../main/java/org/jackhuang/hmcl/game/ModpackHelper.java | 6 ++---- .../hmcl/ui/download/ModpackInstallWizardProvider.java | 4 ++-- HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 89ad1f7da9..a8d600a26c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -156,8 +156,7 @@ public static ModpackConfiguration readModpackConfiguration(Path file) throws } } - public static Task getInstallTask(HMCLGameRepository repository, ServerModpackManifest manifest, String name, Modpack modpack) { - GameInstanceID instanceId = new GameInstanceID(name); + public static Task getInstallTask(HMCLGameRepository repository, ServerModpackManifest manifest, GameInstanceID instanceId, Modpack modpack) { repository.markInstanceAsModpack(instanceId); ExceptionalRunnable success = () -> { @@ -201,8 +200,7 @@ public static Task getInstallManuallyCreatedModpackTask(Path zipFile, String }); } - public static Task getInstallTask(HMCLGameRepository repository, Path zipFile, String name, Modpack modpack, String iconUrl) { - GameInstanceID instanceId = new GameInstanceID(name); + public static Task getInstallTask(HMCLGameRepository repository, Path zipFile, GameInstanceID instanceId, Modpack modpack, String iconUrl) { repository.markInstanceAsModpack(instanceId); ExceptionalRunnable success = () -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index 41f25deef9..9a56fbc132 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -123,10 +123,10 @@ private Task finishModpackInstallingAsync(SettingsMap settings) { return null; } else { if (serverModpackManifest != null) { - return ModpackHelper.getInstallTask(repository, serverModpackManifest, name, modpack) + return ModpackHelper.getInstallTask(repository, serverModpackManifest, instanceId, modpack) .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(instanceId)); } else { - return ModpackHelper.getInstallTask(repository, selected, name, modpack, iconUrl) + return ModpackHelper.getInstallTask(repository, selected, instanceId, modpack, iconUrl) .thenRunAsync(Schedulers.javafx(), () -> repository.setSelectedInstance(instanceId)); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index c94fb35340..ad65466db5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -281,7 +281,7 @@ private void onRefreshedVersions(HMCLGameRepository repository) { Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(modpackFile)) .thenApplyAsync(encoding -> ModpackHelper.readModpackManifest(modpackFile, encoding)) .thenApplyAsync(modpack -> ModpackHelper - .getInstallTask(repository, modpackFile, modpack.getName(), modpack, null) + .getInstallTask(repository, modpackFile, new GameInstanceID(modpack.getName()), modpack, null) .executor()) .thenAcceptAsync(Schedulers.javafx(), executor -> { Controllers.taskDialog(executor, i18n("modpack.installing"), TaskCancellationAction.NO_CANCEL); From f6daa1981306e0386cfa2c4f046bcd7b644bbd57 Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 22:01:21 +0800 Subject: [PATCH 70/85] refactor: update LibraryAnalyzer calls to use GameInstanceManifest.Resolved directly --- .../jackhuang/hmcl/game/HMCLGameRepository.java | 9 +++++---- .../hmcl/game/HMCLModpackInstallTask.java | 6 +++--- .../hmcl/ui/download/AdditionalInstallersPage.java | 4 ++-- .../jackhuang/hmcl/ui/instances/DownloadPage.java | 6 +++--- .../org/jackhuang/hmcl/ui/instances/GameItem.java | 2 +- .../hmcl/ui/instances/InstallerListPage.java | 2 +- .../org/jackhuang/hmcl/addon/mod/ModManager.java | 2 +- .../hmcl/download/DefaultDependencyManager.java | 4 ++-- .../jackhuang/hmcl/download/LibraryAnalyzer.java | 14 ++++++-------- .../hmcl/modpack/mcbbs/McbbsModpackExportTask.java | 2 +- .../modrinth/ModrinthModpackExportTask.java | 2 +- .../modpack/multimc/MultiMCModpackExportTask.java | 2 +- .../modpack/server/ServerModpackExportTask.java | 2 +- 13 files changed, 28 insertions(+), 29 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 58fd037fed..ce31841101 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -537,7 +537,7 @@ public void applyDefaultIsolationSetting(GameInstanceID instanceId) { boolean isolated = switch (type) { case NEVER -> false; case ALWAYS -> true; - case MODDED -> LibraryAnalyzer.isModded(this, getInstanceManifest(instanceId).resolve(this)); + case MODDED -> LibraryAnalyzer.isModded(getResolvedInstanceManifest(instanceId)); }; if (isolated) { @@ -592,7 +592,8 @@ public Image getInstanceIconImage(@Nullable GameInstanceID instanceId) { GameInstanceIconType iconType = setting != null ? Lang.requireNonNullElse(setting.iconProperty().getValue(), GameInstanceIconType.DEFAULT) : GameInstanceIconType.DEFAULT; if (iconType == GameInstanceIconType.DEFAULT) { - GameInstanceManifest manifest = getResolvedInstanceManifest(instanceId).launchManifest(); + GameInstanceManifest.Resolved resolvedInstanceManifest = getResolvedInstanceManifest(instanceId); + GameInstanceManifest manifest = resolvedInstanceManifest.launchManifest(); Optional iconFile = getInstanceIconFile(instanceId); if (iconFile.isPresent()) { try { @@ -602,8 +603,8 @@ public Image getInstanceIconImage(@Nullable GameInstanceID instanceId) { } } - if (LibraryAnalyzer.isModded(this, manifest)) { - LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(manifest, null); + if (LibraryAnalyzer.isModded(resolvedInstanceManifest)) { + LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(resolvedInstanceManifest, null); if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.FABRIC)) return GameInstanceIconType.FABRIC.getIcon(); else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.QUILT)) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java index b1cd325584..e55d085f31 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLModpackInstallTask.java @@ -89,9 +89,9 @@ public List> getDependents() { @Override public void execute() throws Exception { String json = CompressingUtils.readTextZipEntry(zipFile, "minecraft/pack.json"); - GameInstanceManifest originalVersion = JsonUtils.GSON.fromJson(json, GameInstanceManifest.class).withId(instanceId).withJar(null); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(originalVersion, null); - Task libraryTask = Task.supplyAsync(() -> originalVersion); + GameInstanceManifest originalManifest = JsonUtils.GSON.fromJson(json, GameInstanceManifest.class).withId(instanceId).withJar(null); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(originalManifest, null); + Task libraryTask = Task.supplyAsync(() -> originalManifest); // reinstall libraries // libraries of Forge and OptiFine should be obtained by installation. for (LibraryAnalyzer.LibraryMark mark : analyzer) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java index d46bfcc5ed..c5b0354425 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AdditionalInstallersPage.java @@ -80,8 +80,8 @@ private String getVersion(String id) { @Override protected void reload() { - GameInstanceManifest standalone = repository.resolve(manifest).standaloneManifest(); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(standalone, repository.getGameVersion(standalone).orElse(null)); + GameInstanceManifest.Resolved resolvedManifest = repository.resolve(manifest); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolvedManifest, repository.getGameVersion(manifest).orElse(null)); String game = analyzer.getVersion(MINECRAFT).orElse(null); String currentGameVersion = Lang.nonNull(getVersion("game"), game); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadPage.java index 85857bb4cd..85325afba2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadPage.java @@ -272,13 +272,13 @@ protected DownloadPageSkin(DownloadPage control) { if (control.instanceReference.repository() != null && control.instanceReference.instanceId() != null) { HMCLGameRepository repository = control.instanceReference.repository(); - GameInstanceManifest game = repository.getResolvedInstanceManifest(control.instanceReference.instanceId()).standaloneManifest(); - String gameVersion = repository.getGameVersion(game).orElse(null); + GameInstanceManifest.Resolved resolvedManifest = repository.getResolvedInstanceManifest(control.instanceReference.instanceId()); + String gameVersion = repository.getGameVersion(resolvedManifest.unresolved()).orElse(null); if (gameVersion != null && control.versions.containsKey(gameVersion)) { List modVersions = control.versions.get(gameVersion); if (modVersions != null && !modVersions.isEmpty()) { - Set targetLoaders = LibraryAnalyzer.analyze(game, gameVersion).getModLoaders(); + Set targetLoaders = LibraryAnalyzer.analyze(resolvedManifest, gameVersion).getModLoaders(); resolve: for (RemoteAddon.Version modVersion : modVersions) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameItem.java index a5a9f6b02b..99594fa77f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameItem.java @@ -102,7 +102,7 @@ record Result(@Nullable String gameVersion, @Nullable String tag) { } StringBuilder libraries = new StringBuilder(Objects.requireNonNullElse(result.gameVersion, i18n("message.unknown"))); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), result.gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId), result.gameVersion); for (LibraryAnalyzer.LibraryMark mark : analyzer) { String libraryId = mark.getLibraryId(); String libraryVersion = mark.getLibraryVersion(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java index 528cc93814..06a5e28243 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/InstallerListPage.java @@ -72,7 +72,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID CompletableFuture.supplyAsync(() -> { gameVersion = repository.getGameVersion(manifest).orElse(null); - return LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); + return LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId), gameVersion); }).thenAcceptAsync(analyzer -> { itemsProperty().clear(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java index 79ca0ece44..ab60fac8b2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/ModManager.java @@ -181,7 +181,7 @@ public void refresh() throws IOException { localMods.clear(); try { - analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedInstanceManifest(instanceId).standaloneManifest(), null); + analyzer = LibraryAnalyzer.analyze(getRepository().getResolvedInstanceManifest(instanceId), null); } catch (NoSuchGameInstanceException e) { throw new IOException(e); } 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 d780f76b92..d3c41560f6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -108,9 +108,9 @@ public Task checkPatchCompletionAsync(GameInstanceManifest manifest, boolean if (gameVersion == null) return null; GameInstanceManifest original = repository.getInstanceManifest(manifest.id()); - GameInstanceManifest standalone = repository.resolve(original).standaloneManifest(); + GameInstanceManifest.Resolved resolvedInstanceManifest = repository.getResolvedInstanceManifest(manifest.id()); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(standalone, gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(resolvedInstanceManifest, gameVersion); for (LibraryAnalyzer.LibraryType type : LibraryAnalyzer.LibraryType.values()) { if (!analyzer.has(type)) continue; 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 de58d4697f..540b754c8f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -154,6 +154,10 @@ public GameInstanceManifest build() { return manifest; } + public static LibraryAnalyzer analyze(GameInstanceManifest.Resolved resolved, String gameVersion) { + return analyze(resolved.standaloneManifest(), gameVersion); + } + public static LibraryAnalyzer analyze(GameInstanceManifest manifest, String gameVersion) { if (manifest.inheritsFrom() != null) throw new IllegalArgumentException("LibraryAnalyzer can only analyze independent game version"); @@ -182,14 +186,8 @@ public static LibraryAnalyzer analyze(GameInstanceManifest manifest, String game return new LibraryAnalyzer(manifest, libraries); } - public static boolean isModded(GameRepository provider, GameInstanceManifest manifest) { - GameInstanceManifest resolvedVersion; - try { - resolvedVersion = manifest.resolve(provider); - } catch (NoSuchGameInstanceException e) { - return false; - } - String mainClass = resolvedVersion.mainClass(); + public static boolean isModded(GameInstanceManifest.Resolved resolved) { + String mainClass = resolved.launchManifest().mainClass(); return mainClass != null && (LAUNCH_WRAPPER_MAIN.equals(mainClass) || mainClass.startsWith("net.minecraftforge") || mainClass.startsWith("net.neoforged") diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java index 464487958f..bdbc683262 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackExportTask.java @@ -91,7 +91,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(instanceId) .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId), gameVersion); // Mcbbs manifest List addons = new ArrayList<>(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java index 68223a1def..25edbf60a0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/modrinth/ModrinthModpackExportTask.java @@ -173,7 +173,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(instanceId) .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId), gameVersion); Map dependencies = new HashMap<>(); dependencies.put("minecraft", gameVersion); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java index 631fc9242d..a27f9ca961 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackExportTask.java @@ -81,7 +81,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(instanceId) .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId), gameVersion); List components = new ArrayList<>(); components.add(new MultiMCManifest.MultiMCManifestComponent(true, false, MultiMCComponents.getComponent(MINECRAFT), gameVersion)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java index 3949d27c99..c2bcfcbe70 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/server/ServerModpackExportTask.java @@ -87,7 +87,7 @@ public void execute() throws Exception { String gameVersion = repository.getGameVersion(instanceId) .orElseThrow(() -> new IOException("Cannot parse the version of " + instanceId)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId).standaloneManifest(), gameVersion); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedInstanceManifest(instanceId), gameVersion); List addons = new ArrayList<>(); addons.add(new ServerModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion)); analyzer.getVersion(FORGE).ifPresent(forgeVersion -> From 6b6b332829ba5058d148c89c3af1ba51e655361c Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 6 Jul 2026 22:04:40 +0800 Subject: [PATCH 71/85] refactor: update GameInstancePatch methods to use direct property access --- .../org/jackhuang/hmcl/game/LogExporter.java | 1 - .../download/DefaultDependencyManager.java | 7 +-- .../hmcl/download/LibraryAnalyzer.java | 12 ++-- .../download/fabric/FabricInstallTask.java | 3 +- .../neoforge/NeoForgeInstallTask.java | 4 +- .../hmcl/game/GameInstancePatch.java | 57 ------------------- .../java/org/jackhuang/hmcl/game/Library.java | 1 - .../mcbbs/McbbsModpackLocalInstallTask.java | 2 +- 8 files changed, 14 insertions(+), 73 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java index efb5092eef..47ea469b3c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LogExporter.java @@ -17,7 +17,6 @@ */ package org.jackhuang.hmcl.game; -import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.IOUtils; import org.jackhuang.hmcl.util.io.Zipper; import org.jackhuang.hmcl.util.logging.Logger; 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 d3c41560f6..89d4264419 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -27,7 +27,6 @@ import org.jackhuang.hmcl.game.Artifact; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.GameInstanceManifest; -import org.jackhuang.hmcl.game.GameInstancePatch; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.io.FileUtils; @@ -121,10 +120,10 @@ public Task checkPatchCompletionAsync(GameInstanceManifest manifest, boolean Matcher matcher = Pattern.compile("^([0-9.]+)_(?HD_.+)$").matcher(optifineVersion); return matcher.find() ? matcher.group("optifine") : optifineVersion; }) - .orElseGet(() -> standalone.getPatches().stream() - .filter(patch -> "optifine".equals(patch.getId())) + .orElseGet(() -> resolvedInstanceManifest.standaloneManifest().getPatches().stream() + .filter(patch -> "optifine".equals(patch.id())) .findAny() - .map(GameInstancePatch::getVersion) + .map(gameInstancePatch -> gameInstancePatch.version()) .orElse(null)); boolean needsReInstallation = manifest.getLibraries().stream() 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 540b754c8f..a11e51ba5e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -98,7 +98,7 @@ public boolean hasModLoader() { public boolean hasModLauncher() { return LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(manifest.mainClass()) || manifest.getPatches().stream().anyMatch( - patch -> LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(patch.getMainClass()) + patch -> LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(patch.mainClass()) ); } @@ -144,7 +144,7 @@ public LibraryAnalyzer removeLibrary(String libraryId) { if (!has(libraryId)) return this; GameInstanceManifest manifest = removingMatchedLibrary(this.manifest, libraryId); this.manifest = manifest.withPatches(this.manifest.getPatches().stream() - .filter(patch -> !libraryId.equals(patch.getId())) + .filter(patch -> !libraryId.equals(patch.id())) .map(patch -> removingMatchedLibrary(patch, libraryId)) .collect(Collectors.toList())); return this; @@ -180,7 +180,7 @@ public static LibraryAnalyzer analyze(GameInstanceManifest manifest, String game for (GameInstancePatch patch : manifest.getPatches()) { if (patch.isHidden()) continue; - libraries.put(patch.getId(), pair(null, patch.getVersion())); + libraries.put(patch.id(), pair(null, patch.version())); } return new LibraryAnalyzer(manifest, libraries); @@ -312,11 +312,11 @@ private String scanVersion(GameInstanceManifest manifest) { } private String scanPatch(GameInstancePatch patch) { - Optional optArgument = patch.getArguments(); - if (!optArgument.isPresent()) { + Arguments optArgument = patch.arguments(); + if (optArgument == null) { return null; } - List gameArguments = optArgument.get().game(); + List gameArguments = optArgument.game(); if (gameArguments == null) { return null; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java index 470767a261..72c8764cee 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/fabric/FabricInstallTask.java @@ -22,6 +22,7 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.UnsupportedInstallationException; +import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.Artifact; import org.jackhuang.hmcl.game.GameInstanceManifest; @@ -93,7 +94,7 @@ public void execute() throws IOException { setResult(getPatch(fabricInfo, remote.getGameVersion(), remote.getSelfVersion())); - dependencies.add(new org.jackhuang.hmcl.download.game.GameLibrariesTask(dependencyManager, manifest, true, getResult().getLibraries())); + dependencies.add(new GameLibrariesTask(dependencyManager, manifest, true, getResult().getLibraries())); } private GameInstancePatch getPatch(FabricInfo fabricInfo, String gameVersion, String loaderVersion) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java index b5f6bc6f59..3467a195d3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeInstallTask.java @@ -111,12 +111,12 @@ public static Task install(DefaultDependencyManager dependenc if (!gameVersion.get().equals(profile.getMinecraft())) throw new VersionMismatchException(profile.getMinecraft(), gameVersion.get()); return new ForgeNewInstallTask(dependencyManager, version, modifyNeoForgeOldVersion(gameVersion.get(), profile.getVersion()), installer).thenApplyAsync(neoForgeVersion -> { - if (!neoForgeVersion.getId().equals(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) || neoForgeVersion.getVersion() == null) { + if (!neoForgeVersion.id().equals(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) || neoForgeVersion.version() == null) { throw new IOException("Invalid neoforge version."); } return neoForgeVersion.withId(LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId()) .withVersion( - removePrefix(neoForgeVersion.getVersion().replace(LibraryAnalyzer.LibraryType.FORGE.getPatchId(), ""), "-") + removePrefix(neoForgeVersion.version().replace(LibraryAnalyzer.LibraryType.FORGE.getPatchId(), ""), "-") ); }); } else if (LibraryAnalyzer.LibraryType.NEO_FORGE.getPatchId().equals(installProfile.get("profile")) || "NeoForge".equals(installProfile.get("profile"))) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 1d6bb6874a..7f2449a0ce 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -30,14 +30,12 @@ import org.jetbrains.annotations.Unmodifiable; import java.time.Instant; -import java.time.ZoneId; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; import java.util.List; import java.util.Map; -import java.util.Optional; @NotNullByDefault public record GameInstancePatch( @@ -162,71 +160,16 @@ public static GameInstancePatch fromManifest( null); } - /// Returns the patch id. - public @Nullable String getId() { - return id; - } - - /// Returns the patch version. - public @Nullable String getVersion() { - return version; - } - /// Returns the patch priority. public int getPriority() { return priority == null ? Integer.MIN_VALUE : priority; } - /// Returns legacy game arguments. - public Optional getMinecraftArguments() { - return Optional.ofNullable(minecraftArguments); - } - - /// Returns structured arguments. - public Optional getArguments() { - return Optional.ofNullable(arguments); - } - - /// Returns the main class. - public @Nullable String getMainClass() { - return mainClass; - } - - /// Returns the jar id. - public @Nullable String getJar() { - return jar == null ? null : jar.id(); - } - - /// Returns the asset index. - public @Nullable AssetIndexInfo getAssetIndex() { - return assetIndex; - } - - /// Returns the Java version. - public @Nullable GameJavaVersion getJavaVersion() { - return javaVersion; - } - /// Returns libraries. public List getLibraries() { return libraries == null ? List.of() : libraries; } - /// Returns compatibility rules. - public List getCompatibilityRules() { - return compatibilityRules == null ? List.of() : compatibilityRules; - } - - /// Returns downloads. - public Map getDownloads() { - return downloads == null ? Map.of() : downloads; - } - - /// Returns logging metadata. - public Map getLogging() { - return logging == null ? Map.of() : logging; - } - /// Returns whether the patch is hidden. public boolean isHidden() { return hidden != null && hidden; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java index 1a18496979..6dd68a60f8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java @@ -78,7 +78,6 @@ public class Library implements Comparable, Validation { } } - /// Parses a library from either the standard version JSON format or TLauncher library format. public static Library fromJson(JsonObject json) { if (json == null) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java index 996153c2b8..110a1a82d3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/mcbbs/McbbsModpackLocalInstallTask.java @@ -106,7 +106,7 @@ public List> getDependencies() { @Override public void execute() throws Exception { GameInstanceManifest instanceManifest = repository.getInstanceManifest(instanceId); - Optional mcbbsPatch = instanceManifest.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.getId())).findFirst(); + Optional mcbbsPatch = instanceManifest.getPatches().stream().filter(patch -> PATCH_NAME.equals(patch.id())).findFirst(); if (!update) { GameInstancePatch patch = new GameInstancePatch(PATCH_NAME).withLibraries(manifest.getLibraries()); dependencies.add(repository.saveAsync(instanceManifest.addPatch(patch))); From 0e7f5d942562cef2a6dc98018413a1f7d4c8bd4c Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 16:06:14 +0800 Subject: [PATCH 72/85] refactor: update saveAsync method to maintain patches in GameInstanceManifest --- .../jackhuang/hmcl/game/DefaultGameRepository.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 7fcbf5889e..64461ea8c4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -20,6 +20,7 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.addon.mod.ModManager; import org.jackhuang.hmcl.addon.resourcepack.ResourcePackManager; +import org.jackhuang.hmcl.download.MaintainTask; import org.jackhuang.hmcl.event.*; import org.jackhuang.hmcl.modpack.ModpackConfiguration; import org.jackhuang.hmcl.task.Task; @@ -574,14 +575,18 @@ protected Path reconstructAssets(GameInstanceID instanceId, String assetId) thro public Task saveAsync(GameInstanceManifest instanceManifest) { return Task.supplyAsync(() -> { - Path json = getInstanceJson(instanceManifest.id()).toAbsolutePath(); + GameInstanceManifest savedManifest = instanceManifest.isResolvedPreservingPatches() + ? MaintainTask.maintainPreservingPatches(this, instanceManifest) + : instanceManifest; + + Path json = getInstanceJson(savedManifest.id()).toAbsolutePath(); Files.createDirectories(json.getParent()); - JsonUtils.writeToJsonFile(json, instanceManifest); + JsonUtils.writeToJsonFile(json, savedManifest); Status currentStatus = status; - currentStatus.instances.put(instanceManifest.id(), new InstanceHolder(currentStatus, instanceManifest.id(), instanceManifest)); + currentStatus.instances.put(savedManifest.id(), new InstanceHolder(currentStatus, savedManifest.id(), savedManifest)); gameVersions.clear(); - return instanceManifest; + return savedManifest; }); } From 3d9db5336bb73f0f71cbfc68cd8e1f35183f9604 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 16:08:44 +0800 Subject: [PATCH 73/85] refactor: update game version retrieval to use resolved instance manifest directly --- .../main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index ce31841101..020da9f7fa 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -593,7 +593,6 @@ public Image getInstanceIconImage(@Nullable GameInstanceID instanceId) { if (iconType == GameInstanceIconType.DEFAULT) { GameInstanceManifest.Resolved resolvedInstanceManifest = getResolvedInstanceManifest(instanceId); - GameInstanceManifest manifest = resolvedInstanceManifest.launchManifest(); Optional iconFile = getInstanceIconFile(instanceId); if (iconFile.isPresent()) { try { @@ -623,7 +622,7 @@ else if (libraryAnalyzer.has(LibraryAnalyzer.LibraryType.OPTIFINE)) return GameInstanceIconType.OPTIFINE.getIcon(); } - String gameVersion = getGameVersion(manifest).orElse(null); + String gameVersion = getGameVersion(resolvedInstanceManifest.launchManifest()).orElse(null); if (gameVersion != null) { GameVersionNumber versionNumber = GameVersionNumber.asGameVersion(gameVersion); if (versionNumber.isAprilFools()) { From 612ee2caa7fbfeecd70f16673ebc4f21b003392b Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 16:09:55 +0800 Subject: [PATCH 74/85] refactor: update getResolvedInstanceManifest to use current status for instance retrieval --- .../java/org/jackhuang/hmcl/game/DefaultGameRepository.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 64461ea8c4..65109a8903 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -277,14 +277,16 @@ public GameInstanceManifest getInstanceManifest(GameInstanceID instanceId) throw @Override public GameInstanceManifest.Resolved getResolvedInstanceManifest(GameInstanceID instanceId) throws NoSuchGameInstanceException { - InstanceHolder instanceHolder = status.instances.get(instanceId); + Status currentStatus = status; + + InstanceHolder instanceHolder = currentStatus.instances.get(instanceId); if (instanceHolder == null) { throw new NoSuchGameInstanceException(instanceId); } GameInstanceManifest.Resolved resolvedManifest = instanceHolder.resolvedManifest; if (resolvedManifest == null) { - resolvedManifest = resolve(instanceHolder.manifest); + resolvedManifest = currentStatus.resolve(instanceHolder.manifest, new HashSet<>()); instanceHolder.resolvedManifest = resolvedManifest; } return resolvedManifest; From ac929e4e643c603cd4a0bb7193a27899c4878803 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 16:23:28 +0800 Subject: [PATCH 75/85] refactor: remove unused clearPatches and removePatchById methods from GameInstanceManifest --- .../hmcl/game/GameInstanceManifest.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 771d0e6949..a918411cb9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -592,26 +592,6 @@ public GameInstanceManifest addPatches(@Nullable List additio return withPatches(patches); } - /// Returns a manifest copy without pending patches. - public GameInstanceManifest clearPatches() { - return withPatches(null); - } - - /// Returns a manifest copy without the patch with the given id. - public GameInstanceManifest removePatchById(String patchId) { - if (patches == null) { - return this; - } - - List filtered = new ArrayList<>(); - for (GameInstancePatch patch : patches) { - if (!patchId.equals(patch.id())) { - filtered.add(patch); - } - } - return withPatches(filtered); - } - /// Returns whether this manifest has a patch with the given id. public boolean hasPatch(String patchId) { return patches != null && patches.stream().anyMatch(patch -> patchId.equals(patch.id())); From 7d73f1ceee729e38235cfb6588296190189ab51f Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 16:27:49 +0800 Subject: [PATCH 76/85] refactor: update GameInstanceManifest setters to preserve raw JSON fields --- .../hmcl/game/GameInstanceManifest.java | 80 ++++++++++++++++--- .../hmcl/game/GameInstanceManifestTest.java | 22 +++++ 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index a918411cb9..9f1796b08b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -767,17 +767,35 @@ public void setJar(@Nullable GameInstanceID jar) { public void setMinecraftArguments(@Nullable String minecraftArguments) { this.minecraftArguments = minecraftArguments; - rawJson = null; + if (rawJson != null) { + if (minecraftArguments != null) { + rawJson.addProperty("minecraftArguments", minecraftArguments); + } else { + rawJson.remove("minecraftArguments"); + } + } } public void setArguments(@Nullable Arguments arguments) { this.arguments = arguments; - rawJson = null; + if (rawJson != null) { + if (arguments != null) { + rawJson.add("arguments", JsonUtils.GSON.toJsonTree(arguments)); + } else { + rawJson.remove("arguments"); + } + } } public void setMainClass(@Nullable String mainClass) { this.mainClass = mainClass; - rawJson = null; + if (rawJson != null) { + if (mainClass != null) { + rawJson.addProperty("mainClass", mainClass); + } else { + rawJson.remove("mainClass"); + } + } } public void setInheritsFrom(@Nullable GameInstanceID inheritsFrom) { @@ -793,32 +811,76 @@ public void setInheritsFrom(@Nullable GameInstanceID inheritsFrom) { public void setAssetIndex(@Nullable AssetIndexInfo assetIndex) { this.assetIndex = assetIndex; - rawJson = null; + if (rawJson != null) { + if (assetIndex != null) { + rawJson.add("assetIndex", JsonUtils.GSON.toJsonTree(assetIndex)); + } else { + rawJson.remove("assetIndex"); + } + } } public void setJavaVersion(@Nullable GameJavaVersion javaVersion) { this.javaVersion = javaVersion; - rawJson = null; + if (rawJson != null) { + if (javaVersion != null) { + rawJson.add("javaVersion", JsonUtils.GSON.toJsonTree(javaVersion)); + } else { + rawJson.remove("javaVersion"); + } + } } public void setLibraries(@Nullable List libraries) { this.libraries = libraries == null ? null : List.copyOf(libraries); - rawJson = null; + if (rawJson != null) { + if (this.libraries != null) { + rawJson.add("libraries", JsonUtils.GSON.toJsonTree(this.libraries)); + } else { + rawJson.remove("libraries"); + } + } } public void setDownloads(@Nullable Map downloads) { this.downloads = downloads == null ? null : Map.copyOf(downloads); - rawJson = null; + if (rawJson != null) { + if (this.downloads != null) { + JsonObject downloadsObject = new JsonObject(); + for (Map.Entry entry : this.downloads.entrySet()) { + downloadsObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + rawJson.add("downloads", downloadsObject); + } else { + rawJson.remove("downloads"); + } + } } public void setLogging(@Nullable Map logging) { this.logging = logging == null ? null : Map.copyOf(logging); - rawJson = null; + if (rawJson != null) { + if (this.logging != null) { + JsonObject loggingObject = new JsonObject(); + for (Map.Entry entry : this.logging.entrySet()) { + loggingObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + rawJson.add("logging", loggingObject); + } else { + rawJson.remove("logging"); + } + } } public void setRoot(@Nullable Boolean root) { this.root = root; - rawJson = null; + if (rawJson != null) { + if (root != null) { + rawJson.addProperty("root", root); + } else { + rawJson.remove("root"); + } + } } public void setPatches(@Nullable List patches) { diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java index a0fe2be6e0..605fff5f56 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java @@ -61,6 +61,28 @@ public void testRootManifestWithPatchesUsesPatchView() throws NoSuchGameInstance assertEquals(List.of(patch("patch", null)), resolved.standaloneManifest().getPatches()); } + /// Manifest edits update known raw JSON fields without discarding unknown fields. + @Test + public void testRawJsonIsPreservedWhenUpdatingKnownFields() { + JsonObject json = new JsonObject(); + json.addProperty("id", "example"); + json.addProperty("root", false); + json.addProperty("unknownField", "value"); + + GameInstanceManifest manifest = GameInstanceManifest.fromJson(json, true) + .withRoot(true) + .withMainClass("example.Main"); + + JsonObject updatedJson = manifest.toJsonObject(); + assertEquals("value", updatedJson.get("unknownField").getAsString()); + assertEquals(true, updatedJson.get("root").getAsBoolean()); + assertEquals("example.Main", updatedJson.get("mainClass").getAsString()); + + JsonObject rootRemovedJson = manifest.withRoot(null).toJsonObject(); + assertEquals("value", rootRemovedJson.get("unknownField").getAsString()); + assertFalse(rootRemovedJson.has("root")); + } + /// Creates a minimal manifest for tests. private static GameInstanceManifest manifest( String id, From 0ce83504eebc6b3ea7a335f72f2c1bd19dd63c7d Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 16:40:05 +0800 Subject: [PATCH 77/85] refactor: replace Map with ImmutableSequencedMap for downloads and logging in GameInstanceManifest --- .../hmcl/game/GameInstanceManifest.java | 13 +++++----- .../hmcl/util/ImmutableSequencedMap.java | 9 +++++-- .../hmcl/game/GameInstanceManifestTest.java | 25 +++++++++++++++++++ 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 9f1796b08b..6752b8cf90 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -22,6 +22,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import org.jackhuang.hmcl.util.Constants; +import org.jackhuang.hmcl.util.ImmutableSequencedMap; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.InstantTypeAdapter; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -233,7 +234,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } case "downloads" -> { if (value instanceof JsonObject obj) { - Map map = new EnumMap<>(DownloadType.class); + var map = new LinkedHashMap(); for (Map.Entry downloadEntry : obj.entrySet()) { try { DownloadType type = DownloadType.valueOf(downloadEntry.getKey()); @@ -241,12 +242,12 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } catch (IllegalArgumentException ignored) { } } - builder.downloads = Collections.unmodifiableMap(map); + builder.downloads = ImmutableSequencedMap.wrap(map); } } case "logging" -> { if (value instanceof JsonObject obj) { - Map map = new EnumMap<>(DownloadType.class); + var map = new LinkedHashMap(); for (Map.Entry loggingEntry : obj.entrySet()) { try { DownloadType type = DownloadType.valueOf(loggingEntry.getKey()); @@ -254,7 +255,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t } catch (IllegalArgumentException ignored) { } } - builder.logging = Collections.unmodifiableMap(map); + builder.logging = ImmutableSequencedMap.wrap(map); } } case "type" -> { @@ -843,7 +844,7 @@ public void setLibraries(@Nullable List libraries) { } public void setDownloads(@Nullable Map downloads) { - this.downloads = downloads == null ? null : Map.copyOf(downloads); + this.downloads = downloads == null ? null : ImmutableSequencedMap.copyOf(downloads); if (rawJson != null) { if (this.downloads != null) { JsonObject downloadsObject = new JsonObject(); @@ -858,7 +859,7 @@ public void setDownloads(@Nullable Map downloads) { } public void setLogging(@Nullable Map logging) { - this.logging = logging == null ? null : Map.copyOf(logging); + this.logging = logging == null ? null : ImmutableSequencedMap.copyOf(logging); if (rawJson != null) { if (this.logging != null) { JsonObject loggingObject = new JsonObject(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java index 2463ec7ae5..9763bdae68 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java @@ -65,12 +65,17 @@ public final class ImmutableSequencedMap the type of values /// @param map the source map whose entries are copied /// @return an [ImmutableSequencedMap] with the same entries and order as {@code map} + @SuppressWarnings("unchecked") public static ImmutableSequencedMap copyOf(Map map) { + if (map instanceof ImmutableSequencedMap) { + return (ImmutableSequencedMap) map; + } + if (map.isEmpty()) { return of(); - } else { - return new ImmutableSequencedMap<>(new LinkedHashMap<>(map)); } + + return new ImmutableSequencedMap<>(new LinkedHashMap<>(map)); } /// Wraps the given [LinkedHashMap] directly without copying. diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java index 605fff5f56..6011fc7f57 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java @@ -83,6 +83,31 @@ public void testRawJsonIsPreservedWhenUpdatingKnownFields() { assertFalse(rootRemovedJson.has("root")); } + /// Download maps keep the JSON entry order after parsing and rebuilding. + @Test + public void testDownloadMapOrderIsPreserved() { + JsonObject serverDownload = new JsonObject(); + serverDownload.addProperty("url", "server"); + JsonObject clientDownload = new JsonObject(); + clientDownload.addProperty("url", "client"); + + JsonObject downloads = new JsonObject(); + downloads.add("SERVER", serverDownload); + downloads.add("CLIENT", clientDownload); + + JsonObject json = new JsonObject(); + json.addProperty("id", "example"); + json.add("downloads", downloads); + + GameInstanceManifest manifest = GameInstanceManifest.fromJson(json, true); + assertEquals(List.of(DownloadType.SERVER, DownloadType.CLIENT), List.copyOf(manifest.getDownloads().keySet())); + + JsonObject updatedDownloads = manifest.withDownloads(manifest.getDownloads()) + .toJsonObject() + .getAsJsonObject("downloads"); + assertEquals(List.of("SERVER", "CLIENT"), List.copyOf(updatedDownloads.keySet())); + } + /// Creates a minimal manifest for tests. private static GameInstanceManifest manifest( String id, From 93d93100a40992a0cc3b90f4c0968e0386b0f14e Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 16:53:24 +0800 Subject: [PATCH 78/85] refactor: update GameInstancePatch to use ImmutableSequencedMap for downloads and logging --- .../hmcl/game/GameInstancePatch.java | 466 +++++++++++++++--- .../hmcl/game/GameInstancePatchTest.java | 83 ++++ 2 files changed, 480 insertions(+), 69 deletions(-) create mode 100644 HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 7f2449a0ce..6a67aa6378 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -22,6 +22,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; +import org.jackhuang.hmcl.util.ImmutableSequencedMap; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.InstantTypeAdapter; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -32,10 +33,10 @@ import java.time.Instant; import java.time.ZoneOffset; import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; @NotNullByDefault public record GameInstancePatch( @@ -177,111 +178,438 @@ public boolean isHidden() { /// Returns a patch copy with the given id. public GameInstancePatch withId(@Nullable String id) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (Objects.equals(this.id, id)) { + return this; + } + + Builder builder = new Builder(this); + builder.setId(id); + return builder.toPatch(); } /// Returns a patch copy with the given version. public GameInstancePatch withVersion(@Nullable String version) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (Objects.equals(this.version, version)) { + return this; + } + + Builder builder = new Builder(this); + builder.setVersion(version); + return builder.toPatch(); } /// Returns a patch copy with the given priority. public GameInstancePatch withPriority(@Nullable Integer priority) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (Objects.equals(this.priority, priority)) { + return this; + } + + Builder builder = new Builder(this); + builder.setPriority(priority); + return builder.toPatch(); } /// Returns a patch copy with the given jar id. public GameInstancePatch withJar(@Nullable GameInstanceID jar) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, - jar, assetIndex, assets, complianceLevel, javaVersion, - libraries, compatibilityRules, downloads, logging, type, time, releaseTime, minimumLauncherVersion, - hidden); + if (Objects.equals(this.jar, jar)) { + return this; + } + + Builder builder = new Builder(this); + builder.setJar(jar); + return builder.toPatch(); } /// Returns a patch copy with the given libraries. public GameInstancePatch withLibraries(@Nullable List libraries) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (this.libraries == libraries) { + return this; + } + + Builder builder = new Builder(this); + builder.setLibraries(libraries); + return builder.toPatch(); } /// Returns a patch copy with the given Java version. public GameInstancePatch withJavaVersion(@Nullable GameJavaVersion javaVersion) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (this.javaVersion == javaVersion) { + return this; + } + + Builder builder = new Builder(this); + builder.setJavaVersion(javaVersion); + return builder.toPatch(); } /// Returns a patch copy with the given arguments. public GameInstancePatch withArguments(@Nullable Arguments arguments) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (this.arguments == arguments) { + return this; + } + + Builder builder = new Builder(this); + builder.setArguments(arguments); + return builder.toPatch(); } /// Returns a patch copy with the given main class. public GameInstancePatch withMainClass(@Nullable String mainClass) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (Objects.equals(this.mainClass, mainClass)) { + return this; + } + + Builder builder = new Builder(this); + builder.setMainClass(mainClass); + return builder.toPatch(); } /// Returns a patch copy with the given asset index. public GameInstancePatch withAssetIndex(@Nullable AssetIndexInfo assetIndex) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (this.assetIndex == assetIndex) { + return this; + } + + Builder builder = new Builder(this); + builder.setAssetIndex(assetIndex); + return builder.toPatch(); } /// Returns a patch copy with the given downloads. public GameInstancePatch withDownload(@Nullable Map downloads) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (this.downloads == downloads) { + return this; + } + + Builder builder = new Builder(this); + builder.setDownloads(downloads); + return builder.toPatch(); } /// Returns a patch copy with the given logging metadata. public GameInstancePatch withLogging(@Nullable Map logging) { - return copy(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, jar, assetIndex, - assets, complianceLevel, javaVersion, libraries, compatibilityRules, downloads, logging, type, time, - releaseTime, minimumLauncherVersion, hidden); + if (this.logging == logging) { + return this; + } + + Builder builder = new Builder(this); + builder.setLogging(logging); + return builder.toPatch(); } - private static GameInstancePatch copy( - @Nullable String id, - @Nullable String version, - @Nullable Integer priority, - @Nullable String minecraftArguments, - @Nullable Arguments arguments, - @Nullable String mainClass, - @Nullable String inheritsFrom, - @Nullable GameInstanceID jar, - @Nullable AssetIndexInfo assetIndex, - @Nullable String assets, - @Nullable Integer complianceLevel, - @Nullable GameJavaVersion javaVersion, - @Nullable List libraries, - @Nullable List compatibilityRules, - @Nullable Map downloads, - @Nullable Map logging, - @Nullable ReleaseType type, - @Nullable Instant time, - @Nullable Instant releaseTime, - @Nullable Integer minimumLauncherVersion, - @Nullable Boolean hidden) { - return new GameInstancePatch(id, version, priority, minecraftArguments, arguments, mainClass, inheritsFrom, - jar, assetIndex, assets, complianceLevel, javaVersion, - libraries == null ? null : List.copyOf(libraries), - compatibilityRules == null ? null : List.copyOf(compatibilityRules), - downloads == null ? null : Map.copyOf(downloads), - logging == null ? null : Map.copyOf(logging), - type, time, releaseTime, minimumLauncherVersion, hidden, null); + private static final class Builder { + private @Nullable String id; + private @Nullable String version; + private @Nullable Integer priority; + private @Nullable String minecraftArguments; + private @Nullable Arguments arguments; + private @Nullable String mainClass; + private @Nullable String inheritsFrom; + private @Nullable GameInstanceID jar; + private @Nullable AssetIndexInfo assetIndex; + private @Nullable String assets; + private @Nullable Integer complianceLevel; + private @Nullable GameJavaVersion javaVersion; + private @Nullable @Unmodifiable List libraries; + private @Nullable @Unmodifiable List compatibilityRules; + private @Nullable @Unmodifiable Map downloads; + private @Nullable @Unmodifiable Map logging; + private @Nullable ReleaseType type; + private @Nullable Instant time; + private @Nullable Instant releaseTime; + private @Nullable Integer minimumLauncherVersion; + private @Nullable Boolean hidden; + private @Nullable JsonObject rawJson; + + private Builder(GameInstancePatch patch) { + this.id = patch.id; + this.version = patch.version; + this.priority = patch.priority; + this.minecraftArguments = patch.minecraftArguments; + this.arguments = patch.arguments; + this.mainClass = patch.mainClass; + this.inheritsFrom = patch.inheritsFrom; + this.jar = patch.jar; + this.assetIndex = patch.assetIndex; + this.assets = patch.assets; + this.complianceLevel = patch.complianceLevel; + this.javaVersion = patch.javaVersion; + this.libraries = patch.libraries; + this.compatibilityRules = patch.compatibilityRules; + this.downloads = patch.downloads; + this.logging = patch.logging; + this.type = patch.type; + this.time = patch.time; + this.releaseTime = patch.releaseTime; + this.minimumLauncherVersion = patch.minimumLauncherVersion; + this.hidden = patch.hidden; + this.rawJson = patch.rawJson != null ? patch.rawJson.deepCopy() : null; + } + + private void setId(@Nullable String id) { + this.id = id; + if (rawJson != null) { + if (id != null) { + rawJson.addProperty("id", id); + } else { + rawJson.remove("id"); + } + } + } + + private void setVersion(@Nullable String version) { + this.version = version; + if (rawJson != null) { + if (version != null) { + rawJson.addProperty("version", version); + } else { + rawJson.remove("version"); + } + } + } + + private void setPriority(@Nullable Integer priority) { + this.priority = priority; + if (rawJson != null) { + if (priority != null) { + rawJson.addProperty("priority", priority); + } else { + rawJson.remove("priority"); + } + } + } + + private void setMinecraftArguments(@Nullable String minecraftArguments) { + this.minecraftArguments = minecraftArguments; + if (rawJson != null) { + if (minecraftArguments != null) { + rawJson.addProperty("minecraftArguments", minecraftArguments); + } else { + rawJson.remove("minecraftArguments"); + } + } + } + + private void setArguments(@Nullable Arguments arguments) { + this.arguments = arguments; + if (rawJson != null) { + if (arguments != null) { + rawJson.add("arguments", JsonUtils.GSON.toJsonTree(arguments)); + } else { + rawJson.remove("arguments"); + } + } + } + + private void setMainClass(@Nullable String mainClass) { + this.mainClass = mainClass; + if (rawJson != null) { + if (mainClass != null) { + rawJson.addProperty("mainClass", mainClass); + } else { + rawJson.remove("mainClass"); + } + } + } + + private void setInheritsFrom(@Nullable String inheritsFrom) { + this.inheritsFrom = inheritsFrom; + if (rawJson != null) { + if (inheritsFrom != null) { + rawJson.addProperty("inheritsFrom", inheritsFrom); + } else { + rawJson.remove("inheritsFrom"); + } + } + } + + private void setJar(@Nullable GameInstanceID jar) { + this.jar = jar; + if (rawJson != null) { + if (jar != null) { + rawJson.addProperty("jar", jar.toString()); + } else { + rawJson.remove("jar"); + } + } + } + + private void setAssetIndex(@Nullable AssetIndexInfo assetIndex) { + this.assetIndex = assetIndex; + if (rawJson != null) { + if (assetIndex != null) { + rawJson.add("assetIndex", JsonUtils.GSON.toJsonTree(assetIndex)); + } else { + rawJson.remove("assetIndex"); + } + } + } + + private void setAssets(@Nullable String assets) { + this.assets = assets; + if (rawJson != null) { + if (assets != null) { + rawJson.addProperty("assets", assets); + } else { + rawJson.remove("assets"); + } + } + } + + private void setComplianceLevel(@Nullable Integer complianceLevel) { + this.complianceLevel = complianceLevel; + if (rawJson != null) { + if (complianceLevel != null) { + rawJson.addProperty("complianceLevel", complianceLevel); + } else { + rawJson.remove("complianceLevel"); + } + } + } + + private void setJavaVersion(@Nullable GameJavaVersion javaVersion) { + this.javaVersion = javaVersion; + if (rawJson != null) { + if (javaVersion != null) { + rawJson.add("javaVersion", JsonUtils.GSON.toJsonTree(javaVersion)); + } else { + rawJson.remove("javaVersion"); + } + } + } + + private void setLibraries(@Nullable List libraries) { + this.libraries = libraries == null ? null : List.copyOf(libraries); + if (rawJson != null) { + if (this.libraries != null) { + rawJson.add("libraries", JsonUtils.GSON.toJsonTree(this.libraries)); + } else { + rawJson.remove("libraries"); + } + } + } + + private void setCompatibilityRules(@Nullable List compatibilityRules) { + this.compatibilityRules = compatibilityRules == null ? null : List.copyOf(compatibilityRules); + if (rawJson != null) { + if (this.compatibilityRules != null) { + rawJson.add("compatibilityRules", JsonUtils.GSON.toJsonTree(this.compatibilityRules)); + } else { + rawJson.remove("compatibilityRules"); + } + } + } + + private void setDownloads(@Nullable Map downloads) { + this.downloads = downloads == null ? null : ImmutableSequencedMap.copyOf(downloads); + if (rawJson != null) { + if (this.downloads != null) { + JsonObject downloadsObject = new JsonObject(); + for (Map.Entry entry : this.downloads.entrySet()) { + downloadsObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + rawJson.add("downloads", downloadsObject); + } else { + rawJson.remove("downloads"); + } + } + } + + private void setLogging(@Nullable Map logging) { + this.logging = logging == null ? null : ImmutableSequencedMap.copyOf(logging); + if (rawJson != null) { + if (this.logging != null) { + JsonObject loggingObject = new JsonObject(); + for (Map.Entry entry : this.logging.entrySet()) { + loggingObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + } + rawJson.add("logging", loggingObject); + } else { + rawJson.remove("logging"); + } + } + } + + private void setType(@Nullable ReleaseType type) { + this.type = type; + if (rawJson != null) { + if (type != null) { + rawJson.addProperty("type", type.name()); + } else { + rawJson.remove("type"); + } + } + } + + private void setTime(@Nullable Instant time) { + this.time = time; + if (rawJson != null) { + if (time != null) { + rawJson.addProperty("time", InstantTypeAdapter.serializeToString(time, ZoneOffset.UTC)); + } else { + rawJson.remove("time"); + } + } + } + + private void setReleaseTime(@Nullable Instant releaseTime) { + this.releaseTime = releaseTime; + if (rawJson != null) { + if (releaseTime != null) { + rawJson.addProperty("releaseTime", InstantTypeAdapter.serializeToString(releaseTime, ZoneOffset.UTC)); + } else { + rawJson.remove("releaseTime"); + } + } + } + + private void setMinimumLauncherVersion(@Nullable Integer minimumLauncherVersion) { + this.minimumLauncherVersion = minimumLauncherVersion; + if (rawJson != null) { + if (minimumLauncherVersion != null) { + rawJson.addProperty("minimumLauncherVersion", minimumLauncherVersion); + } else { + rawJson.remove("minimumLauncherVersion"); + } + } + } + + private void setHidden(@Nullable Boolean hidden) { + this.hidden = hidden; + if (rawJson != null) { + if (hidden != null) { + rawJson.addProperty("hidden", hidden); + } else { + rawJson.remove("hidden"); + } + } + } + + private GameInstancePatch toPatch() { + return new GameInstancePatch( + id, + version, + priority, + minecraftArguments, + arguments, + mainClass, + inheritsFrom, + jar, + assetIndex, + assets, + complianceLevel, + javaVersion, + libraries, + compatibilityRules, + downloads, + logging, + type, + time, + releaseTime, + minimumLauncherVersion, + hidden, + rawJson); + } } public static GameInstancePatch fromJson(JsonObject json) throws JsonParseException { @@ -385,7 +713,7 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept } case "downloads" -> { if (value instanceof JsonObject object) { - Map map = new EnumMap<>(DownloadType.class); + Map map = new LinkedHashMap<>(); for (Map.Entry downloadEntry : object.entrySet()) { try { DownloadType downloadType = DownloadType.valueOf(downloadEntry.getKey()); @@ -393,12 +721,12 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept } catch (IllegalArgumentException ignored) { } } - downloads = Collections.unmodifiableMap(map); + downloads = ImmutableSequencedMap.copyOf(map); } } case "logging" -> { if (value instanceof JsonObject object) { - Map map = new EnumMap<>(DownloadType.class); + Map map = new LinkedHashMap<>(); for (Map.Entry loggingEntry : object.entrySet()) { try { DownloadType downloadType = DownloadType.valueOf(loggingEntry.getKey()); @@ -406,7 +734,7 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept } catch (IllegalArgumentException ignored) { } } - logging = Collections.unmodifiableMap(map); + logging = ImmutableSequencedMap.copyOf(map); } } case "type" -> { diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java new file mode 100644 index 0000000000..8cf76163e0 --- /dev/null +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java @@ -0,0 +1,83 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import com.google.gson.JsonObject; +import org.jetbrains.annotations.NotNullByDefault; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; + +/// Tests for game instance patch parsing and copy behavior. +@NotNullByDefault +public final class GameInstancePatchTest { + + /// Patch edits update known raw JSON fields without discarding unknown fields. + @Test + public void testRawJsonIsPreservedWhenUpdatingKnownFields() { + JsonObject json = new JsonObject(); + json.addProperty("id", "patch"); + json.addProperty("mainClass", "old.Main"); + json.addProperty("unknownField", "value"); + + GameInstancePatch originalPatch = GameInstancePatch.fromJson(json); + assertSame(originalPatch, originalPatch.withId("patch")); + assertSame(originalPatch, originalPatch.withMainClass("old.Main")); + + GameInstancePatch patch = originalPatch + .withMainClass("new.Main") + .withId(null); + + JsonObject updatedJson = patch.toJsonObject(); + assertEquals("value", updatedJson.get("unknownField").getAsString()); + assertEquals("new.Main", updatedJson.get("mainClass").getAsString()); + assertFalse(updatedJson.has("id")); + } + + /// Download maps keep the JSON entry order after parsing and rebuilding. + @Test + public void testDownloadMapOrderIsPreserved() { + JsonObject serverDownload = new JsonObject(); + serverDownload.addProperty("url", "server"); + JsonObject clientDownload = new JsonObject(); + clientDownload.addProperty("url", "client"); + + JsonObject downloads = new JsonObject(); + downloads.add("SERVER", serverDownload); + downloads.add("CLIENT", clientDownload); + + JsonObject json = new JsonObject(); + json.addProperty("id", "patch"); + json.add("downloads", downloads); + + GameInstancePatch patch = GameInstancePatch.fromJson(json); + Map patchDownloads = Objects.requireNonNull(patch.downloads()); + assertEquals(List.of(DownloadType.SERVER, DownloadType.CLIENT), List.copyOf(patchDownloads.keySet())); + + JsonObject updatedDownloads = patch.withDownload(patchDownloads) + .toJsonObject() + .getAsJsonObject("downloads"); + assertEquals(List.of("SERVER", "CLIENT"), List.copyOf(updatedDownloads.keySet())); + } +} From 08e471b20cb35a4d024c18dcfbc58f0bd3c8b222 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 16:56:34 +0800 Subject: [PATCH 79/85] refactor: enhance ImmutableSequencedMap with unmodifiable views and implement standard map methods --- .../hmcl/util/ImmutableSequencedMap.java | 43 +++++++++++------ .../hmcl/util/ImmutableSequencedMapTest.java | 46 +++++++++++++++++++ 2 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 HMCLCore/src/test/java/org/jackhuang/hmcl/util/ImmutableSequencedMapTest.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java index 9763bdae68..da68dd5aff 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ImmutableSequencedMap.java @@ -161,32 +161,45 @@ public void clear() { throw new UnsupportedOperationException(); } - /// Returns an unmodifiable view of the keys in insertion order. - /// - /// @return an unmodifiable set of keys + /// {@inheritDoc} @Override - public Set keySet() { - return Collections.unmodifiableSet(map.keySet()); + public @Unmodifiable Set keySet() { + return map.keySet(); } - /// Returns an unmodifiable view of the values in insertion order. - /// - /// @return an unmodifiable collection of values + /// {@inheritDoc} @Override - public Collection values() { - return Collections.unmodifiableCollection(map.values()); + public @Unmodifiable Collection values() { + return map.values(); } - /// Returns an unmodifiable view of the entries in insertion order. - /// - /// @return an unmodifiable set of entries + /// {@inheritDoc} @Override - public Set> entrySet() { - return Collections.unmodifiableSet(map.entrySet()); + public @Unmodifiable Set> entrySet() { + return map.entrySet(); } + /// {@inheritDoc} @Override public void forEach(BiConsumer action) { map.forEach(action); } + + /// {@inheritDoc} + @Override + public boolean equals(@Nullable Object obj) { + return obj == this || map.equals(obj); + } + + /// {@inheritDoc} + @Override + public int hashCode() { + return map.hashCode(); + } + + /// {@inheritDoc} + @Override + public String toString() { + return map.toString(); + } } diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/ImmutableSequencedMapTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/ImmutableSequencedMapTest.java new file mode 100644 index 0000000000..3ca7278796 --- /dev/null +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/ImmutableSequencedMapTest.java @@ -0,0 +1,46 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.util; + +import org.jetbrains.annotations.NotNullByDefault; +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/// Tests for immutable sequenced map behavior. +@NotNullByDefault +public final class ImmutableSequencedMapTest { + + /// Immutable sequenced maps follow the standard map object contract. + @Test + public void testObjectContract() { + LinkedHashMap source = new LinkedHashMap<>(); + source.put("b", 2); + source.put("a", 1); + + ImmutableSequencedMap map = ImmutableSequencedMap.copyOf(source); + + assertEquals(source, map); + assertEquals(source.hashCode(), map.hashCode()); + assertEquals(source.toString(), map.toString()); + assertEquals(List.of("b", "a"), List.copyOf(map.keySet())); + } +} From 82e4e3063b4f4d31c4ee5bf234ccac663abdcf3b Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 17:18:27 +0800 Subject: [PATCH 80/85] refactor: update instance ID handling to ensure consistent string representation --- .../org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java | 2 +- .../main/java/org/jackhuang/hmcl/ui/instances/Instances.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index 5d9d3832a0..88c437b27a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -108,7 +108,7 @@ private ModpackFileTreeItem getTreeItem(Path file, String basePath, int level) { } if (fileName.startsWith("._")) // macOS system file state = ModAdviser.ModSuggestion.HIDDEN; - if (FileUtils.getNameWithoutExtension(file).equals(instanceId)) + if (FileUtils.getNameWithoutExtension(file).equals(instanceId.toString())) state = ModAdviser.ModSuggestion.HIDDEN; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java index 47cb76f3d8..3e26d3c9eb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java @@ -157,7 +157,7 @@ public static CompletableFuture renameInstance(HMCLGameRepository reposi } }, instanceId.toString(), new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId), - new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName) || newVersionName.equals(instanceId))); + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName) || newVersionName.equals(instanceId.toString()))); } public static void exportInstance(HMCLGameRepository repository, GameInstanceID instanceId) { From f9beb987e53571d5bbbb4a424215d412356b4baf Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 17:23:39 +0800 Subject: [PATCH 81/85] refactor: update inheritsFrom field to use GameInstanceID for improved type safety --- .../hmcl/game/GameInstanceManifest.java | 2 +- .../hmcl/game/GameInstancePatch.java | 19 +++++++++++-------- .../hmcl/game/GameInstancePatchTest.java | 13 +++++++++++++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 6752b8cf90..1aad729ba1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -609,7 +609,7 @@ public GameInstancePatch toPatch() { minecraftArguments, arguments, mainClass, - inheritsFrom == null ? null : inheritsFrom.toString(), + inheritsFrom, jar, assetIndex, assets, diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java index 6a67aa6378..ab2d72a07f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstancePatch.java @@ -46,7 +46,7 @@ public record GameInstancePatch( @Nullable String minecraftArguments, @Nullable Arguments arguments, @Nullable String mainClass, - @Nullable String inheritsFrom, + @Nullable GameInstanceID inheritsFrom, @Nullable GameInstanceID jar, @Nullable AssetIndexInfo assetIndex, @Nullable String assets, @@ -143,7 +143,7 @@ public static GameInstancePatch fromManifest( manifest.minecraftArguments(), manifest.arguments(), manifest.mainClass(), - manifest.inheritsFrom() == null ? null : manifest.inheritsFrom().id(), + manifest.inheritsFrom(), manifest.jar(), manifest.assetIndex(), manifest.assets(), @@ -304,7 +304,7 @@ private static final class Builder { private @Nullable String minecraftArguments; private @Nullable Arguments arguments; private @Nullable String mainClass; - private @Nullable String inheritsFrom; + private @Nullable GameInstanceID inheritsFrom; private @Nullable GameInstanceID jar; private @Nullable AssetIndexInfo assetIndex; private @Nullable String assets; @@ -412,11 +412,11 @@ private void setMainClass(@Nullable String mainClass) { } } - private void setInheritsFrom(@Nullable String inheritsFrom) { + private void setInheritsFrom(@Nullable GameInstanceID inheritsFrom) { this.inheritsFrom = inheritsFrom; if (rawJson != null) { if (inheritsFrom != null) { - rawJson.addProperty("inheritsFrom", inheritsFrom); + rawJson.addProperty("inheritsFrom", inheritsFrom.toString()); } else { rawJson.remove("inheritsFrom"); } @@ -619,7 +619,7 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept @Nullable String minecraftArguments = null; @Nullable Arguments arguments = null; @Nullable String mainClass = null; - @Nullable String inheritsFrom = null; + @Nullable GameInstanceID inheritsFrom = null; @Nullable GameInstanceID jar = null; @Nullable AssetIndexInfo assetIndex = null; @Nullable String assets = null; @@ -668,7 +668,10 @@ public static GameInstancePatch fromJson(JsonObject json) throws JsonParseExcept } case "inheritsFrom" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { - inheritsFrom = primitive.getAsString(); + try { + inheritsFrom = new GameInstanceID(primitive.getAsString()); + } catch (IllegalArgumentException ignored) { + } } } case "jar" -> { @@ -819,7 +822,7 @@ public JsonObject toJsonObject() { if (mainClass != null) json.addProperty("mainClass", mainClass); if (inheritsFrom != null) - json.addProperty("inheritsFrom", inheritsFrom); + json.addProperty("inheritsFrom", inheritsFrom.toString()); if (jar != null) json.addProperty("jar", jar.toString()); if (assetIndex != null) diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java index 8cf76163e0..feb495d1cc 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java @@ -80,4 +80,17 @@ public void testDownloadMapOrderIsPreserved() { .getAsJsonObject("downloads"); assertEquals(List.of("SERVER", "CLIENT"), List.copyOf(updatedDownloads.keySet())); } + + /// Inherited instance ids are parsed and written through GameInstanceID. + @Test + public void testInheritsFromUsesGameInstanceId() { + JsonObject json = new JsonObject(); + json.addProperty("id", "patch"); + json.addProperty("inheritsFrom", "parent"); + + GameInstancePatch patch = GameInstancePatch.fromJson(json); + + assertEquals(new GameInstanceID("parent"), patch.inheritsFrom()); + assertEquals("parent", patch.toJsonObject().get("inheritsFrom").getAsString()); + } } From b99e34ede54297a647c4c849b63eff6a3f7c12dc Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 17:49:23 +0800 Subject: [PATCH 82/85] refactor: add tests for patch parsing and download handling in GameInstanceManifest --- .../hmcl/game/GameInstanceManifestTest.java | 50 ++++++++++ .../hmcl/game/GameInstancePatchTest.java | 96 ------------------- .../hmcl/util/ImmutableSequencedMapTest.java | 46 --------- 3 files changed, 50 insertions(+), 142 deletions(-) delete mode 100644 HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java delete mode 100644 HMCLCore/src/test/java/org/jackhuang/hmcl/util/ImmutableSequencedMapTest.java diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java index 6011fc7f57..f42b58d5ce 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstanceManifestTest.java @@ -24,10 +24,13 @@ import java.nio.file.Path; import java.util.List; +import java.util.Map; +import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; /// Tests for game instance manifest parsing and resolution behavior. @NotNullByDefault @@ -108,6 +111,53 @@ public void testDownloadMapOrderIsPreserved() { assertEquals(List.of("SERVER", "CLIENT"), List.copyOf(updatedDownloads.keySet())); } + /// Patch edits preserve raw JSON fields and typed inherited instance ids. + @Test + public void testPatchParsingAndCopyBehavior() { + JsonObject json = new JsonObject(); + json.addProperty("id", "patch"); + json.addProperty("mainClass", "old.Main"); + json.addProperty("inheritsFrom", "parent"); + json.addProperty("unknownField", "value"); + + GameInstancePatch originalPatch = GameInstancePatch.fromJson(json); + assertEquals(new GameInstanceID("parent"), originalPatch.inheritsFrom()); + assertSame(originalPatch, originalPatch.withId("patch")); + assertSame(originalPatch, originalPatch.withMainClass("old.Main")); + + GameInstancePatch patch = originalPatch + .withMainClass("new.Main") + .withId(null); + + JsonObject updatedJson = patch.toJsonObject(); + assertEquals("value", updatedJson.get("unknownField").getAsString()); + assertEquals("new.Main", updatedJson.get("mainClass").getAsString()); + assertEquals("parent", updatedJson.get("inheritsFrom").getAsString()); + assertFalse(updatedJson.has("id")); + + JsonObject serverDownload = new JsonObject(); + serverDownload.addProperty("url", "server"); + JsonObject clientDownload = new JsonObject(); + clientDownload.addProperty("url", "client"); + + JsonObject downloads = new JsonObject(); + downloads.add("SERVER", serverDownload); + downloads.add("CLIENT", clientDownload); + + JsonObject patchJson = new JsonObject(); + patchJson.addProperty("id", "patch"); + patchJson.add("downloads", downloads); + + GameInstancePatch patchWithDownloads = GameInstancePatch.fromJson(patchJson); + Map patchDownloads = Objects.requireNonNull(patchWithDownloads.downloads()); + assertEquals(List.of(DownloadType.SERVER, DownloadType.CLIENT), List.copyOf(patchDownloads.keySet())); + + JsonObject updatedDownloads = patchWithDownloads.withDownload(patchDownloads) + .toJsonObject() + .getAsJsonObject("downloads"); + assertEquals(List.of("SERVER", "CLIENT"), List.copyOf(updatedDownloads.keySet())); + } + /// Creates a minimal manifest for tests. private static GameInstanceManifest manifest( String id, diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java deleted file mode 100644 index feb495d1cc..0000000000 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/game/GameInstancePatchTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2026 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.game; - -import com.google.gson.JsonObject; -import org.jetbrains.annotations.NotNullByDefault; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; - -/// Tests for game instance patch parsing and copy behavior. -@NotNullByDefault -public final class GameInstancePatchTest { - - /// Patch edits update known raw JSON fields without discarding unknown fields. - @Test - public void testRawJsonIsPreservedWhenUpdatingKnownFields() { - JsonObject json = new JsonObject(); - json.addProperty("id", "patch"); - json.addProperty("mainClass", "old.Main"); - json.addProperty("unknownField", "value"); - - GameInstancePatch originalPatch = GameInstancePatch.fromJson(json); - assertSame(originalPatch, originalPatch.withId("patch")); - assertSame(originalPatch, originalPatch.withMainClass("old.Main")); - - GameInstancePatch patch = originalPatch - .withMainClass("new.Main") - .withId(null); - - JsonObject updatedJson = patch.toJsonObject(); - assertEquals("value", updatedJson.get("unknownField").getAsString()); - assertEquals("new.Main", updatedJson.get("mainClass").getAsString()); - assertFalse(updatedJson.has("id")); - } - - /// Download maps keep the JSON entry order after parsing and rebuilding. - @Test - public void testDownloadMapOrderIsPreserved() { - JsonObject serverDownload = new JsonObject(); - serverDownload.addProperty("url", "server"); - JsonObject clientDownload = new JsonObject(); - clientDownload.addProperty("url", "client"); - - JsonObject downloads = new JsonObject(); - downloads.add("SERVER", serverDownload); - downloads.add("CLIENT", clientDownload); - - JsonObject json = new JsonObject(); - json.addProperty("id", "patch"); - json.add("downloads", downloads); - - GameInstancePatch patch = GameInstancePatch.fromJson(json); - Map patchDownloads = Objects.requireNonNull(patch.downloads()); - assertEquals(List.of(DownloadType.SERVER, DownloadType.CLIENT), List.copyOf(patchDownloads.keySet())); - - JsonObject updatedDownloads = patch.withDownload(patchDownloads) - .toJsonObject() - .getAsJsonObject("downloads"); - assertEquals(List.of("SERVER", "CLIENT"), List.copyOf(updatedDownloads.keySet())); - } - - /// Inherited instance ids are parsed and written through GameInstanceID. - @Test - public void testInheritsFromUsesGameInstanceId() { - JsonObject json = new JsonObject(); - json.addProperty("id", "patch"); - json.addProperty("inheritsFrom", "parent"); - - GameInstancePatch patch = GameInstancePatch.fromJson(json); - - assertEquals(new GameInstanceID("parent"), patch.inheritsFrom()); - assertEquals("parent", patch.toJsonObject().get("inheritsFrom").getAsString()); - } -} diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/ImmutableSequencedMapTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/ImmutableSequencedMapTest.java deleted file mode 100644 index 3ca7278796..0000000000 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/ImmutableSequencedMapTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2026 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.util; - -import org.jetbrains.annotations.NotNullByDefault; -import org.junit.jupiter.api.Test; - -import java.util.LinkedHashMap; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/// Tests for immutable sequenced map behavior. -@NotNullByDefault -public final class ImmutableSequencedMapTest { - - /// Immutable sequenced maps follow the standard map object contract. - @Test - public void testObjectContract() { - LinkedHashMap source = new LinkedHashMap<>(); - source.put("b", 2); - source.put("a", 1); - - ImmutableSequencedMap map = ImmutableSequencedMap.copyOf(source); - - assertEquals(source, map); - assertEquals(source.hashCode(), map.hashCode()); - assertEquals(source.toString(), map.toString()); - assertEquals(List.of("b", "a"), List.copyOf(map.keySet())); - } -} From 458dea1a99744b9677be02e162d156524ed31826 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 18:02:25 +0800 Subject: [PATCH 83/85] refactor: update UI labels to use 'instance' terminology for consistency --- .../jackhuang/hmcl/game/LauncherHelper.java | 6 +- .../ui/download/AbstractInstallersPage.java | 2 +- .../hmcl/ui/download/ModpackPage.java | 2 +- .../hmcl/ui/download/VersionsPage.java | 22 ++-- .../hmcl/ui/instances/DownloadListPage.java | 2 +- .../ui/instances/GameAdvancedListItem.java | 6 +- .../hmcl/ui/instances/GameInstancePage.java | 24 ++-- .../hmcl/ui/instances/GameListCell.java | 16 +-- .../hmcl/ui/instances/GameListPage.java | 4 +- .../hmcl/ui/instances/GameListPopupMenu.java | 2 +- .../hmcl/ui/instances/Instances.java | 26 ++--- .../hmcl/ui/instances/WorldListPage.java | 6 +- .../hmcl/ui/instances/WorldManagePage.java | 6 +- .../org/jackhuang/hmcl/ui/main/MainPage.java | 10 +- .../org/jackhuang/hmcl/ui/main/RootPage.java | 4 +- .../terracotta/TerracottaControllerPage.java | 2 +- .../hmcl/ui/terracotta/TerracottaPage.java | 4 +- .../resources/assets/lang/I18N.properties | 104 +++++++++--------- .../resources/assets/lang/I18N_ar.properties | 104 +++++++++--------- .../resources/assets/lang/I18N_es.properties | 90 +++++++-------- .../resources/assets/lang/I18N_ja.properties | 72 ++++++------ .../resources/assets/lang/I18N_lzh.properties | 92 ++++++++-------- .../resources/assets/lang/I18N_ru.properties | 92 ++++++++-------- .../resources/assets/lang/I18N_uk.properties | 88 +++++++-------- .../resources/assets/lang/I18N_zh.properties | 104 +++++++++--------- .../assets/lang/I18N_zh_CN.properties | 104 +++++++++--------- 26 files changed, 497 insertions(+), 497 deletions(-) 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 ce23c7d740..0b978df780 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -99,7 +99,7 @@ public LauncherHelper(HMCLGameRepository repository, Account account, GameInstan this.setting = repository.getEffectiveGameSettings(selectedInstanceId); this.launcherVisibility = setting.getInheritable(GameSettings::launcherVisibilityProperty); this.showLogs = setting.getInheritable(GameSettings::showLogsProperty); - this.launchingStepsPane.setTitle(i18n("version.launch")); + this.launchingStepsPane.setTitle(i18n("instance.launch")); } private final TaskExecutorDialogPane launchingStepsPane = new TaskExecutorDialogPane(TaskCancellationAction.NORMAL); @@ -276,7 +276,7 @@ private void launch0() { } else { runLater(() -> { launchingStepsPane.fireEvent(new DialogCloseEvent()); - Controllers.dialog(i18n("version.launch_script.success", FileUtils.getAbsolutePath(scriptFile))); + Controllers.dialog(i18n("instance.launch_script.success", FileUtils.getAbsolutePath(scriptFile))); }); } }).withFakeProgress( @@ -378,7 +378,7 @@ public void onStop(boolean success, TaskExecutor executor) { message = StringUtils.getStackTrace(ex); } Controllers.dialog(message, - scriptFile == null ? i18n("launch.failed") : i18n("version.launch_script.failed"), + scriptFile == null ? i18n("launch.failed") : i18n("instance.launch_script.failed"), MessageType.ERROR); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AbstractInstallersPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AbstractInstallersPage.java index b1c2a97d59..f582340523 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AbstractInstallersPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/AbstractInstallersPage.java @@ -131,7 +131,7 @@ protected InstallersPageSkin(AbstractInstallersPage control) { versionNamePane.setAlignment(Pos.CENTER_LEFT); control.txtName.setMaxWidth(300); - versionNamePane.getChildren().setAll(new Label(i18n("version.name")), control.txtName); + versionNamePane.getChildren().setAll(new Label(i18n("instance.name")), control.txtName); root.setTop(versionNamePane); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java index 3c5443c349..0baf1041d2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java @@ -61,7 +61,7 @@ protected ModpackPage(WizardController controller) { { var archiveNamePane = new LinePane(); { - archiveNamePane.setTitle(i18n("version.name")); + archiveNamePane.setTitle(i18n("instance.name")); txtModpackName = new JFXTextField(); txtModpackName.setPrefWidth(300); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index c9c4ba7592..193d2955b4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -237,28 +237,28 @@ public void updateItem(RemoteVersion remoteVersion, boolean empty) { switch (versionType) { case RELEASE -> { - twoLineListItem.addTag(i18n("version.game.release")); + twoLineListItem.addTag(i18n("instance.game.release")); imageView.setImage(GameInstanceIconType.GRASS.getIcon()); } case SNAPSHOT, PENDING, UNOBFUSCATED -> { if (versionType == RemoteVersion.Type.SNAPSHOT && GameVersionNumber.asGameVersion(remoteVersion.getGameVersion()).isAprilFools()) { - twoLineListItem.addTag(i18n("version.game.april_fools")); + twoLineListItem.addTag(i18n("instance.game.april_fools")); imageView.setImage(GameInstanceIconType.APRIL_FOOLS.getIcon()); } else { - twoLineListItem.addTag(i18n("version.game.snapshot")); + twoLineListItem.addTag(i18n("instance.game.snapshot")); imageView.setImage(GameInstanceIconType.COMMAND.getIcon()); } } default -> { - twoLineListItem.addTag(i18n("version.game.old")); + twoLineListItem.addTag(i18n("instance.game.old")); imageView.setImage(GameInstanceIconType.CRAFT_TABLE.getIcon()); } } switch (NativePatcher.checkSupportedStatus(gameVersion, Platform.SYSTEM_PLATFORM, OperatingSystem.SYSTEM_VERSION)) { - case UNTESTED -> twoLineListItem.addTagWarning(i18n("version.game.support_status.untested")); - case UNSUPPORTED -> twoLineListItem.addTagWarning(i18n("version.game.support_status.unsupported")); + case UNTESTED -> twoLineListItem.addTagWarning(i18n("instance.game.support_status.untested")); + case UNSUPPORTED -> twoLineListItem.addTagWarning(i18n("instance.game.support_status.unsupported")); } } else { GameInstanceIconType iconType; @@ -333,7 +333,7 @@ private static final class VersionsPageSkin extends SkinBase { { nameField = new JFXTextField(); - nameField.setPromptText(i18n("version.search.prompt")); + nameField.setPromptText(i18n("instance.search.prompt")); nameField.textProperty().addListener(o -> updateList()); if ("game".equals(control.libraryId)) { @@ -353,7 +353,7 @@ private static final class VersionsPageSkin extends SkinBase { ); categoryField.getSelectionModel().select(VersionTypeFilter.ALL); } - categoryField.setConverter(stringConverter(type -> i18n("version.game." + type.name().toLowerCase(Locale.ROOT)))); + categoryField.setConverter(stringConverter(type -> i18n("instance.game." + type.name().toLowerCase(Locale.ROOT)))); categoryField.getSelectionModel().selectedItemProperty().addListener(o -> updateList()); JFXButton refreshButton = FXUtils.newRaisedButton(i18n("button.refresh")); @@ -361,13 +361,13 @@ private static final class VersionsPageSkin extends SkinBase { if (control.versionList.hasType()) { searchPane.addRow(rowIndex++, - new Label(i18n("version.search")), nameField, - new Label(i18n("version.game.type")), categoryField, + new Label(i18n("instance.search")), nameField, + new Label(i18n("instance.game.type")), categoryField, refreshButton ); } else { searchPane.addRow(rowIndex++, - new Label(i18n("version.search")), nameField, + new Label(i18n("instance.search")), nameField, refreshButton ); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java index 6ed4142842..f82992d90e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java @@ -283,7 +283,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) { Bindings.bindContent(instancesComboBox.getItems(), control.instances); selectedItemPropertyFor(instancesComboBox).bindBidirectional(control.selectedInstance); - searchPane.add(new Label(i18n("version")), columns++, rowIndex); + searchPane.add(new Label(i18n("instance")), columns++, rowIndex); searchPane.add(lastNode = instancesComboBox, columns++, rowIndex); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameAdvancedListItem.java index 00c8f03e47..19bfb72f77 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameAdvancedListItem.java @@ -59,15 +59,15 @@ private void loadInstance(GameInstanceID instanceId) { } if (instanceId != null && repository != null) { if (repository.hasInstance(instanceId)) { - setTitle(i18n("version.manage.manage")); + setTitle(i18n("instance.manage.manage")); setSubtitle(instanceId.toString()); imageContainer.setImage(repository.getInstanceIconImage(instanceId)); return; } } - setTitle(i18n("version.empty")); - setSubtitle(i18n("version.empty.add")); + setTitle(i18n("instance.empty")); + setSubtitle(i18n("instance.empty.add")); imageContainer.setImage(GameInstanceIconType.DEFAULT.getIcon()); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstancePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstancePage.java index 6f9479cb42..b27d2eff24 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstancePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameInstancePage.java @@ -317,25 +317,25 @@ protected Skin(GameInstancePage control) { PopupMenu managementList = new PopupMenu(); JFXPopup managementPopup = new JFXPopup(managementList); managementList.getContent().setAll( - new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("version.launch.test"), control::testGame, managementPopup), - new IconedMenuItem(SVG.SCRIPT, i18n("version.launch_script"), control::generateLaunchScript, managementPopup), + new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("instance.launch.test"), control::testGame, managementPopup), + new IconedMenuItem(SVG.SCRIPT, i18n("instance.launch_script"), control::generateLaunchScript, managementPopup), new MenuSeparator(), - new IconedMenuItem(SVG.EDIT, i18n("version.manage.rename"), control::rename, managementPopup), - new IconedMenuItem(SVG.FOLDER_COPY, i18n("version.manage.duplicate"), control::duplicate, managementPopup), - new IconedMenuItem(SVG.DELETE, i18n("version.manage.remove"), control::remove, managementPopup), + new IconedMenuItem(SVG.EDIT, i18n("instance.manage.rename"), control::rename, managementPopup), + new IconedMenuItem(SVG.FOLDER_COPY, i18n("instance.manage.duplicate"), control::duplicate, managementPopup), + new IconedMenuItem(SVG.DELETE, i18n("instance.manage.remove"), control::remove, managementPopup), new IconedMenuItem(SVG.OUTPUT, i18n("modpack.export"), control::export, managementPopup), new MenuSeparator(), - new IconedMenuItem(null, i18n("version.manage.redownload_assets_index"), control::redownloadAssetIndex, managementPopup), - new IconedMenuItem(null, i18n("version.manage.remove_assets"), control::clearAssets, managementPopup), - new IconedMenuItem(null, i18n("version.manage.remove_libraries"), control::clearLibraries, managementPopup), - new IconedMenuItem(null, i18n("version.manage.clean"), control::clearJunkFiles, managementPopup).addTooltip(i18n("version.manage.clean.tooltip")) + new IconedMenuItem(null, i18n("instance.manage.redownload_assets_index"), control::redownloadAssetIndex, managementPopup), + new IconedMenuItem(null, i18n("instance.manage.remove_assets"), control::clearAssets, managementPopup), + new IconedMenuItem(null, i18n("instance.manage.remove_libraries"), control::clearLibraries, managementPopup), + new IconedMenuItem(null, i18n("instance.manage.clean"), control::clearJunkFiles, managementPopup).addTooltip(i18n("instance.manage.clean.tooltip")) ); AdvancedListBox toolbar = new AdvancedListBox() - .addNavigationDrawerItem(i18n("version.update"), SVG.UPDATE, control::updateGame, upgradeItem -> { + .addNavigationDrawerItem(i18n("instance.update"), SVG.UPDATE, control::updateGame, upgradeItem -> { upgradeItem.visibleProperty().bind(control.currentInstanceUpgradable); }) - .addNavigationDrawerItem(i18n("version.launch.test"), SVG.ROCKET_LAUNCH, control::testGame) + .addNavigationDrawerItem(i18n("instance.launch.test"), SVG.ROCKET_LAUNCH, control::testGame) .addNavigationDrawerItem(i18n("settings.game.exploration"), SVG.FOLDER_OPEN, null, browseMenuItem -> { browseMenuItem.setOnAction(e -> browsePopup.show(browseMenuItem, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.LEFT, browseMenuItem.getWidth(), 0)); }) @@ -349,7 +349,7 @@ protected Skin(GameInstancePage control) { } control.state.bind(Bindings.createObjectBinding(() -> - State.fromTitle(i18n("version.manage.manage.title", getSkinnable().getInstanceId()), -1), + State.fromTitle(i18n("instance.manage.manage.title", getSkinnable().getInstanceId()), -1), getSkinnable().instanceReference)); //control.transitionPane.getStyleClass().add("gray-background"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java index 67669c353f..bb2e3c9907 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java @@ -113,7 +113,7 @@ public void fire() { if (item != null) item.update(); }); - FXUtils.installFastTooltip(btnUpgrade, i18n("version.update")); + FXUtils.installFastTooltip(btnUpgrade, i18n("instance.update")); right.getChildren().add(btnUpgrade); this.btnLaunch = FXUtils.newToggleButton4(SVG.ROCKET_LAUNCH); @@ -123,7 +123,7 @@ public void fire() { item.testGame(); }); BorderPane.setAlignment(btnLaunch, Pos.CENTER); - FXUtils.installFastTooltip(btnLaunch, i18n("version.launch.test")); + FXUtils.installFastTooltip(btnLaunch, i18n("instance.launch.test")); right.getChildren().add(btnLaunch); this.btnManage = FXUtils.newToggleButton4(SVG.MORE_VERT); @@ -191,14 +191,14 @@ private static JFXPopup getPopup(GameListItem item) { JFXPopup popup = new JFXPopup(menu); menu.getContent().setAll( - new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("version.launch.test"), item::testGame, popup), - new IconedMenuItem(SVG.SCRIPT, i18n("version.launch_script"), item::generateLaunchScript, popup), + new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("instance.launch.test"), item::testGame, popup), + new IconedMenuItem(SVG.SCRIPT, i18n("instance.launch_script"), item::generateLaunchScript, popup), new MenuSeparator(), - new IconedMenuItem(SVG.SETTINGS, i18n("version.manage.manage"), item::modifyGameSettings, popup), + new IconedMenuItem(SVG.SETTINGS, i18n("instance.manage.manage"), item::modifyGameSettings, popup), new MenuSeparator(), - new IconedMenuItem(SVG.EDIT, i18n("version.manage.rename"), item::rename, popup), - new IconedMenuItem(SVG.FOLDER_COPY, i18n("version.manage.duplicate"), item::duplicate, popup), - new IconedMenuItem(SVG.DELETE, i18n("version.manage.remove"), item::remove, popup), + new IconedMenuItem(SVG.EDIT, i18n("instance.manage.rename"), item::rename, popup), + new IconedMenuItem(SVG.FOLDER_COPY, i18n("instance.manage.duplicate"), item::duplicate, popup), + new IconedMenuItem(SVG.DELETE, i18n("instance.manage.remove"), item::remove, popup), new IconedMenuItem(SVG.OUTPUT, i18n("modpack.export"), item::export, popup), new MenuSeparator(), new IconedMenuItem(SVG.FOLDER_OPEN, i18n("folder.game"), item::browse, popup)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPage.java index c727003ab9..7bc9dd5c95 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPage.java @@ -70,7 +70,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public class GameListPage extends DecoratorAnimatedPage implements DecoratorPage { - private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("version.manage"))); + private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("instance.manage"))); /// Navigation drawer items for configured game directories. @SuppressWarnings("FieldCanBeLocal") private final ObservableList gameDirectoryListItems; @@ -157,7 +157,7 @@ private void loadVersions(HMCLGameRepository repository) { sourceList.setAll(versionItems); if (versionItems.isEmpty()) { - setFailedReason(i18n("version.empty.hint")); + setFailedReason(i18n("instance.empty.hint")); } setLoading(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java index 4b5fc05cb2..c8c0da1d94 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java @@ -75,7 +75,7 @@ public GameListPopupMenu() { listView.prefHeightProperty().bind(Bindings.size(getItems()).multiply(50).add(2)); - Label placeholder = new Label(i18n("version.empty")); + Label placeholder = new Label(i18n("instance.empty")); placeholder.setStyle("-fx-padding: 10px; -fx-text-fill: -monet-on-surface-variant; -fx-font-style: italic;"); FXUtils.onChangeAndOperate(isEmpty, empty -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java index 3e26d3c9eb..520915bda3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java @@ -119,8 +119,8 @@ public static void downloadModpackImpl(DownloadProvider downloadProvider, HMCLGa public static void deleteInstance(HMCLGameRepository repository, GameInstanceID instanceId) { boolean isIndependent = repository.getRunDirectory(instanceId).toAbsolutePath().normalize() .equals(repository.getInstanceRoot(instanceId).toAbsolutePath().normalize()); - String message = isIndependent ? i18n("version.manage.remove.confirm.independent", instanceId) : - i18n("version.manage.remove.confirm.trash", instanceId, instanceId + "_removed"); + String message = isIndependent ? i18n("instance.manage.remove.confirm.independent", instanceId) : + i18n("instance.manage.remove.confirm.trash", instanceId, instanceId + "_removed"); JFXButton deleteButton = new JFXButton(i18n("button.delete")); deleteButton.getStyleClass().add("dialog-error"); @@ -128,7 +128,7 @@ public static void deleteInstance(HMCLGameRepository repository, GameInstanceID Task.supplyAsync(Schedulers.io(), () -> repository.removeInstanceFromDisk(instanceId)) .whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception != null || !Boolean.TRUE.equals(result)) { - Controllers.dialog(i18n("version.manage.remove.failed"), i18n("message.error"), MessageDialogPane.MessageType.ERROR); + Controllers.dialog(i18n("instance.manage.remove.failed"), i18n("message.error"), MessageDialogPane.MessageType.ERROR); } }).start(); }); @@ -137,7 +137,7 @@ public static void deleteInstance(HMCLGameRepository repository, GameInstanceID } public static CompletableFuture renameInstance(HMCLGameRepository repository, GameInstanceID instanceId) { - return Controllers.prompt(i18n("version.manage.rename.message"), (newName, handler) -> { + return Controllers.prompt(i18n("instance.manage.rename.message"), (newName, handler) -> { if (newName.equals(instanceId.toString())) { handler.resolve(); return; @@ -153,7 +153,7 @@ public static CompletableFuture renameInstance(HMCLGameRepository reposi } }).start(); } else { - handler.reject(i18n("version.manage.rename.fail")); + handler.reject(i18n("instance.manage.rename.fail")); } }, instanceId.toString(), new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId), @@ -180,7 +180,7 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { return; } - Controllers.prompt(i18n("version.manage.duplicate.prompt"), (result, handler) -> { + Controllers.prompt(i18n("instance.manage.duplicate.prompt"), (result, handler) -> { handler.resolve(); GameInstanceID instanceId = new GameInstanceID(result); @@ -211,7 +211,7 @@ public static void installFromJson(HMCLGameRepository repository, Path file) { public static void duplicateInstance(HMCLGameRepository repository, GameInstanceID instanceId) { Controllers.prompt( - new PromptDialogPane.Builder(i18n("version.manage.duplicate.prompt"), (res, handler) -> { + new PromptDialogPane.Builder(i18n("instance.manage.duplicate.prompt"), (res, handler) -> { String newInstanceName = ((PromptDialogPane.Builder.StringQuestion) res.get(1)).getValue(); GameInstanceID newInstanceId = new GameInstanceID(newInstanceName); boolean copySaves = ((PromptDialogPane.Builder.BooleanQuestion) res.get(2)).getValue(); @@ -228,11 +228,11 @@ public static void duplicateInstance(HMCLGameRepository repository, GameInstance } }).start(); }) - .addQuestion(new PromptDialogPane.Builder.HintQuestion(i18n("version.manage.duplicate.confirm"))) + .addQuestion(new PromptDialogPane.Builder.HintQuestion(i18n("instance.manage.duplicate.confirm"))) .addQuestion(new PromptDialogPane.Builder.StringQuestion(null, instanceId.toString(), new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId), new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName)))) - .addQuestion(new PromptDialogPane.Builder.BooleanQuestion(i18n("version.manage.duplicate.duplicate_save"), false))); + .addQuestion(new PromptDialogPane.Builder.BooleanQuestion(i18n("instance.manage.duplicate.duplicate_save"), false))); } public static void updateInstance(HMCLGameRepository repository, GameInstanceID instanceId) { @@ -242,7 +242,7 @@ public static void updateInstance(HMCLGameRepository repository, GameInstanceID public static void updateGameAssets(HMCLGameRepository repository, GameInstanceID instanceId) { TaskExecutor executor = new GameAssetDownloadTask(repository.getDependency(), repository.getInstanceManifest(instanceId), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true) .executor(); - Controllers.taskDialog(executor, i18n("version.manage.redownload_assets_index"), TaskCancellationAction.NO_CANCEL); + Controllers.taskDialog(executor, i18n("instance.manage.redownload_assets_index"), TaskCancellationAction.NO_CANCEL); executor.start(); } @@ -262,7 +262,7 @@ public static void generateLaunchScript(HMCLGameRepository repository, GameInsta FileChooser chooser = new FileChooser(); if (Files.isDirectory(repository.getRunDirectory(instanceId))) chooser.setInitialDirectory(repository.getRunDirectory(instanceId).toFile()); - chooser.setTitle(i18n("version.launch_script.save")); + chooser.setTitle(i18n("instance.launch_script.save")); if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { chooser.getExtensionFilters().add( new FileChooser.ExtensionFilter(i18n("extension.command"), "*.command") @@ -339,11 +339,11 @@ private static boolean checkVersionForLaunching(HMCLGameRepository repository, G } if (unavailable) { - JFXButton gotoDownload = new JFXButton(i18n("version.empty.launch.goto_download")); + JFXButton gotoDownload = new JFXButton(i18n("instance.empty.launch.goto_download")); gotoDownload.getStyleClass().add("dialog-accept"); gotoDownload.setOnAction(e -> Controllers.navigate(Controllers.getDownloadPage())); - Controllers.confirmAction(i18n("version.empty.launch"), i18n("launch.failed"), + Controllers.confirmAction(i18n("instance.empty.launch"), i18n("launch.failed"), MessageDialogPane.MessageType.ERROR, gotoDownload, null); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldListPage.java index bab9f30a0e..dae93f97a5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldListPage.java @@ -282,7 +282,7 @@ public WorldListCell(WorldListPage page) { btnLaunch.visibleProperty().bind(page.supportQuickPlayProperty()); btnLaunch.managedProperty().bind(btnLaunch.visibleProperty()); right.getChildren().add(btnLaunch); - FXUtils.installFastTooltip(btnLaunch, i18n("version.launch")); + FXUtils.installFastTooltip(btnLaunch, i18n("instance.launch")); btnLaunch.setOnAction(event -> { World world = getItem(); if (world != null) @@ -356,12 +356,12 @@ public void showPopupMenu(World world, boolean supportQuickPlay, JFXPopup.PopupH if (supportQuickPlay) { - IconedMenuItem launchItem = new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("version.launch_and_enter_world"), () -> page.launch(world), popup); + IconedMenuItem launchItem = new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("instance.launch_and_enter_world"), () -> page.launch(world), popup); launchItem.setDisable(worldLocked); popupMenu.getContent().addAll( launchItem, - new IconedMenuItem(SVG.SCRIPT, i18n("version.launch_script"), () -> page.generateLaunchScript(world), popup), + new IconedMenuItem(SVG.SCRIPT, i18n("instance.launch_script"), () -> page.generateLaunchScript(world), popup), new MenuSeparator() ); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManagePage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManagePage.java index d807d4b819..fcc89fb16d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManagePage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManagePage.java @@ -233,7 +233,7 @@ private AdvancedListBox getToolBar() { BorderPane.setMargin(toolbar, new Insets(0, 0, 12, 0)); { if (getSkinnable().supportQuickPlay) { - toolbar.addNavigationDrawerItem(i18n("version.launch"), SVG.ROCKET_LAUNCH, () -> getSkinnable().launch(), advancedListItem -> advancedListItem.disableProperty().bind(getSkinnable().readOnlyProperty())); + toolbar.addNavigationDrawerItem(i18n("instance.launch"), SVG.ROCKET_LAUNCH, () -> getSkinnable().launch(), advancedListItem -> advancedListItem.disableProperty().bind(getSkinnable().readOnlyProperty())); } if (ChunkBaseApp.isSupported(getSkinnable().world)) { @@ -266,8 +266,8 @@ private AdvancedListBox getToolBar() { if (getSkinnable().supportQuickPlay) { managePopupMenu.getContent().addAll( - new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("version.launch"), () -> getSkinnable().launch(), managePopup), - new IconedMenuItem(SVG.SCRIPT, i18n("version.launch_script"), () -> getSkinnable().generateLaunchScript(), managePopup), + new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("instance.launch"), () -> getSkinnable().launch(), managePopup), + new IconedMenuItem(SVG.SCRIPT, i18n("instance.launch_script"), () -> getSkinnable().generateLaunchScript(), managePopup), new MenuSeparator() ); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java index f9b17611ea..f9af5a9893 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/MainPage.java @@ -233,15 +233,15 @@ public final class MainPage extends StackPane implements DecoratorPage { @Override public void accept(@Nullable GameInstanceID currentGame) { if (currentGame == null) { - launchLabel.setText(i18n("version.launch.empty")); + launchLabel.setText(i18n("instance.launch.empty")); currentLabel.setText(null); graphic.getChildren().setAll(launchLabel); FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launchNoGame); if (tooltip == null) - tooltip = new Tooltip(i18n("version.launch.empty.tooltip")); + tooltip = new Tooltip(i18n("instance.launch.empty.tooltip")); FXUtils.installFastTooltip(launchButton, tooltip); } else { - launchLabel.setText(i18n("version.launch")); + launchLabel.setText(i18n("instance.launch")); currentLabel.setText(currentGame.toString()); graphic.getChildren().setAll(launchLabel, currentLabel); FXUtils.setOnActionWithCooldown(launchButton, MainPage.this::launch); @@ -264,7 +264,7 @@ public void accept(@Nullable GameInstanceID currentGame) { -menuButton.getHeight(), repository, versions )); - FXUtils.installFastTooltip(menuButton, i18n("version.switch")); + FXUtils.installFastTooltip(menuButton, i18n("instance.switch")); menuButton.setGraphic(SVG.ARROW_DROP_UP.createIcon(30)); EventHandler secondaryClickHandle = event -> { @@ -362,7 +362,7 @@ private void launchNoGame() { MessageDialogPane.MessageType.WARNING); } }); - Controllers.taskDialog(task, i18n("version.launch.empty.installing"), TaskCancellationAction.NORMAL); + Controllers.taskDialog(task, i18n("instance.launch.empty.installing"), TaskCancellationAction.NORMAL); } private void onUpgrade() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index ad65466db5..97478cf94f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -174,7 +174,7 @@ protected Skin(RootPage control) { // third item in left sidebar AdvancedListItem gameItem = new AdvancedListItem(); gameItem.setLeftIcon(SVG.FORMAT_LIST_BULLETED); - gameItem.setTitle(i18n("version.manage")); + gameItem.setTitle(i18n("instance.manage")); gameItem.setOnAction(e -> Controllers.navigate(Controllers.getGameListPage())); FXUtils.onSecondaryButtonClicked(gameItem, () -> showGameListPopupMenu(gameItem)); @@ -229,7 +229,7 @@ else if (Platform.SYSTEM_PLATFORM.equals(OperatingSystem.LINUX, Architecture.LOO AdvancedListBox sideBar = new AdvancedListBox() .startCategory(i18n("account").toUpperCase(Locale.ROOT)) .add(accountListItem) - .startCategory(i18n("version").toUpperCase(Locale.ROOT)) + .startCategory(i18n("instance").toUpperCase(Locale.ROOT)) .add(gameListItem) .add(gameItem) .add(downloadItem) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java index 67e88371dd..7880fe3f27 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaControllerPage.java @@ -210,7 +210,7 @@ public TerracottaControllerPage() { i18n("terracotta.status.waiting.host.launch.desc"), i18n("terracotta.status.waiting.host.launch.title"), MessageDialogPane.MessageType.QUESTION - ).addAction(i18n("version.launch"), () -> { + ).addAction(i18n("instance.launch"), () -> { var repository = GameDirectoryManager.getSelectedRepository(); Instances.launch(repository, repository.getSelectedInstance(), launcherHelper -> { launcherHelper.setKeep(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java index 6ec2a39f71..e4ea34fda5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/terracotta/TerracottaPage.java @@ -77,7 +77,7 @@ public TerracottaPage() { AdvancedListBox toolbar = new AdvancedListBox() .add(accountListItem) - .addNavigationDrawerItem(i18n("version.launch"), SVG.ROCKET_LAUNCH, () -> { + .addNavigationDrawerItem(i18n("instance.launch"), SVG.ROCKET_LAUNCH, () -> { var repository = GameDirectoryManager.getSelectedRepository(); Instances.launch(repository, repository.getSelectedInstance(), launcherHelper -> { launcherHelper.setKeep(); @@ -85,7 +85,7 @@ public TerracottaPage() { }); }, item -> { instanceChangeListenerHolder = FXUtils.onWeakChangeAndOperate(GameDirectoryManager.selectedInstanceProperty(), - instanceName -> item.setSubtitle(instanceName != null ? instanceName.toString() : i18n("version.empty")) + instanceName -> item.setSubtitle(instanceName != null ? instanceName.toString() : i18n("instance.empty")) ); MainPage mainPage = Controllers.getRootPage().getMainPage(); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 9b45c3be3a..ae3385c992 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -816,6 +816,58 @@ install.new_game.malformed_json=Invalid Minecraft instance JSON file. install.select=Choose operation install.success=Successfully installed. +instance=Games +instance.name=Instance Name +instance.empty=No Instances +instance.empty.add=Add new instance +instance.empty.launch=No available instances.\nYou can go to the "Download" page to get the game, or switch the game directory in the "All Instances" page. +instance.empty.launch.goto_download=Go to Download Page +instance.empty.hint=There are no Minecraft instances here.\nYou can try switching to another game directory or clicking here to download one. +instance.game.all=All +instance.game.april_fools=April Fools +instance.game.old=Historical +instance.game.release=Release +instance.game.releases=Releases +instance.game.snapshot=Snapshot +instance.game.snapshots=Snapshots +instance.game.support_status.unsupported=Unsupported +instance.game.support_status.untested=Untested +instance.game.type=Type +instance.launch=Launch Game +instance.launch_and_enter_world=Play World +instance.launch.empty=Start Game +instance.launch.empty.installing=Installing Game +instance.launch.empty.tooltip=Install and launch the latest official release +instance.launch.test=Test Launch +instance.switch=Switch Instance +instance.launch_script=Export Launch Script +instance.launch_script.failed=Failed to export launch script. +instance.launch_script.save=Export Launch Script +instance.launch_script.success=Exported launch script as %s. +instance.manage=All Instances +instance.manage.clean=Delete Log Files +instance.manage.clean.tooltip=Delete the files in "logs" and "crash-reports" directories. +instance.manage.duplicate=Duplicate Instance +instance.manage.duplicate.duplicate_save=Duplicate Worlds +instance.manage.duplicate.prompt=Enter New Instance Name +instance.manage.duplicate.confirm=The duplicated instance will have a copy of all files in the instance directory (".minecraft/versions/"), with an isolated working directory and settings. +instance.manage.manage=Edit Instance +instance.manage.manage.title=Edit Instance - %1s +instance.manage.redownload_assets_index=Update Game Assets +instance.manage.remove=Delete Instance +instance.manage.remove.confirm.trash=Are you sure you want to remove the instance "%1$s"? You can still find its files in your recycle bin by the name of "%2$s". +instance.manage.remove.confirm.independent=Since this instance is stored in an isolated directory, deleting it will also delete its worlds and other data. Do you still want to delete the instance "%s"? +instance.manage.remove.failed=Failed to delete the instance. Some files might be in use. +instance.manage.remove_assets=Delete All Assets +instance.manage.remove_libraries=Delete All Libraries +instance.manage.rename=Rename Instance +instance.manage.rename.message=Enter New Instance Name +instance.manage.rename.fail=Failed to rename the instance. Some files might be in use, or the name contains an invalid character. +instance.search=Name +instance.search.prompt=Enter the version name to search +instance.settings=Settings +instance.update=Update Modpack + java.add=Add java.add.failed=This Java is invalid or incompatible with the current platform. java.disable=Disable Java @@ -1800,58 +1852,6 @@ update.tooltip=Update update.preview=Preview HMCL releases early update.preview.subtitle=Enable this option to receive new versions of HMCL early for testing before their official release. -version=Games -version.name=Instance Name -version.empty=No Instances -version.empty.add=Add new instance -version.empty.launch=No available instances.\nYou can go to the "Download" page to get the game, or switch the game directory in the "All Instances" page. -version.empty.launch.goto_download=Go to Download Page -version.empty.hint=There are no Minecraft instances here.\nYou can try switching to another game directory or clicking here to download one. -version.game.all=All -version.game.april_fools=April Fools -version.game.old=Historical -version.game.release=Release -version.game.releases=Releases -version.game.snapshot=Snapshot -version.game.snapshots=Snapshots -version.game.support_status.unsupported=Unsupported -version.game.support_status.untested=Untested -version.game.type=Type -version.launch=Launch Game -version.launch_and_enter_world=Play World -version.launch.empty=Start Game -version.launch.empty.installing=Installing Game -version.launch.empty.tooltip=Install and launch the latest official release -version.launch.test=Test Launch -version.switch=Switch Instance -version.launch_script=Export Launch Script -version.launch_script.failed=Failed to export launch script. -version.launch_script.save=Export Launch Script -version.launch_script.success=Exported launch script as %s. -version.manage=All Instances -version.manage.clean=Delete Log Files -version.manage.clean.tooltip=Delete the files in "logs" and "crash-reports" directories. -version.manage.duplicate=Duplicate Instance -version.manage.duplicate.duplicate_save=Duplicate Worlds -version.manage.duplicate.prompt=Enter New Instance Name -version.manage.duplicate.confirm=The duplicated instance will have a copy of all files in the instance directory (".minecraft/versions/"), with an isolated working directory and settings. -version.manage.manage=Edit Instance -version.manage.manage.title=Edit Instance - %1s -version.manage.redownload_assets_index=Update Game Assets -version.manage.remove=Delete Instance -version.manage.remove.confirm.trash=Are you sure you want to remove the instance "%1$s"? You can still find its files in your recycle bin by the name of "%2$s". -version.manage.remove.confirm.independent=Since this instance is stored in an isolated directory, deleting it will also delete its worlds and other data. Do you still want to delete the instance "%s"? -version.manage.remove.failed=Failed to delete the instance. Some files might be in use. -version.manage.remove_assets=Delete All Assets -version.manage.remove_libraries=Delete All Libraries -version.manage.rename=Rename Instance -version.manage.rename.message=Enter New Instance Name -version.manage.rename.fail=Failed to rename the instance. Some files might be in use, or the name contains an invalid character. -version.search=Name -version.search.prompt=Enter the version name to search -version.settings=Settings -version.update=Update Modpack - warning.java_interpreted_mode=HMCL is running in an interpreted Java environment, which will greatly affect performance.\n\ \n\ We recommend using a Java with JIT support to open HMCL for the best experience. diff --git a/HMCL/src/main/resources/assets/lang/I18N_ar.properties b/HMCL/src/main/resources/assets/lang/I18N_ar.properties index 8e3314eab2..5008d63099 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ar.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ar.properties @@ -779,6 +779,58 @@ install.new_game.malformed=اسم غير صالح. install.select=اختر العملية install.success=تم التثبيت بنجاح. +instance=الألعاب +instance.name=اسم النسخة +instance.empty=لا توجد نسخ +instance.empty.add=إضافة نسخة جديدة +instance.empty.launch=لا توجد نسخ متاحة.\nيمكنك الانتقال إلى صفحة "التنزيل" للحصول على اللعبة، أو تغيير مجلد اللعبة في صفحة "جميع النسخ". +instance.empty.launch.goto_download=الانتقال إلى صفحة التنزيل +instance.empty.hint=لا توجد نسخ Minecraft هنا.\nيمكنك محاولة التبديل إلى مجلد لعبة آخر أو النقر هنا لتنزيل واحدة. +instance.game.all=الكل +instance.game.april_fools=كذبة أبريل +instance.game.old=تاريخي +instance.game.release=رسمي +instance.game.releases=الإصدارات الرسمية +instance.game.snapshot=تجريبي +instance.game.snapshots=الإصدارات التجريبية +instance.game.support_status.unsupported=غير مدعوم +instance.game.support_status.untested=غير مختبر +instance.game.type=النوع +instance.launch=تشغيل اللعبة +instance.launch_and_enter_world=لعب في العالم +instance.launch.empty=تشغيل اللعبة +instance.launch.empty.installing=جارٍ تثبيت اللعبة +instance.launch.empty.tooltip=تثبيت وتشغيل أحدث إصدار رسمي +instance.launch.test=تشغيل تجريبي +instance.switch=تبديل النسخة +instance.launch_script=تصدير سكريبت التشغيل +instance.launch_script.failed=فشل تصدير سكريبت التشغيل. +instance.launch_script.save=تصدير سكريبت التشغيل +instance.launch_script.success=تم تصدير سكريبت التشغيل باسم %s. +instance.manage=جميع النسخ +instance.manage.clean=حذف ملفات السجلات +instance.manage.clean.tooltip=حذف الملفات في مجلدي "logs" و"crash-reports". +instance.manage.duplicate=نسخ النسخة +instance.manage.duplicate.duplicate_save=نسخ العوالم +instance.manage.duplicate.prompt=أدخل اسم النسخة الجديدة +instance.manage.duplicate.confirm=ستحتوي النسخة المنسوخة على نسخة من جميع الملفات في مجلد النسخة (".minecraft/versions/<اسم النسخة>")، مع مجلد عمل وإعدادات معزولة. +instance.manage.manage=تعديل النسخة +instance.manage.manage.title=تعديل النسخة - %1s +instance.manage.redownload_assets_index=تحديث أصول اللعبة +instance.manage.remove=حذف النسخة +instance.manage.remove.confirm.trash=هل أنت متأكد من إزالة النسخة "%1$s"؟ لا يزال بإمكانك إيجاد ملفاتها في سلة المحذوفات باسم "%2$s". +instance.manage.remove.confirm.independent=نظراً لأن هذه النسخة مخزَّنة في مجلد معزول، فإن حذفها سيحذف أيضاً عوالمها وبياناتها الأخرى. هل تريد حذف النسخة "%s"؟ +instance.manage.remove.failed=فشل حذف النسخة. قد تكون بعض الملفات قيد الاستخدام. +instance.manage.remove_assets=حذف جميع الأصول +instance.manage.remove_libraries=حذف جميع المكتبات +instance.manage.rename=إعادة تسمية النسخة +instance.manage.rename.message=أدخل اسم النسخة الجديدة +instance.manage.rename.fail=فشل إعادة تسمية النسخة. قد تكون بعض الملفات قيد الاستخدام، أو يحتوي الاسم على حرف غير صالح. +instance.search=الاسم +instance.search.prompt=أدخل اسم النسخة للبحث +instance.settings=الإعدادات +instance.update=تحديث حزمة المودات + java.add=إضافة java.add.failed=هذا Java غير صالح أو غير متوافق مع المنصة الحالية. java.disable=تعطيل Java @@ -1566,58 +1618,6 @@ update.tooltip=تحديث update.preview=الاطلاع المبكر على إصدارات HMCL update.preview.subtitle=فعّل هذا الخيار لاستقبال إصدارات HMCL الجديدة مبكراً للاختبار قبل إصدارها الرسمي. -version=الألعاب -version.name=اسم النسخة -version.empty=لا توجد نسخ -version.empty.add=إضافة نسخة جديدة -version.empty.launch=لا توجد نسخ متاحة.\nيمكنك الانتقال إلى صفحة "التنزيل" للحصول على اللعبة، أو تغيير مجلد اللعبة في صفحة "جميع النسخ". -version.empty.launch.goto_download=الانتقال إلى صفحة التنزيل -version.empty.hint=لا توجد نسخ Minecraft هنا.\nيمكنك محاولة التبديل إلى مجلد لعبة آخر أو النقر هنا لتنزيل واحدة. -version.game.all=الكل -version.game.april_fools=كذبة أبريل -version.game.old=تاريخي -version.game.release=رسمي -version.game.releases=الإصدارات الرسمية -version.game.snapshot=تجريبي -version.game.snapshots=الإصدارات التجريبية -version.game.support_status.unsupported=غير مدعوم -version.game.support_status.untested=غير مختبر -version.game.type=النوع -version.launch=تشغيل اللعبة -version.launch_and_enter_world=لعب في العالم -version.launch.empty=تشغيل اللعبة -version.launch.empty.installing=جارٍ تثبيت اللعبة -version.launch.empty.tooltip=تثبيت وتشغيل أحدث إصدار رسمي -version.launch.test=تشغيل تجريبي -version.switch=تبديل النسخة -version.launch_script=تصدير سكريبت التشغيل -version.launch_script.failed=فشل تصدير سكريبت التشغيل. -version.launch_script.save=تصدير سكريبت التشغيل -version.launch_script.success=تم تصدير سكريبت التشغيل باسم %s. -version.manage=جميع النسخ -version.manage.clean=حذف ملفات السجلات -version.manage.clean.tooltip=حذف الملفات في مجلدي "logs" و"crash-reports". -version.manage.duplicate=نسخ النسخة -version.manage.duplicate.duplicate_save=نسخ العوالم -version.manage.duplicate.prompt=أدخل اسم النسخة الجديدة -version.manage.duplicate.confirm=ستحتوي النسخة المنسوخة على نسخة من جميع الملفات في مجلد النسخة (".minecraft/versions/<اسم النسخة>")، مع مجلد عمل وإعدادات معزولة. -version.manage.manage=تعديل النسخة -version.manage.manage.title=تعديل النسخة - %1s -version.manage.redownload_assets_index=تحديث أصول اللعبة -version.manage.remove=حذف النسخة -version.manage.remove.confirm.trash=هل أنت متأكد من إزالة النسخة "%1$s"؟ لا يزال بإمكانك إيجاد ملفاتها في سلة المحذوفات باسم "%2$s". -version.manage.remove.confirm.independent=نظراً لأن هذه النسخة مخزَّنة في مجلد معزول، فإن حذفها سيحذف أيضاً عوالمها وبياناتها الأخرى. هل تريد حذف النسخة "%s"؟ -version.manage.remove.failed=فشل حذف النسخة. قد تكون بعض الملفات قيد الاستخدام. -version.manage.remove_assets=حذف جميع الأصول -version.manage.remove_libraries=حذف جميع المكتبات -version.manage.rename=إعادة تسمية النسخة -version.manage.rename.message=أدخل اسم النسخة الجديدة -version.manage.rename.fail=فشل إعادة تسمية النسخة. قد تكون بعض الملفات قيد الاستخدام، أو يحتوي الاسم على حرف غير صالح. -version.search=الاسم -version.search.prompt=أدخل اسم النسخة للبحث -version.settings=الإعدادات -version.update=تحديث حزمة المودات - warning.java_interpreted_mode=يعمل HMCL في بيئة Java مُترجمة تفسيرياً، مما سيؤثر بشكل كبير على الأداء.\n\ \n\ نوصي باستخدام Java يدعم JIT لفتح HMCL للحصول على أفضل تجربة. diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index b141caba87..bb70694832 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -743,6 +743,51 @@ install.new_game.malformed=Nombre no válido. install.select=Elegir una operación install.success=Instalado con éxito. +instance=Juegos +instance.name=Nombre de instancia +instance.empty=No hay instancias +instance.empty.add=Añadir una instancia +instance.empty.hint=No hay instancias de Minecraft aquí.\nPuedes intentar cambiar a otro directorio del juego o hacer clic aquí para descargar una. +instance.game.all=Todos +instance.game.april_fools=Día de los Inocentes +instance.game.old=Histórico +instance.game.release=Lanzamiento +instance.game.releases=Lanzamientos +instance.game.snapshot=Snapshot +instance.game.snapshots=Snapshots +instance.game.type=Tipo +instance.launch=Iniciar Minecraft +instance.launch.empty=Iniciar Minecraft +instance.launch.empty.installing=Instalando el juego +instance.launch.empty.tooltip=Instala y ejecuta la última versión oficial del juego. +instance.launch.test=Probar instalación +instance.switch=Cambiar Instancia +instance.launch_script=Exportar script de ejecución +instance.launch_script.failed=No se ha podido exportar el script de ejecución. +instance.launch_script.save=Exportar script de ejecución +instance.launch_script.success=Exportado el script de ejecución como %s. +instance.manage=Todas las instancias +instance.manage.clean=Eliminar archivos de registros +instance.manage.clean.tooltip=Eliminar los archivos de los directorios «logs» y «crash-reports». +instance.manage.duplicate=Duplicar instancia +instance.manage.duplicate.duplicate_save=Duplicar mundos +instance.manage.duplicate.prompt=Introduzca el nombre de la nueva instancia +instance.manage.duplicate.confirm=La instancia duplicada tendrá una copia de todos los archivos de esta instancia, con un directorio de juego y una configuración aislados. +instance.manage.manage=Editar instancia +instance.manage.manage.title=Editar instancia - %1s +instance.manage.redownload_assets_index=Actualizar activos del juego +instance.manage.remove=Eliminar instancia +instance.manage.remove.confirm.trash=¿Estás seguro de que quieres eliminar la instancia «%s»? Todavía puedes encontrar sus archivos en tu papelera de reciclaje con el nombre de «%s». +instance.manage.remove_assets=Borrar todas las activos del juego +instance.manage.remove_libraries=Borrar todas las bibliotecas +instance.manage.rename=Renombrar instancia +instance.manage.rename.message=Introduzca el nombre de la nueva instancia +instance.manage.rename.fail=No se ha podido renombrar la instancia, algunos archivos pueden estar en uso o el nombre contiene un carácter no válido. +instance.search=Nombre +instance.search.prompt=Introduzca el nombre de la versión para buscar. +instance.settings=Configuración +instance.update=Actualizar Modpack + java.add.failed=Este Java no es válido o es incompatible con la plataforma actual. java.disable=Deshabilitar este Java java.disable.confirm=¿Está seguro de que desea desactivar este Java? @@ -1379,51 +1424,6 @@ update.tooltip=Actualización update.preview=Vista previa de actualizaciones anticipadas update.preview.subtitle=Activa esta opción para recibir nuevas versiones del lanzador antes de su lanzamiento oficial para probarlas -version=Juegos -version.name=Nombre de instancia -version.empty=No hay instancias -version.empty.add=Añadir una instancia -version.empty.hint=No hay instancias de Minecraft aquí.\nPuedes intentar cambiar a otro directorio del juego o hacer clic aquí para descargar una. -version.game.all=Todos -version.game.april_fools=Día de los Inocentes -version.game.old=Histórico -version.game.release=Lanzamiento -version.game.releases=Lanzamientos -version.game.snapshot=Snapshot -version.game.snapshots=Snapshots -version.game.type=Tipo -version.launch=Iniciar Minecraft -version.launch.empty=Iniciar Minecraft -version.launch.empty.installing=Instalando el juego -version.launch.empty.tooltip=Instala y ejecuta la última versión oficial del juego. -version.launch.test=Probar instalación -version.switch=Cambiar Instancia -version.launch_script=Exportar script de ejecución -version.launch_script.failed=No se ha podido exportar el script de ejecución. -version.launch_script.save=Exportar script de ejecución -version.launch_script.success=Exportado el script de ejecución como %s. -version.manage=Todas las instancias -version.manage.clean=Eliminar archivos de registros -version.manage.clean.tooltip=Eliminar los archivos de los directorios «logs» y «crash-reports». -version.manage.duplicate=Duplicar instancia -version.manage.duplicate.duplicate_save=Duplicar mundos -version.manage.duplicate.prompt=Introduzca el nombre de la nueva instancia -version.manage.duplicate.confirm=La instancia duplicada tendrá una copia de todos los archivos de esta instancia, con un directorio de juego y una configuración aislados. -version.manage.manage=Editar instancia -version.manage.manage.title=Editar instancia - %1s -version.manage.redownload_assets_index=Actualizar activos del juego -version.manage.remove=Eliminar instancia -version.manage.remove.confirm.trash=¿Estás seguro de que quieres eliminar la instancia «%s»? Todavía puedes encontrar sus archivos en tu papelera de reciclaje con el nombre de «%s». -version.manage.remove_assets=Borrar todas las activos del juego -version.manage.remove_libraries=Borrar todas las bibliotecas -version.manage.rename=Renombrar instancia -version.manage.rename.message=Introduzca el nombre de la nueva instancia -version.manage.rename.fail=No se ha podido renombrar la instancia, algunos archivos pueden estar en uso o el nombre contiene un carácter no válido. -version.search=Nombre -version.search.prompt=Introduzca el nombre de la versión para buscar. -version.settings=Configuración -version.update=Actualizar Modpack - warning.java_interpreted_mode=El lanzador se ejecuta en un entorno Java interpretado, lo que afectará considerablemente al rendimiento.\n\ \n\ Recomendamos utilizar Java con soporte JIT para ejecutar el lanzador y obtener la mejor experiencia. diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index a54ed75ebc..faa409ba03 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -476,6 +476,42 @@ install.new_game.malformed=無効な名前 install.select=操作を選択します install.success=正常にインストールされました +instance=ゲーム +instance.empty=ゲームなし +instance.empty.add=起動構成を作成 +instance.empty.hint=インストールされているゲームバージョンはありません。新しいゲームをダウンロードするには、ここをクリックしてください。 +instance.game.old=Old +instance.game.release=リリース +instance.game.releases=リリース +instance.game.snapshot=スナップショット +instance.game.snapshots=Snapshots +instance.launch=プレイ +instance.launch.test=テストプレイ +instance.switch=バージョンを切り替える +instance.launch_script=起動スクリプトを作成する +instance.launch_script.failed=起動スクリプトを作成できません。 +instance.launch_script.save=起動スクリプトを保存する +instance.launch_script.success=作成されたスクリプト: %s。 +instance.manage=起動構成 +instance.manage.clean=ゲームディレクトリをクリアする +instance.manage.clean.tooltip=ログのクリア、クラッシュレポート +instance.manage.duplicate=ゲームインスタンスをコピーする +instance.manage.duplicate.duplicate_save=コピー保存 +instance.manage.duplicate.prompt=新しいバージョン名を入力してください +instance.manage.duplicate.confirm= +instance.manage.manage=バージョンの管理 +instance.manage.manage.title=バージョンの管理 - %1s +instance.manage.redownload_assets_index=ゲームアセットファイルの更新 +instance.manage.remove=このバージョンを削除します +instance.manage.remove.confirm.trash=このバージョン %s を削除してもよろしいですか?このバージョンは、システムのゴミ箱に %s という名前で復元できます。 +instance.manage.remove_assets=すべてのゲームリソースファイルの削除 +instance.manage.remove_libraries=ライブラリファイルを削除します +instance.manage.rename=このバージョンの名前を変更します +instance.manage.rename.message=このバージョンの新しい名前を入力してください +instance.manage.rename.fail=バージョンの名前を変更できませんでした。 +instance.settings=設定 +instance.update=modpackを更新する + java.add.failed=このJavaは無効であるか、現在のプラットフォームと互換性がない。 java.disable=無効化 java.disable.confirm=本当にこのJavaを無効にしますか? @@ -902,42 +938,6 @@ update.latest=これは最新バージョンです。 update.no_browser=ブラウザを開くことができません。リンクがクリップボードにコピーされました。ブラウザのアドレスバーに貼り付けて更新します。 update.tooltip=更新 -version=ゲーム -version.empty=ゲームなし -version.empty.add=起動構成を作成 -version.empty.hint=インストールされているゲームバージョンはありません。新しいゲームをダウンロードするには、ここをクリックしてください。 -version.game.old=Old -version.game.release=リリース -version.game.releases=リリース -version.game.snapshot=スナップショット -version.game.snapshots=Snapshots -version.launch=プレイ -version.launch.test=テストプレイ -version.switch=バージョンを切り替える -version.launch_script=起動スクリプトを作成する -version.launch_script.failed=起動スクリプトを作成できません。 -version.launch_script.save=起動スクリプトを保存する -version.launch_script.success=作成されたスクリプト: %s。 -version.manage=起動構成 -version.manage.clean=ゲームディレクトリをクリアする -version.manage.clean.tooltip=ログのクリア、クラッシュレポート -version.manage.duplicate=ゲームインスタンスをコピーする -version.manage.duplicate.duplicate_save=コピー保存 -version.manage.duplicate.prompt=新しいバージョン名を入力してください -version.manage.duplicate.confirm= -version.manage.manage=バージョンの管理 -version.manage.manage.title=バージョンの管理 - %1s -version.manage.redownload_assets_index=ゲームアセットファイルの更新 -version.manage.remove=このバージョンを削除します -version.manage.remove.confirm.trash=このバージョン %s を削除してもよろしいですか?このバージョンは、システムのゴミ箱に %s という名前で復元できます。 -version.manage.remove_assets=すべてのゲームリソースファイルの削除 -version.manage.remove_libraries=ライブラリファイルを削除します -version.manage.rename=このバージョンの名前を変更します -version.manage.rename.message=このバージョンの新しい名前を入力してください -version.manage.rename.fail=バージョンの名前を変更できませんでした。 -version.settings=設定 -version.update=modpackを更新する - wiki.version.game=https://ja.minecraft.wiki/w/Java_Edition_%s wiki.version.game.snapshot=https://ja.minecraft.wiki/w/Java_Edition_%s diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 530a12a44b..c8f7afb618 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -559,6 +559,52 @@ install.new_game.malformed=逾矩之名 install.select=擇一裝法 install.success=裝成 +instance=戲 +instance.name=戲例之名 +instance.empty=無游戲例 +instance.empty.add=入引頁以裝戲 +instance.empty.hint=無畢裝者。\n君且更他戲夾,抑擊斯以往戲之引頁。 +instance.game.all=全覽 +instance.game.april_fools=愚人節 +instance.game.old=古典版 +instance.game.release=當版 +instance.game.releases=當版 +instance.game.snapshot=預版 +instance.game.snapshots=預版 +instance.game.type=版類 +instance.launch=戲 +instance.launch.empty=啟戲 +instance.launch.empty.installing=裝戲 +instance.launch.empty.tooltip=裝啟至新之當版 +instance.launch.test=試其戲 +instance.switch=迭例 +instance.launch_script=成啟戲角本 +instance.launch_script.failed=弗能成啟戲角本 +instance.launch_script.save=存啟戲角本 +instance.launch_script.success=啟戲角本既成:%s +instance.manage=例列 +instance.manage.clean=掃戲之案夾 +instance.manage.clean.tooltip=除空「logs」與「crash-reports」案夾 +instance.manage.duplicate=鈔戲例 +instance.manage.duplicate.duplicate_save=鈔生界 +instance.manage.duplicate.prompt=書新戲之名 +instance.manage.duplicate.confirm=新戲將鈔例夾 (「.minecraft/versions/<例名>」) 之案,並另立行案、置設。 +instance.manage.manage=司例 +instance.manage.manage.title=司例 - %1s +instance.manage.redownload_assets_index=新戲之資案 +instance.manage.remove=除是例 +instance.manage.remove.confirm.trash=誠刪例「%s」乎?君猶可復之,蓋復案夾「%s」於棄案站也。 +instance.manage.remove.confirm.independent=以戲之用「(全例/例殊) 戲設 → 運徑 → 例獨」项,除之即咸除戲之檔與數矣!君其欲除之「%s」而後快邪? +instance.manage.remove_assets=刪戲之資案 +instance.manage.remove_libraries=率刪庫案 +instance.manage.rename=更名是例 +instance.manage.rename.message=告新名 +instance.manage.rename.fail=更名未成,或名有諱,或案見用。 +instance.search=名 +instance.search.prompt=書版名以尋 +instance.settings=戲設 +instance.update=更迭改囊集 + java.add=增爪哇 java.add.failed=爪哇謬抑弗兼此算機。 java.disable=禁此爪哇 @@ -1208,52 +1254,6 @@ update.tooltip=迭更 update.preview=先覽 HMCL 之版 update.preview.subtitle=啟此,君可先得 HMCL 之新,試之於未行。 -version=戲 -version.name=戲例之名 -version.empty=無游戲例 -version.empty.add=入引頁以裝戲 -version.empty.hint=無畢裝者。\n君且更他戲夾,抑擊斯以往戲之引頁。 -version.game.all=全覽 -version.game.april_fools=愚人節 -version.game.old=古典版 -version.game.release=當版 -version.game.releases=當版 -version.game.snapshot=預版 -version.game.snapshots=預版 -version.game.type=版類 -version.launch=戲 -version.launch.empty=啟戲 -version.launch.empty.installing=裝戲 -version.launch.empty.tooltip=裝啟至新之當版 -version.launch.test=試其戲 -version.switch=迭例 -version.launch_script=成啟戲角本 -version.launch_script.failed=弗能成啟戲角本 -version.launch_script.save=存啟戲角本 -version.launch_script.success=啟戲角本既成:%s -version.manage=例列 -version.manage.clean=掃戲之案夾 -version.manage.clean.tooltip=除空「logs」與「crash-reports」案夾 -version.manage.duplicate=鈔戲例 -version.manage.duplicate.duplicate_save=鈔生界 -version.manage.duplicate.prompt=書新戲之名 -version.manage.duplicate.confirm=新戲將鈔例夾 (「.minecraft/versions/<例名>」) 之案,並另立行案、置設。 -version.manage.manage=司例 -version.manage.manage.title=司例 - %1s -version.manage.redownload_assets_index=新戲之資案 -version.manage.remove=除是例 -version.manage.remove.confirm.trash=誠刪例「%s」乎?君猶可復之,蓋復案夾「%s」於棄案站也。 -version.manage.remove.confirm.independent=以戲之用「(全例/例殊) 戲設 → 運徑 → 例獨」项,除之即咸除戲之檔與數矣!君其欲除之「%s」而後快邪? -version.manage.remove_assets=刪戲之資案 -version.manage.remove_libraries=率刪庫案 -version.manage.rename=更名是例 -version.manage.rename.message=告新名 -version.manage.rename.fail=更名未成,或名有諱,或案見用。 -version.search=名 -version.search.prompt=書版名以尋 -version.settings=戲設 -version.update=更迭改囊集 - wiki.tooltip=礦藝大典之葉 wiki.version.game=https://lzh.minecraft.wiki/w/爪哇版%s wiki.version.game.snapshot=https://lzh.minecraft.wiki/w/%s diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index 8e6619dc13..b65431c346 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -734,6 +734,52 @@ install.new_game.malformed=Недопустимое имя install.select=Выберите операцию install.success=Успешно установлено. +instance=Игры +instance.name=Название сборки +instance.empty=Нет сборок +instance.empty.add=Установить новую сборок +instance.empty.hint=Нет сборок.\nВы можете попробовать перейти в другую папку с игрой или нажать здесь, чтобы скачать игру. +instance.game.all=Все +instance.game.april_fools=День смеха +instance.game.old=Исторические +instance.game.release=Релиз +instance.game.releases=Релизы +instance.game.snapshot=Снапшот +instance.game.snapshots=Снапшоты +instance.game.type=Тип +instance.launch=Играть +instance.launch.empty=Играть +instance.launch.empty.installing=Установка игры +instance.launch.empty.tooltip=Установить и запустить последнюю официальную версию игры. +instance.launch.test=Тестовая игра +instance.switch=Сменить сборку +instance.launch_script=Создать сценарий запуска +instance.launch_script.failed=Невозможно создать сценарий запуска. +instance.launch_script.save=Сохранить сценарий запуска +instance.launch_script.success=Создан сценарий %s. +instance.manage=Все сборки +instance.manage.clean=Очистить журнал игры +instance.manage.clean.tooltip=Очистить файлы в папках «logs» и «crash-reports». +instance.manage.duplicate=Скопировать экземпляр сборки +instance.manage.duplicate.duplicate_save=Скопировать миры +instance.manage.duplicate.prompt=Введите новое название для этой сборки +instance.manage.duplicate.confirm=Скопированная сборка будет иметь копию всех файлов в папке сборки(«.minecraft/versions/<имя сборки>»), с изолированной рабочей папкой и настройками. +instance.manage.manage=Изменить сборку +instance.manage.manage.title=Изменить сборку - %1s +instance.manage.redownload_assets_index=Обновить файлы игровых активов +instance.manage.remove=Удалить +instance.manage.remove.confirm.trash=Удалить %s? Вы можете восстановить эту сборку с именем %s из корзины системы. +instance.manage.remove.confirm.independent=Поскольку эта сборка находится в режиме разделения, удаление этой сборки приведет к удалению всех миров, принадлежащих этой сборке. Удалить %s? +instance.manage.remove_assets=Удалить файлы игровых активов +instance.manage.remove_libraries=Удалить файлы библиотек +instance.manage.rename=Переименовать +instance.manage.rename.message=Введите новое название для этой сборки +instance.manage.rename.fail=Не удалось переименовать сборку. Возможно, некоторые файлы уже используются или в их имени содержится недопустимый символ. +instance.search=Название +instance.search.prompt=Введите название версии для поиска +instance.settings=Настройки +instance.update=Обновить модпак + java.add.failed=Этот Java недопустим или несовместим с текущей платформой. java.disable=Отключить Java java.disable.confirm=Вы уверены, что хотите отключить эту Java? @@ -1366,52 +1412,6 @@ update.latest=Это последняя версия. update.no_browser=Не удалось открыть браузер. Ссылка была скопирована в буфер обмена. Вставьте его в адресную строку браузера для обновления. update.tooltip=Обновить -version=Игры -version.name=Название сборки -version.empty=Нет сборок -version.empty.add=Установить новую сборок -version.empty.hint=Нет сборок.\nВы можете попробовать перейти в другую папку с игрой или нажать здесь, чтобы скачать игру. -version.game.all=Все -version.game.april_fools=День смеха -version.game.old=Исторические -version.game.release=Релиз -version.game.releases=Релизы -version.game.snapshot=Снапшот -version.game.snapshots=Снапшоты -version.game.type=Тип -version.launch=Играть -version.launch.empty=Играть -version.launch.empty.installing=Установка игры -version.launch.empty.tooltip=Установить и запустить последнюю официальную версию игры. -version.launch.test=Тестовая игра -version.switch=Сменить сборку -version.launch_script=Создать сценарий запуска -version.launch_script.failed=Невозможно создать сценарий запуска. -version.launch_script.save=Сохранить сценарий запуска -version.launch_script.success=Создан сценарий %s. -version.manage=Все сборки -version.manage.clean=Очистить журнал игры -version.manage.clean.tooltip=Очистить файлы в папках «logs» и «crash-reports». -version.manage.duplicate=Скопировать экземпляр сборки -version.manage.duplicate.duplicate_save=Скопировать миры -version.manage.duplicate.prompt=Введите новое название для этой сборки -version.manage.duplicate.confirm=Скопированная сборка будет иметь копию всех файлов в папке сборки(«.minecraft/versions/<имя сборки>»), с изолированной рабочей папкой и настройками. -version.manage.manage=Изменить сборку -version.manage.manage.title=Изменить сборку - %1s -version.manage.redownload_assets_index=Обновить файлы игровых активов -version.manage.remove=Удалить -version.manage.remove.confirm.trash=Удалить %s? Вы можете восстановить эту сборку с именем %s из корзины системы. -version.manage.remove.confirm.independent=Поскольку эта сборка находится в режиме разделения, удаление этой сборки приведет к удалению всех миров, принадлежащих этой сборке. Удалить %s? -version.manage.remove_assets=Удалить файлы игровых активов -version.manage.remove_libraries=Удалить файлы библиотек -version.manage.rename=Переименовать -version.manage.rename.message=Введите новое название для этой сборки -version.manage.rename.fail=Не удалось переименовать сборку. Возможно, некоторые файлы уже используются или в их имени содержится недопустимый символ. -version.search=Название -version.search.prompt=Введите название версии для поиска -version.settings=Настройки -version.update=Обновить модпак - wiki.tooltip=Страница Minecraft Wiki wiki.version.game=https://ru.minecraft.wiki/w/%s_(Java_Edition) wiki.version.game.snapshot=https://ru.minecraft.wiki/w/%s_(Java_Edition) diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index fa992f1288..1bc2362db9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -685,6 +685,50 @@ install.new_game.malformed=Недійсна назва. install.select=Виберіть операцію install.success=Успішно встановлено. +instance=Ігри +instance.name=Назва екземпляра +instance.empty=Немає екземплярів +instance.empty.add=Додати новий екземпляр +instance.empty.hint=Тут немає екземплярів Minecraft. Ви можете спробувати переключитися на інший ігровий каталог або натиснути тут, щоб завантажити один. +instance.game.all=Усі +instance.game.april_fools=Перший квітня +instance.game.old=Історичні +instance.game.release=Реліз +instance.game.releases=Релізи +instance.game.snapshot=Снапшот +instance.game.snapshots=Снапшоти +instance.game.type=Тип +instance.launch=Запустити гру +instance.launch.empty=Почати гру +instance.launch.empty.installing=Встановлення гри +instance.launch.empty.tooltip=Встановити та запустити останній офіційний реліз +instance.launch.test=Тестовий запуск +instance.switch=Переключити екземпляр +instance.launch_script=Експортувати скрипт запуску +instance.launch_script.failed=Не вдалося експортувати скрипт запуску. +instance.launch_script.save=Експортувати скрипт запуску +instance.launch_script.success=Експортовано скрипт запуску як %s. +instance.manage=Усі екземпляри +instance.manage.clean=Видалити файли журналів +instance.manage.clean.tooltip=Видалити файли в каталогах "logs" та "crash-reports". +instance.manage.duplicate=Дублювати екземпляр +instance.manage.duplicate.prompt=Введіть нову назву екземпляра +instance.manage.duplicate.confirm=Дубльований екземпляр матиме копію всіх файлів у каталозі екземпляра (".minecraft/versions/<назва екземпляра>"), з ізольованим робочим каталогом та налаштуваннями. +instance.manage.manage=Редагувати екземпляр +instance.manage.manage.title=Редагувати екземпляр - %1s +instance.manage.redownload_assets_index=Оновити ресурси гри +instance.manage.remove=Видалити екземпляр +instance.manage.remove.confirm.trash=Ви впевнені, що хочете видалити екземпляр "%s"? Ви все ще можете знайти його файли у своєму кошику за назвою "%s". +instance.manage.remove_assets=Видалити всі ресурси +instance.manage.remove_libraries=Видалити всі бібліотеки +instance.manage.rename=Перейменувати екземпляр +instance.manage.rename.message=Введіть нову назву екземпляра +instance.manage.rename.fail=Не вдалося перейменувати екземпляр. Деякі файли можуть використовуватися або назва містить недійсний символ. +instance.search=Назва +instance.search.prompt=Введіть назву версії для пошуку +instance.settings=Налаштування +instance.update=Оновити модпак + java.add.failed=Ця Java недійсна або несумісна з поточною платформою. java.disable=Вимкнути Java java.disable.confirm=Ви впевнені, що хочете вимкнути цю Java? @@ -1319,50 +1363,6 @@ update.latest=Це остання версія update.no_browser=Не вдається відкрити в системному браузері. Але ми скопіювали посилання до вашого буфера обміну, і ви можете відкрити його вручну. update.tooltip=Оновити -version=Ігри -version.name=Назва екземпляра -version.empty=Немає екземплярів -version.empty.add=Додати новий екземпляр -version.empty.hint=Тут немає екземплярів Minecraft. Ви можете спробувати переключитися на інший ігровий каталог або натиснути тут, щоб завантажити один. -version.game.all=Усі -version.game.april_fools=Перший квітня -version.game.old=Історичні -version.game.release=Реліз -version.game.releases=Релізи -version.game.snapshot=Снапшот -version.game.snapshots=Снапшоти -version.game.type=Тип -version.launch=Запустити гру -version.launch.empty=Почати гру -version.launch.empty.installing=Встановлення гри -version.launch.empty.tooltip=Встановити та запустити останній офіційний реліз -version.launch.test=Тестовий запуск -version.switch=Переключити екземпляр -version.launch_script=Експортувати скрипт запуску -version.launch_script.failed=Не вдалося експортувати скрипт запуску. -version.launch_script.save=Експортувати скрипт запуску -version.launch_script.success=Експортовано скрипт запуску як %s. -version.manage=Усі екземпляри -version.manage.clean=Видалити файли журналів -version.manage.clean.tooltip=Видалити файли в каталогах "logs" та "crash-reports". -version.manage.duplicate=Дублювати екземпляр -version.manage.duplicate.prompt=Введіть нову назву екземпляра -version.manage.duplicate.confirm=Дубльований екземпляр матиме копію всіх файлів у каталозі екземпляра (".minecraft/versions/<назва екземпляра>"), з ізольованим робочим каталогом та налаштуваннями. -version.manage.manage=Редагувати екземпляр -version.manage.manage.title=Редагувати екземпляр - %1s -version.manage.redownload_assets_index=Оновити ресурси гри -version.manage.remove=Видалити екземпляр -version.manage.remove.confirm.trash=Ви впевнені, що хочете видалити екземпляр "%s"? Ви все ще можете знайти його файли у своєму кошику за назвою "%s". -version.manage.remove_assets=Видалити всі ресурси -version.manage.remove_libraries=Видалити всі бібліотеки -version.manage.rename=Перейменувати екземпляр -version.manage.rename.message=Введіть нову назву екземпляра -version.manage.rename.fail=Не вдалося перейменувати екземпляр. Деякі файли можуть використовуватися або назва містить недійсний символ. -version.search=Назва -version.search.prompt=Введіть назву версії для пошуку -version.settings=Налаштування -version.update=Оновити модпак - wiki.tooltip=Сторінка Minecraft Wiki wiki.version.game=https://uk.minecraft.wiki/w/%s_(Java_Edition) wiki.version.game.snapshot=https://uk.minecraft.wiki/w/%s_(Java_Edition) diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 926aaf4a43..670265444f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -623,6 +623,58 @@ install.new_game.malformed_json=無效的 Minecraft 實例 JSON 文件。 install.select=請選取安裝方式 install.success=安裝成功 +instance=遊戲 +instance.name=遊戲實例名稱 +instance.empty=沒有遊戲實例 +instance.empty.add=進入下載頁安裝遊戲 +instance.empty.launch=沒有可啟動的遊戲。\n你可以前往「下載」頁面下載新遊戲,或在「實例清單」切換遊戲目錄。 +instance.empty.launch.goto_download=前往下載頁面 +instance.empty.hint=沒有已安裝的遊戲。\n你可以切換其他遊戲目錄,或者點擊此處進入遊戲下載頁面。 +instance.game.all=全部 +instance.game.april_fools=愚人節 +instance.game.old=遠古版 +instance.game.release=正式版 +instance.game.releases=正式版 +instance.game.snapshot=快照 +instance.game.snapshots=快照 +instance.game.support_status.unsupported=不支援 +instance.game.support_status.untested=未經測試 +instance.game.type=版本類型 +instance.launch=啟動遊戲 +instance.launch_and_enter_world=進入世界 +instance.launch.empty=開始遊戲 +instance.launch.empty.installing=安裝遊戲 +instance.launch.empty.tooltip=安裝並啟動最新正式版遊戲 +instance.launch.test=測試遊戲 +instance.switch=切換實例 +instance.launch_script=生成啟動指令碼 +instance.launch_script.failed=生成啟動指令碼失敗 +instance.launch_script.save=儲存啟動指令碼 +instance.launch_script.success=啟動指令碼已生成完畢: %s +instance.manage=實例清單 +instance.manage.clean=清理遊戲目錄 +instance.manage.clean.tooltip=清理「logs」與「crash-reports」目錄 +instance.manage.duplicate=複製遊戲實例 +instance.manage.duplicate.duplicate_save=複製世界 +instance.manage.duplicate.prompt=請輸入新遊戲實例名稱 +instance.manage.duplicate.confirm=新的遊戲將複製該實例目錄 (".minecraft/versions/<實例名>") 下的檔案,並帶有獨立的執行目錄和設定。 +instance.manage.manage=實例管理 +instance.manage.manage.title=實例管理 - %1s +instance.manage.redownload_assets_index=更新遊戲資源檔案 +instance.manage.remove=刪除該實例 +instance.manage.remove.confirm.trash=真的要刪除實例「%s」嗎? 你可以在系統的資源回收筒 (或垃圾桶) 中還原目錄「%s」來找回該實例。 +instance.manage.remove.confirm.independent=由於該實例啟用了「(全域/實例特定) 遊戲設定 → 執行路徑 → 各實例獨立」設定,刪除該實例將導致該遊戲的世界等資料一同被刪除!真的要刪除實例「%s」嗎? +instance.manage.remove.failed=刪除實例失敗。可能檔案被占用。 +instance.manage.remove_assets=刪除所有遊戲資源檔案 +instance.manage.remove_libraries=刪除所有支援庫檔案 +instance.manage.rename=重新命名該實例 +instance.manage.rename.message=請輸入新名稱 +instance.manage.rename.fail=重新命名實例失敗,可能檔案被佔用或者名稱有特殊字元。 +instance.search=名稱 +instance.search.prompt=輸入版本名稱進行搜尋 +instance.settings=遊戲設定 +instance.update=更新模組包 + java.add=新增 Java java.add.failed=Java 無效或與目前平臺不相容 java.disable=停用此 Java @@ -1587,58 +1639,6 @@ update.tooltip=更新 update.preview=提前測試 HMCL 預覽版本 update.preview.subtitle=啟用此選項,你將可以提前取得 HMCL 的新版本,以便在正式發布前進行測試。 -version=遊戲 -version.name=遊戲實例名稱 -version.empty=沒有遊戲實例 -version.empty.add=進入下載頁安裝遊戲 -version.empty.launch=沒有可啟動的遊戲。\n你可以前往「下載」頁面下載新遊戲,或在「實例清單」切換遊戲目錄。 -version.empty.launch.goto_download=前往下載頁面 -version.empty.hint=沒有已安裝的遊戲。\n你可以切換其他遊戲目錄,或者點擊此處進入遊戲下載頁面。 -version.game.all=全部 -version.game.april_fools=愚人節 -version.game.old=遠古版 -version.game.release=正式版 -version.game.releases=正式版 -version.game.snapshot=快照 -version.game.snapshots=快照 -version.game.support_status.unsupported=不支援 -version.game.support_status.untested=未經測試 -version.game.type=版本類型 -version.launch=啟動遊戲 -version.launch_and_enter_world=進入世界 -version.launch.empty=開始遊戲 -version.launch.empty.installing=安裝遊戲 -version.launch.empty.tooltip=安裝並啟動最新正式版遊戲 -version.launch.test=測試遊戲 -version.switch=切換實例 -version.launch_script=生成啟動指令碼 -version.launch_script.failed=生成啟動指令碼失敗 -version.launch_script.save=儲存啟動指令碼 -version.launch_script.success=啟動指令碼已生成完畢: %s -version.manage=實例清單 -version.manage.clean=清理遊戲目錄 -version.manage.clean.tooltip=清理「logs」與「crash-reports」目錄 -version.manage.duplicate=複製遊戲實例 -version.manage.duplicate.duplicate_save=複製世界 -version.manage.duplicate.prompt=請輸入新遊戲實例名稱 -version.manage.duplicate.confirm=新的遊戲將複製該實例目錄 (".minecraft/versions/<實例名>") 下的檔案,並帶有獨立的執行目錄和設定。 -version.manage.manage=實例管理 -version.manage.manage.title=實例管理 - %1s -version.manage.redownload_assets_index=更新遊戲資源檔案 -version.manage.remove=刪除該實例 -version.manage.remove.confirm.trash=真的要刪除實例「%s」嗎? 你可以在系統的資源回收筒 (或垃圾桶) 中還原目錄「%s」來找回該實例。 -version.manage.remove.confirm.independent=由於該實例啟用了「(全域/實例特定) 遊戲設定 → 執行路徑 → 各實例獨立」設定,刪除該實例將導致該遊戲的世界等資料一同被刪除!真的要刪除實例「%s」嗎? -version.manage.remove.failed=刪除實例失敗。可能檔案被占用。 -version.manage.remove_assets=刪除所有遊戲資源檔案 -version.manage.remove_libraries=刪除所有支援庫檔案 -version.manage.rename=重新命名該實例 -version.manage.rename.message=請輸入新名稱 -version.manage.rename.fail=重新命名實例失敗,可能檔案被佔用或者名稱有特殊字元。 -version.search=名稱 -version.search.prompt=輸入版本名稱進行搜尋 -version.settings=遊戲設定 -version.update=更新模組包 - warning.java_interpreted_mode=HMCL 正在執行於直譯器模式的 Java 環境中,效能將會受到很大影響。\n我們建議你使用支援 JIT 的 Java 開啟 HMCL 以獲得最佳體驗。 warning.software_rendering=HMCL 正在使用軟體繪製,效能將會受到很大影響。 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 22b183a1d0..fefa4a8469 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -628,6 +628,58 @@ install.new_game.malformed_json=不是有效的 Minecraft 实例 JSON 文件。 install.select=请选择安装方式 install.success=安装成功 +instance=游戏 +instance.name=游戏实例名称 +instance.empty=没有游戏实例 +instance.empty.add=进入下载页安装游戏 +instance.empty.launch=没有可启动的游戏。\n你可以前往下载页面下载新游戏,或在“实例列表”中切换游戏文件夹。 +instance.empty.launch.goto_download=前往下载页面 +instance.empty.hint=没有已安装的游戏。\n你可以切换其他游戏文件夹,或者点击此处进入游戏下载页面。 +instance.game.all=全部 +instance.game.april_fools=愚人节 +instance.game.old=远古版 +instance.game.release=正式版 +instance.game.releases=正式版 +instance.game.snapshot=快照 +instance.game.snapshots=快照 +instance.game.support_status.unsupported=不支持 +instance.game.support_status.untested=未经测试 +instance.game.type=版本类型 +instance.launch=启动游戏 +instance.launch_and_enter_world=进入世界 +instance.launch.empty=开始游戏 +instance.launch.empty.installing=安装游戏 +instance.launch.empty.tooltip=安装并启动最新正式版游戏 +instance.launch.test=测试游戏 +instance.switch=切换实例 +instance.launch_script=生成启动脚本 +instance.launch_script.failed=生成启动脚本失败 +instance.launch_script.save=保存启动脚本 +instance.launch_script.success=启动脚本已生成完毕:%s +instance.manage=实例列表 +instance.manage.clean=清理游戏文件夹 +instance.manage.clean.tooltip=清理“logs”和“crash-reports”文件夹 +instance.manage.duplicate=复制游戏实例 +instance.manage.duplicate.duplicate_save=复制世界 +instance.manage.duplicate.prompt=请输入新游戏名称 +instance.manage.duplicate.confirm=新的游戏实例将复制该实例文件夹 (".minecraft/versions/<实例名>") 下的文件,并带有独立的运行文件夹和设置。 +instance.manage.manage=实例管理 +instance.manage.manage.title=实例管理 - %1s +instance.manage.redownload_assets_index=更新游戏资源文件 +instance.manage.remove=删除该实例 +instance.manage.remove.confirm.trash=真的要删除实例“%s”吗?你可以在系统的回收站中还原“%s”文件夹来找回该实例。 +instance.manage.remove.confirm.independent=由于该游戏启用了“(全局/实例特定) 游戏设置 → 版本隔离 → 各实例独立”选项,删除该实例将导致该游戏的世界等数据一同被删除!真的要删除实例“%s”吗? +instance.manage.remove.failed=删除实例失败。可能文件被占用。 +instance.manage.remove_assets=删除所有游戏资源文件 +instance.manage.remove_libraries=删除所有库文件 +instance.manage.rename=重命名该实例 +instance.manage.rename.message=请输入要修改的名称 +instance.manage.rename.fail=重命名实例失败,可能文件被占用或者名字有特殊字符。 +instance.search=名称 +instance.search.prompt=输入版本名称进行搜索 +instance.settings=游戏设置 +instance.update=更新整合包 + java.add=添加 Java java.add.failed=Java 无效或与当前平台不兼容。 java.disable=禁用此 Java @@ -1592,58 +1644,6 @@ update.tooltip=更新 update.preview=提前预览 HMCL 版本 update.preview.subtitle=启用此选项,你将可以提前获取 HMCL 的新版本,以便在正式发布前进行测试。 -version=游戏 -version.name=游戏实例名称 -version.empty=没有游戏实例 -version.empty.add=进入下载页安装游戏 -version.empty.launch=没有可启动的游戏。\n你可以前往下载页面下载新游戏,或在“实例列表”中切换游戏文件夹。 -version.empty.launch.goto_download=前往下载页面 -version.empty.hint=没有已安装的游戏。\n你可以切换其他游戏文件夹,或者点击此处进入游戏下载页面。 -version.game.all=全部 -version.game.april_fools=愚人节 -version.game.old=远古版 -version.game.release=正式版 -version.game.releases=正式版 -version.game.snapshot=快照 -version.game.snapshots=快照 -version.game.support_status.unsupported=不支持 -version.game.support_status.untested=未经测试 -version.game.type=版本类型 -version.launch=启动游戏 -version.launch_and_enter_world=进入世界 -version.launch.empty=开始游戏 -version.launch.empty.installing=安装游戏 -version.launch.empty.tooltip=安装并启动最新正式版游戏 -version.launch.test=测试游戏 -version.switch=切换实例 -version.launch_script=生成启动脚本 -version.launch_script.failed=生成启动脚本失败 -version.launch_script.save=保存启动脚本 -version.launch_script.success=启动脚本已生成完毕:%s -version.manage=实例列表 -version.manage.clean=清理游戏文件夹 -version.manage.clean.tooltip=清理“logs”和“crash-reports”文件夹 -version.manage.duplicate=复制游戏实例 -version.manage.duplicate.duplicate_save=复制世界 -version.manage.duplicate.prompt=请输入新游戏名称 -version.manage.duplicate.confirm=新的游戏实例将复制该实例文件夹 (".minecraft/versions/<实例名>") 下的文件,并带有独立的运行文件夹和设置。 -version.manage.manage=实例管理 -version.manage.manage.title=实例管理 - %1s -version.manage.redownload_assets_index=更新游戏资源文件 -version.manage.remove=删除该实例 -version.manage.remove.confirm.trash=真的要删除实例“%s”吗?你可以在系统的回收站中还原“%s”文件夹来找回该实例。 -version.manage.remove.confirm.independent=由于该游戏启用了“(全局/实例特定) 游戏设置 → 版本隔离 → 各实例独立”选项,删除该实例将导致该游戏的世界等数据一同被删除!真的要删除实例“%s”吗? -version.manage.remove.failed=删除实例失败。可能文件被占用。 -version.manage.remove_assets=删除所有游戏资源文件 -version.manage.remove_libraries=删除所有库文件 -version.manage.rename=重命名该实例 -version.manage.rename.message=请输入要修改的名称 -version.manage.rename.fail=重命名实例失败,可能文件被占用或者名字有特殊字符。 -version.search=名称 -version.search.prompt=输入版本名称进行搜索 -version.settings=游戏设置 -version.update=更新整合包 - warning.java_interpreted_mode=HMCL 正在运行在解释器模式的 Java 环境中,性能将会受到很大影响。\n我们建议你使用支持 JIT 的 Java 启动 HMCL 以获取最佳体验。 warning.software_rendering=HMCL 正在使用软件渲染,性能将会受到很大影响。\n你可以访问 https://docs.hmcl.net/help.html 页面寻求帮助。 From f219566c4ea56e6146ffaf173198758ee002a177 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 20:00:19 +0800 Subject: [PATCH 84/85] refactor: implement LowerCaseEnumTypeAdapter for case-insensitive enum serialization --- .../hmcl/game/GameInstanceManifest.java | 21 ++- .../jackhuang/hmcl/util/gson/JsonUtils.java | 10 +- .../util/gson/LowerCaseEnumTypeAdapter.java | 138 ++++++++++++++++++ .../gson/LowerCaseEnumTypeAdapterFactory.java | 74 ---------- 4 files changed, 156 insertions(+), 87 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapter.java delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapterFactory.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java index 1aad729ba1..3fb6e8d773 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GameInstanceManifest.java @@ -26,6 +26,7 @@ import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.InstantTypeAdapter; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.gson.LowerCaseEnumTypeAdapter; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; @@ -237,8 +238,10 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t var map = new LinkedHashMap(); for (Map.Entry downloadEntry : obj.entrySet()) { try { - DownloadType type = DownloadType.valueOf(downloadEntry.getKey()); - map.put(type, JsonUtils.GSON.fromJson(downloadEntry.getValue(), DownloadInfo.class)); + DownloadType key = LowerCaseEnumTypeAdapter.fromJson(DownloadType.class, downloadEntry.getKey()); + if (key != null) { + map.put(key, JsonUtils.GSON.fromJson(downloadEntry.getValue(), DownloadInfo.class)); + } } catch (IllegalArgumentException ignored) { } } @@ -250,8 +253,10 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t var map = new LinkedHashMap(); for (Map.Entry loggingEntry : obj.entrySet()) { try { - DownloadType type = DownloadType.valueOf(loggingEntry.getKey()); - map.put(type, JsonUtils.GSON.fromJson(loggingEntry.getValue(), LoggingInfo.class)); + DownloadType key = LowerCaseEnumTypeAdapter.fromJson(DownloadType.class, loggingEntry.getKey()); + if (key != null) { + map.put(key, JsonUtils.GSON.fromJson(loggingEntry.getValue(), LoggingInfo.class)); + } } catch (IllegalArgumentException ignored) { } } @@ -261,7 +266,7 @@ public static GameInstanceManifest fromJson(JsonObject json, boolean copyJson) t case "type" -> { if (value instanceof JsonPrimitive primitive && primitive.isString()) { try { - builder.type = ReleaseType.valueOf(primitive.getAsString()); + builder.type = LowerCaseEnumTypeAdapter.fromJson(ReleaseType.class, primitive.getAsString()); } catch (IllegalArgumentException ignored) { } } @@ -660,19 +665,19 @@ public JsonObject toJsonObject() { if (downloads != null) { JsonObject downloadsObject = new JsonObject(); for (Map.Entry entry : downloads.entrySet()) { - downloadsObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + downloadsObject.add(entry.getKey().name().toLowerCase(Locale.ROOT), JsonUtils.GSON.toJsonTree(entry.getValue())); } json.add("downloads", downloadsObject); } if (logging != null) { JsonObject loggingObject = new JsonObject(); for (Map.Entry entry : logging.entrySet()) { - loggingObject.add(entry.getKey().name(), JsonUtils.GSON.toJsonTree(entry.getValue())); + loggingObject.add(entry.getKey().name().toLowerCase(Locale.ROOT), JsonUtils.GSON.toJsonTree(entry.getValue())); } json.add("logging", loggingObject); } if (type != null) - json.addProperty("type", type.name()); + json.addProperty("type", type.name().toLowerCase(Locale.ROOT)); if (time != null) json.addProperty("time", InstantTypeAdapter.serializeToString(time, ZoneOffset.UTC)); if (releaseTime != null) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/JsonUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/JsonUtils.java index 0451c92ea9..3e6d66b625 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/JsonUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/JsonUtils.java @@ -62,7 +62,7 @@ public final class JsonUtils { /// - Pretty printing enabled. /// - Complex map key serialization enabled. /// - Type adapters for [java.time.Instant], [java.util.UUID], and [java.nio.file.Path]. - /// - [ValidationTypeAdapterFactory], [LowerCaseEnumTypeAdapterFactory], and + /// - [ValidationTypeAdapterFactory], [LowerCaseEnumTypeAdapter#FACTORY], and /// [JsonTypeAdapterFactory]. public static final Gson GSON = defaultGsonBuilder().create(); @@ -72,11 +72,11 @@ public final class JsonUtils { /// Configured with: /// - [JsonTypeAdapterFactory] /// - [ValidationTypeAdapterFactory] - /// - [LowerCaseEnumTypeAdapterFactory] + /// - [LowerCaseEnumTypeAdapter#FACTORY] public static final Gson UGLY_GSON = new GsonBuilder() .registerTypeAdapterFactory(JsonTypeAdapterFactory.INSTANCE) .registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE) - .registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory.INSTANCE) + .registerTypeAdapterFactory(LowerCaseEnumTypeAdapter.FACTORY) .create(); /// Not instantiable. @@ -651,7 +651,7 @@ public static void writeToJsonFile(Path file, @Nullable Object value) throws IOE /// - [InstantTypeAdapter] registered for [java.time.Instant]. /// - [UUIDTypeAdapter] registered for [java.util.UUID]. /// - [PathTypeAdapter] registered for [java.nio.file.Path]. - /// - [ValidationTypeAdapterFactory], [LowerCaseEnumTypeAdapterFactory], and + /// - [ValidationTypeAdapterFactory], [LowerCaseEnumTypeAdapter#FACTORY], and /// [JsonTypeAdapterFactory] registered as type adapter factories. /// /// Callers may further customize the returned builder before calling @@ -666,7 +666,7 @@ public static GsonBuilder defaultGsonBuilder() { .registerTypeAdapter(UUID.class, UUIDTypeAdapter.INSTANCE) .registerTypeAdapter(Path.class, PathTypeAdapter.INSTANCE) .registerTypeAdapterFactory(ValidationTypeAdapterFactory.INSTANCE) - .registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory.INSTANCE) + .registerTypeAdapterFactory(LowerCaseEnumTypeAdapter.FACTORY) .registerTypeAdapterFactory(JsonTypeAdapterFactory.INSTANCE); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapter.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapter.java new file mode 100644 index 0000000000..58fa41887f --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapter.java @@ -0,0 +1,138 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.util.gson; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/// Serializes enum constants as lower-case enum names and reads enum names case-insensitively. +/// +/// Unknown values are deserialized as `null` for compatibility with legacy code that treated +/// unsupported enum values as absent. +/// +/// TODO: Annotate enum types that should be serialized as lower-case names to use this adapter. +@NotNullByDefault +public final class LowerCaseEnumTypeAdapter> extends TypeAdapter<@Nullable E> { + + /// Gson factory that applies [LowerCaseEnumTypeAdapter] to enum raw types. + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { + /// Creates a lower-case enum adapter for enum types, or returns `null` for non-enum types. + /// + /// @param gson the Gson instance requesting the adapter + /// @param type the target type token + /// @return an enum adapter for enum raw types, or `null` to let Gson continue factory lookup + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + public @Nullable TypeAdapter create(Gson gson, TypeToken type) { + Class rawType = type.getRawType(); + return rawType.isEnum() ? new LowerCaseEnumTypeAdapter(rawType) : null; + } + }; + + /// Maps lower-case enum names to their corresponding constants for lookup during deserialization. + private final @Unmodifiable Map lowercaseToConstant; + + /// Creates an adapter for the supplied enum class. + /// + /// @param enumClass the enum class whose constants are accepted by this adapter + private LowerCaseEnumTypeAdapter(Class enumClass) { + var lowercaseToConstant = new HashMap(); + for (E constant : enumClass.getEnumConstants()) { + lowercaseToConstant.put(constant.name().toLowerCase(), constant); + } + this.lowercaseToConstant = Map.copyOf(lowercaseToConstant); + } + + /// Reads a nullable enum value from the next JSON token. + /// + /// `null` JSON tokens are returned as `null`; string tokens are matched against enum names + /// case-insensitively. Unknown names are also returned as `null` for compatibility. + /// + /// @param in the JSON reader positioned at the enum value + /// @return the enum constant represented by the token, or `null` for JSON null or unknown names + /// @throws IOException if the reader cannot read the next token + @Override + public @Nullable E read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + String name = in.nextString(); + return lowercaseToConstant.get(name.toLowerCase(Locale.ROOT)); + } + + /// Writes a nullable enum value to JSON. + /// + /// Non-null values are written as lower-case enum names; `null` values are written as JSON null. + /// + /// @param out the JSON writer receiving the value + /// @param value the enum constant to write, or `null` + /// @throws IOException if the writer cannot write the value + @Override + public void write(JsonWriter out, @Nullable E value) throws IOException { + out.value(value != null ? value.name().toLowerCase(Locale.ROOT) : null); + } + + /// Converts a JSON primitive into an enum constant by matching enum names case-insensitively. + /// + /// @param clazz the enum class to search + /// @param json the JSON primitive containing the enum name + /// @return the enum constant represented by `json`, or `null` when the primitive is not a string + /// or names no enum constant + public static > @Nullable E fromJson(Class clazz, JsonElement json) { + return json instanceof JsonPrimitive primitive && primitive.isString() + ? fromJson(clazz, json.getAsString()) + : null; + + } + + /// Converts a JSON primitive into an enum constant by matching enum names case-insensitively. + /// + /// @param clazz the enum class to search + /// @param name the JSON string value + /// @return the enum constant represented by `json`, or `null` when the primitive is not a string + /// or names no enum constant + public static > @Nullable E fromJson(Class clazz, String name) { + for (E constant : clazz.getEnumConstants()) { + if (constant.name().equalsIgnoreCase(name)) + return constant; + } + + return null; + } + + /// Converts an enum constant into its lower-case JSON representation. + /// + /// @param value the enum constant to convert, or `null` + /// @return a lower-case string primitive for non-null values, or [JsonNull#INSTANCE] for `null` + public static > JsonElement toJsonTree(@Nullable E value) { + return value != null ? new JsonPrimitive(value.name().toLowerCase(Locale.ROOT)) : JsonNull.INSTANCE; + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapterFactory.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapterFactory.java deleted file mode 100644 index 030f2e66a7..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapterFactory.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.util.gson; - -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Locale; - -/** - * - * @author huangyuhui - */ -public final class LowerCaseEnumTypeAdapterFactory implements TypeAdapterFactory { - - public static final LowerCaseEnumTypeAdapterFactory INSTANCE = new LowerCaseEnumTypeAdapterFactory(); - - @Override - @SuppressWarnings("unchecked") - public TypeAdapter create(Gson gson, TypeToken tt) { - Class rawType = tt.getRawType(); - if (!rawType.isEnum()) - return null; - - HashMap lowercaseToConstant = new HashMap<>(); - for (Object constant : rawType.getEnumConstants()) - lowercaseToConstant.put(toLowercase(constant), (T) constant); - - return new TypeAdapter() { - @Override - public void write(JsonWriter writer, T t) throws IOException { - if (t == null) - writer.nullValue(); - else - writer.value(toLowercase(t)); - } - - @Override - public T read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); - return null; - } - return lowercaseToConstant.get(reader.nextString().toLowerCase(Locale.ROOT)); - } - }; - } - - private static String toLowercase(Object o) { - return o.toString().toLowerCase(Locale.ROOT); - } -} From dd5f62722550c9fa7b45060154e3e9b8252683be Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 7 Jul 2026 20:08:48 +0800 Subject: [PATCH 85/85] fix(gson): Use Locale.ROOT for lowercase enum mapping - normalize enum names with `Locale.ROOT` when building lowercase lookup maps - avoid locale-dependent casing differences that can break enum deserialization on non-English systems --- .../org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapter.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapter.java index 58fa41887f..4ae0f90ec7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapter.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/gson/LowerCaseEnumTypeAdapter.java @@ -64,7 +64,7 @@ public final class LowerCaseEnumTypeAdapter> extends TypeAdapt private LowerCaseEnumTypeAdapter(Class enumClass) { var lowercaseToConstant = new HashMap(); for (E constant : enumClass.getEnumConstants()) { - lowercaseToConstant.put(constant.name().toLowerCase(), constant); + lowercaseToConstant.put(constant.name().toLowerCase(Locale.ROOT), constant); } this.lowercaseToConstant = Map.copyOf(lowercaseToConstant); }