-
Notifications
You must be signed in to change notification settings - Fork 865
feat: 下载页面折叠快照 Minecraft 版本 #5601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
9963f37
41b0b8f
281c1c5
abd9eb8
fe3ad4f
880ca03
7bf2c31
77516d4
e02c5a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -27,18 +27,9 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.geometry.Insets; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.geometry.Pos; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.Node; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.control.Control; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.control.Label; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.control.ScrollPane; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.control.Skin; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.control.SkinBase; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.control.*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.image.ImageView; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.layout.BorderPane; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.layout.HBox; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.layout.Priority; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.layout.Region; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.layout.StackPane; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.layout.VBox; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.scene.layout.*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import javafx.stage.FileChooser; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.download.LibraryAnalyzer; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.game.HMCLGameRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -55,26 +46,15 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.ui.SVG; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.ui.construct.*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.ui.decorator.DecoratorPage; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.Lang; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.Pair; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.SimpleMultimap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.StringUtils; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.TaskCancellationAction; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.i18n.I18n; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.io.FileUtils; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.javafx.BindingMapping; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jackhuang.hmcl.util.versioning.GameVersionNumber; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.jetbrains.annotations.Nullable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.nio.file.Path; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.ArrayList; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Collection; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Collections; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Comparator; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.EnumMap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.HashMap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Set; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.stream.Collectors; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.stream.Stream; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -318,25 +298,47 @@ protected ModDownloadPageSkin(DownloadPage control) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (String gameVersion : control.versions.keys().stream() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<String> versionList = control.versions.keys().stream() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(gv -> Objects.requireNonNullElse(GameVersionNumber.getReleaseOfSnapshot(gv), gv)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .distinct() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .sorted(Collections.reverseOrder(GameVersionNumber::compare)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .toList()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<RemoteMod.Version> versions = control.versions.get(gameVersion); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (versions == null || versions.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .toList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (String release : versionList) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<String> releases = new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<String> snapshots = new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var sublist = new ComponentSublist(() -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ArrayList<ModItem> items = new ArrayList<>(versions.size()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (RemoteMod.Version v : versions) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| items.add(new ModItem(control.addon, v, control)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (String gv : control.versions.keys()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (release.equals(Objects.requireNonNullElse(GameVersionNumber.getReleaseOfSnapshot(gv), gv))) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (gv.equals(release) ? releases : snapshots).add(gv); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return items; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sublist.getStyleClass().add("no-padding"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sublist.setTitle("Minecraft " + gameVersion); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (int i = 0; i <= 1; i++) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // i = 0 -> releases, i = 1 -> snapshots | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<String> target = (i == 0) ? releases : snapshots; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (target.isEmpty()) continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String title = (i == 0) ? "Minecraft " + release | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : String.format("Minecraft %s - %s", release, i18n("version.game.snapshot")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var sublist = new ComponentSublist(() -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ArrayList<ModItem> items = new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (String gv : target) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<RemoteMod.Version> versions = control.versions.get(gv); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (versions != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (RemoteMod.Version v : versions) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| items.add(new ModItem(control.addon, v, control)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Glavo marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+299
to
+308
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (String gv : target) { | |
| List<RemoteMod.Version> versions = control.versions.get(gv); | |
| if (versions != null) { | |
| for (RemoteMod.Version v : versions) { | |
| items.add(new ModItem(control.addon, v, control)); | |
| } | |
| } | |
| } | |
| // Merge versions from all matching game-version buckets, de-duplicating | |
| // by a stable identifier (e.g., the "self" URL), then sort by datePublished. | |
| Map<Object, RemoteMod.Version> uniqueVersions = new LinkedHashMap<>(); | |
| for (String gv : target) { | |
| List<RemoteMod.Version> versions = control.versions.get(gv); | |
| if (versions != null) { | |
| for (RemoteMod.Version v : versions) { | |
| if (v == null) continue; | |
| Object key = v.getSelf() != null ? v.getSelf() : v; | |
| uniqueVersions.putIfAbsent(key, v); | |
| } | |
| } | |
| } | |
| List<RemoteMod.Version> sortedVersions = new ArrayList<>(uniqueVersions.values()); | |
| sortedVersions.sort(Comparator.comparing( | |
| RemoteMod.Version::getDatePublished, | |
| Comparator.nullsLast(Comparator.naturalOrder()) | |
| ).reversed()); | |
| for (RemoteMod.Version v : sortedVersions) { | |
| items.add(new ModItem(control.addon, v, control)); | |
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -189,6 +189,39 @@ public final String toDebugString() { | |||||||||
| return buildDebugString().toString(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| public static String getReleaseOfSnapshot(String version) { | ||||||||||
| if (version == null || version.isEmpty()) { | ||||||||||
| return null; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| GameVersionNumber gvn = asGameVersion(version); | ||||||||||
|
|
||||||||||
| if (gvn instanceof Release release) { | ||||||||||
| if (release.getEaType() == Release.ReleaseType.GA) { | ||||||||||
| return null; | ||||||||||
| } | ||||||||||
| if (release.getPatch() > 0) { | ||||||||||
| return release.getMajor() + "." + release.getMinor() + "." + release.getPatch(); | ||||||||||
| } else { | ||||||||||
| return release.getMajor() + "." + release.getMinor(); | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (gvn instanceof LegacySnapshot snapshot) { | ||||||||||
| String[] defaultVersions = Versions.DEFAULT_GAME_VERSIONS; | ||||||||||
| for (int i = defaultVersions.length - 1; i >= 0; i--) { | ||||||||||
| String gaStr = defaultVersions[i]; | ||||||||||
| Release gaRel = Release.parseSimple(gaStr); | ||||||||||
|
|
||||||||||
| if (gaRel.compareToSnapshot(snapshot) > 0) { | ||||||||||
| return gaStr; | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
||||||||||
| // Special or non-standard versions (e.g. April Fools snapshots like "20w14infinite") | |
| // are intentionally not mapped to a GA release and will fall through to return null. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This builds
versionListby callinggetReleaseOfSnapshot()for every key, and then later the code callsgetReleaseOfSnapshot()again inside nested loops overversionList×versions.keys(). For mods with many supported versions this becomes unnecessarily expensive (andgetReleaseOfSnapshot()may scanDEFAULT_GAME_VERSIONSfor legacy snapshots). Consider computing aMap<String, String> gvToReleaseonce, then grouping keys in a single pass (e.g.,Map<String, List<String>>) to avoid repeated parsing/lookups.