From fb131e34fdf65bd0eda0567122d00f4b6955e78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Sun, 28 Jun 2026 14:04:05 +0800 Subject: [PATCH 1/8] qwq --- .../java/org/jackhuang/hmcl/EntryPoint.java | 6 +- .../hmcl/setting/EnumUpdateMode.java | 25 ++ .../hmcl/setting/LauncherSettings.java | 12 +- .../org/jackhuang/hmcl/ui/UpgradeDialog.java | 7 +- .../org/jackhuang/hmcl/ui/main/MainPage.java | 233 +++++++++++++----- .../org/jackhuang/hmcl/ui/main/RootPage.java | 2 - .../jackhuang/hmcl/ui/main/SettingsPage.java | 10 + .../hmcl/upgrade/HMCLDownloadTask.java | 2 +- .../jackhuang/hmcl/upgrade/UpdateHandler.java | 78 +++--- HMCL/src/main/resources/assets/css/root.css | 8 + .../assets/lang/I18N_zh_CN.properties | 16 +- 11 files changed, 288 insertions(+), 111 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateMode.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java b/HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java index 1082fc7e3c0..191429fcd93 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java @@ -17,15 +17,15 @@ */ package org.jackhuang.hmcl; +import org.jackhuang.hmcl.java.JavaRuntime; import org.jackhuang.hmcl.util.FileSaver; import org.jackhuang.hmcl.util.SelfDependencyPatcher; import org.jackhuang.hmcl.util.SwingUtils; -import org.jackhuang.hmcl.java.JavaRuntime; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.JarUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; -import javax.swing.JOptionPane; +import javax.swing.*; import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -34,8 +34,8 @@ import java.nio.file.Path; import java.util.concurrent.CancellationException; -import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; public final class EntryPoint { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateMode.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateMode.java new file mode 100644 index 00000000000..0f1ef13cec5 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateMode.java @@ -0,0 +1,25 @@ +/* + * 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.setting; + +public enum EnumUpdateMode { + NOTIFY, + DOWNLOAD, + SILENT, + AUTO +} 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 92c65b62e6d..7804f4f0606 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java @@ -41,7 +41,8 @@ import org.jetbrains.annotations.Nullable; import java.nio.file.Path; -import java.util.*; +import java.util.Objects; +import java.util.UUID; /// Stores the current workspace's main launcher settings. /// @@ -169,6 +170,15 @@ public BooleanProperty acceptPreviewUpdateProperty() { return acceptPreviewUpdate; } + /// Whether preview builds are accepted by update checks. + @SerializedName("updateMode") + private final ObjectProperty updateMode = new SimpleObjectProperty<>(EnumUpdateMode.NOTIFY); + + /// Returns the update mode property. + public ObjectProperty updateModeProperty() { + return updateMode; + } + /// Whether automatic update dialogs are disabled. @SerializedName("disableAutoShowUpdateDialog") private final BooleanProperty disableAutoShowUpdateDialog = new SimpleBooleanProperty(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java index ff13f2f67aa..2ebb16a2b68 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java @@ -109,11 +109,14 @@ public UpgradeDialog(RemoteVersion remoteVersion, Runnable updateRunnable) { updateButton.getStyleClass().add("dialog-accept"); updateButton.setOnAction(e -> updateRunnable.run()); - JFXButton cancelButton = new JFXButton(i18n("button.cancel")); + JFXButton cancelButton = new JFXButton(updateRunnable != null ? i18n("button.cancel") : i18n("button.ok")); cancelButton.getStyleClass().add("dialog-cancel"); cancelButton.setOnAction(e -> fireEvent(new DialogCloseEvent())); - setActions(openInBrowser, updateButton, cancelButton); + if (updateRunnable != null) + setActions(openInBrowser, updateButton, cancelButton); + else + setActions(openInBrowser, cancelButton); onEscPressed(this, cancelButton::fire); } } 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 da3f44639ef..6b991fe57ca 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 @@ -19,16 +19,17 @@ import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXPopup; +import com.jfoenix.controls.JFXSpinner; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; +import javafx.beans.InvalidationListener; import javafx.beans.property.*; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; -import javafx.scene.Cursor; import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.image.ImageView; @@ -46,6 +47,7 @@ import org.jackhuang.hmcl.download.VersionList; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.setting.DownloadProviders; +import org.jackhuang.hmcl.setting.EnumUpdateMode; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.task.Schedulers; @@ -54,6 +56,7 @@ import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.UpgradeDialog; import org.jackhuang.hmcl.ui.animation.AnimationUtils; import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.TransitionPane; @@ -62,6 +65,7 @@ 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.upgrade.HMCLDownloadTask; import org.jackhuang.hmcl.upgrade.RemoteVersion; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.upgrade.UpdateHandler; @@ -73,12 +77,15 @@ import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Objects; import java.util.concurrent.CancellationException; import java.util.function.Consumer; import static org.jackhuang.hmcl.download.RemoteVersion.Type.RELEASE; +import static org.jackhuang.hmcl.setting.SettingsManager.settings; import static org.jackhuang.hmcl.setting.SettingsManager.state; import static org.jackhuang.hmcl.ui.FXUtils.SINE; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -89,6 +96,9 @@ public final class MainPage extends StackPane implements DecoratorPage { private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper<>(); + private final SimpleObjectProperty updateState = new SimpleObjectProperty<>(UpdateBubble.State.NOTIFY); + private final SimpleObjectProperty exception = new SimpleObjectProperty<>(); + private Path downloadedHmcl = null; private final StringProperty currentGame = new SimpleStringProperty(this, "currentGame"); private final BooleanProperty showUpdate = new SimpleBooleanProperty(this, "showUpdate"); private final BooleanProperty showUpdateDialog = new SimpleBooleanProperty(this, "showUpdateDialog"); @@ -97,12 +107,17 @@ public final class MainPage extends StackPane implements DecoratorPage { private Profile profile; private TransitionPane announcementPane; - private final StackPane updatePane; + private final UpdateBubble updateBubble = new UpdateBubble(); private final JFXButton menuButton; private RemoteVersion lastShownVersion; { + latestVersion.bind(UpdateChecker.latestVersionProperty()); + FXUtils.onChange(showUpdateProperty(), MainPage.this::doAnimation); + FXUtils.onChange(showUpdateProperty(), MainPage.this::doUpdateIfNeeded); + FXUtils.onChange(showUpdateDialogProperty(), MainPage.this::showUpdateDialog); + HBox titleNode = new HBox(8); titleNode.setPadding(new Insets(0, 0, 0, 2)); titleNode.setAlignment(Pos.CENTER_LEFT); @@ -167,42 +182,6 @@ public final class MainPage extends StackPane implements DecoratorPage { getChildren().add(announcementPane); } - updatePane = new StackPane(); - updatePane.setVisible(false); - updatePane.getStyleClass().add("bubble"); - FXUtils.setLimitWidth(updatePane, 230); - FXUtils.setLimitHeight(updatePane, 55); - StackPane.setAlignment(updatePane, Pos.TOP_RIGHT); - FXUtils.onClicked(updatePane, this::onUpgrade); - updatePane.setCursor(Cursor.HAND); - FXUtils.onChange(showUpdateProperty(), this::doAnimation); - FXUtils.onChange(showUpdateDialogProperty(), this::showUpdateDialog); - - { - HBox hBox = new HBox(); - hBox.setSpacing(12); - hBox.setAlignment(Pos.CENTER_LEFT); - StackPane.setAlignment(hBox, Pos.CENTER_LEFT); - StackPane.setMargin(hBox, new Insets(9, 12, 9, 16)); - { - TwoLineListItem prompt = new TwoLineListItem(); - prompt.setSubtitle(i18n("update.bubble.subtitle")); - prompt.setPickOnBounds(false); - prompt.titleProperty().bind(BindingMapping.of(latestVersionProperty()).map(latestVersion -> - latestVersion == null ? "" : i18n("update.bubble.title", latestVersion.version()))); - - hBox.getChildren().setAll(SVG.UPDATE.createIcon(20), prompt); - } - - JFXButton closeUpdateButton = new JFXButton(); - closeUpdateButton.setGraphic(SVG.CLOSE.createIcon(10)); - StackPane.setAlignment(closeUpdateButton, Pos.TOP_RIGHT); - closeUpdateButton.getStyleClass().add("toggle-icon-tiny"); - StackPane.setMargin(closeUpdateButton, new Insets(5)); - closeUpdateButton.setOnAction(e -> closeUpdateBubble()); - - updatePane.getChildren().setAll(hBox, closeUpdateButton); - } HBox launchPane = new HBox(); launchPane.getStyleClass().add("launch-pane"); @@ -276,18 +255,63 @@ public void accept(String currentGame) { launchPane.getChildren().setAll(launchButton, menuButton); } - getChildren().addAll(updatePane, launchPane); + getChildren().addAll(updateBubble, launchPane); } + private void doUpdateIfNeeded(Boolean show) { + if (!show) return; + + var mode = settings().updateModeProperty().get(); + if (mode == EnumUpdateMode.NOTIFY) return; + + updateState.set(UpdateBubble.State.DOWNLOADING); + + try { + downloadedHmcl = Files.createTempFile("hmcl-update-", ".jar"); + var downloadTask = new HMCLDownloadTask(latestVersion.get(), downloadedHmcl); + downloadTask.whenComplete((e) -> { + if (e != null) { + javafx.application.Platform.runLater(() -> { + exception.set(e); + }); + } else { + javafx.application.Platform.runLater(() -> { + updateState.set(UpdateBubble.State.SUCCESS); + }); + + if (mode == EnumUpdateMode.SILENT) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + UpdateHandler.finishUpdate(downloadedHmcl, true); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + })); + } else if (mode == EnumUpdateMode.AUTO) { + try { + UpdateHandler.finishUpdate(downloadedHmcl, false); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + } + }).start(); + } catch (IOException e) { + LOG.warning("Failed to update", e); + exception.set(e); + } + } + private void showUpdateDialog(boolean show) { - if (show && getLatestVersion() != null && !Objects.equals(getLatestVersion(), lastShownVersion) - && !Objects.equals(state().getPromptedVersion(), getLatestVersion().version()) + if (show && latestVersion.get() != null && !Objects.equals(latestVersion.get(), lastShownVersion) + && !Objects.equals(state().getPromptedVersion(), latestVersion.get().version()) ) { - lastShownVersion = getLatestVersion(); - Controllers.dialogLater(new MessageDialogPane.Builder("", i18n("update.bubble.title", getLatestVersion().version()), MessageDialogPane.MessageType.INFO) + lastShownVersion = latestVersion.get(); + Controllers.dialogLater(new MessageDialogPane.Builder("", i18n("update.bubble.title", latestVersion.get().version()), MessageDialogPane.MessageType.INFO) .addAction(i18n("button.view"), () -> { - state().setPromptedVersion(getLatestVersion().version()); + state().setPromptedVersion(latestVersion.get().version()); onUpgrade(); }) .addCancel(null) @@ -301,16 +325,16 @@ private void doAnimation(boolean show) { Timeline nowAnimation = new Timeline(); nowAnimation.getKeyFrames().addAll( new KeyFrame(Duration.ZERO, - new KeyValue(updatePane.translateXProperty(), show ? 260 : 0, SINE)), + new KeyValue(updateBubble.translateXProperty(), show ? 260 : 0, SINE)), new KeyFrame(duration, - new KeyValue(updatePane.translateXProperty(), show ? 0 : 260, SINE))); + new KeyValue(updateBubble.translateXProperty(), show ? 0 : 260, SINE))); if (show) nowAnimation.getKeyFrames().add( - new KeyFrame(Duration.ZERO, e -> updatePane.setVisible(true))); + new KeyFrame(Duration.ZERO, e -> updateBubble.setVisible(true))); else nowAnimation.getKeyFrames().add( - new KeyFrame(duration, e -> updatePane.setVisible(false))); + new KeyFrame(duration, e -> updateBubble.setVisible(false))); nowAnimation.play(); } else { - updatePane.setVisible(show); + updateBubble.setVisible(show); } } @@ -416,25 +440,106 @@ public BooleanProperty showUpdateDialogProperty() { return showUpdateDialog; } - public void setShowUpdateDialog(boolean showUpdateDialog) { - this.showUpdateDialog.set(showUpdateDialog); + public void initVersions(Profile profile, List versions) { + FXUtils.checkFxUserThread(); + this.profile = profile; + this.versions.setAll(versions); } - public RemoteVersion getLatestVersion() { - return latestVersion.get(); - } + private class UpdateBubble extends StackPane { + public UpdateBubble() { + this.setVisible(false); + this.getStyleClass().add("bubble"); + FXUtils.setLimitWidth(this, 260); + FXUtils.setLimitHeight(this, 55); + StackPane.setAlignment(this, Pos.TOP_RIGHT); - public ObjectProperty latestVersionProperty() { - return latestVersion; - } + HBox hBox = new HBox(); + hBox.setSpacing(12); + hBox.setAlignment(Pos.CENTER_LEFT); + StackPane.setAlignment(hBox, Pos.CENTER_LEFT); + StackPane.setMargin(hBox, new Insets(9, 12, 9, 16)); - public void setLatestVersion(RemoteVersion latestVersion) { - this.latestVersion.set(latestVersion); - } + TwoLineListItem item = new TwoLineListItem(); + item.setPickOnBounds(false); - public void initVersions(Profile profile, List versions) { - FXUtils.checkFxUserThread(); - this.profile = profile; - this.versions.setAll(versions); + JFXButton closeUpdateButton = new JFXButton(); + closeUpdateButton.setGraphic(SVG.CLOSE.createIcon(20)); + StackPane.setAlignment(closeUpdateButton, Pos.TOP_RIGHT); + closeUpdateButton.getStyleClass().add("toggle-icon-tiny"); + StackPane.setMargin(closeUpdateButton, new Insets(10)); + closeUpdateButton.setOnAction(e -> closeUpdateBubble()); + + FXUtils.onClicked(this, this::onClink); + + var invalidationListener = (InvalidationListener) observable -> { + item.titleProperty().unbind(); + if (updateState.get() == State.NOTIFY) { + item.setSubtitle(i18n("update.bubble.notify.subtitle")); + item.titleProperty().bind(BindingMapping.of(latestVersion).map(latestVersion -> + latestVersion == null ? "" : i18n("update.bubble.notify.title", latestVersion.version()))); + hBox.getChildren().setAll(SVG.UPDATE.createIcon(32), item); + this.getChildren().setAll(hBox, closeUpdateButton); + } else if (updateState.get() == State.DOWNLOADING) { + item.setSubtitle(i18n("update.bubble.downloading.subtitle")); + item.setTitle(i18n("update.bubble.downloading.title")); + hBox.getChildren().setAll(new JFXSpinner(), item); + + this.getChildren().setAll(hBox); + } else if (updateState.get() == State.SUCCESS) { + var mode = settings().updateModeProperty().get(); + + if (mode == EnumUpdateMode.SILENT) { + item.setSubtitle(i18n("update.bubble.success.subtitle.silent")); + } else if (mode == EnumUpdateMode.AUTO) { + item.setSubtitle(i18n("update.bubble.success.subtitle.auto")); + } else { + item.setSubtitle(i18n("update.bubble.success.subtitle.download")); + } + + item.setTitle(i18n("update.bubble.success.title")); + hBox.getChildren().setAll(SVG.CHECK_CIRCLE.createIcon(32), item); + this.getChildren().setAll(hBox); + } else if (updateState.get() == State.FAILED) { + item.setSubtitle(i18n("update.bubble.failed.subtitle")); + item.setTitle(i18n("update.failed")); + hBox.getChildren().setAll(SVG.ERROR.createIcon(32), item); + this.getChildren().setAll(hBox, closeUpdateButton); + } + + + }; + invalidationListener.invalidated(null); + updateState.addListener(invalidationListener); + } + + public enum State { + NOTIFY, + DOWNLOADING, + SUCCESS, + FAILED, + } + + public void onClink() { + if (updateState.get() == State.NOTIFY) { + onUpgrade(); + } else if (updateState.get() == State.DOWNLOADING) { + Controllers.dialog(new UpgradeDialog(latestVersion.get(), null)); + } else if (updateState.get() == State.SUCCESS) { + Task.runAsync(() -> { + try { + UpdateHandler.finishUpdate(downloadedHmcl, settings().updateModeProperty().get() == EnumUpdateMode.SILENT); + } catch (IOException e) { + LOG.warning("Failed to apply update", e); + javafx.application.Platform.runLater(() -> { + Controllers.dialog(StringUtils.getStackTrace(e), i18n("update.failed"), MessageDialogPane.MessageType.ERROR); + }); + } + }).start(); + } else if (updateState.get() == State.FAILED) { + var e = StringUtils.getStackTrace(exception.get()); + Controllers.dialog(e, i18n("update.failed"), MessageDialogPane.MessageType.ERROR); + } + } } } 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 b3951ec0486..c9c22dbec64 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 @@ -49,7 +49,6 @@ 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.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.TaskCancellationAction; @@ -120,7 +119,6 @@ public MainPage getMainPage() { }); FXUtils.onChangeAndOperate(Profiles.selectedInstanceProperty(), mainPage::setCurrentGame); - mainPage.latestVersionProperty().bind(UpdateChecker.latestVersionProperty()); Profiles.registerVersionsListener(profile -> { HMCLGameRepository repository = profile.getRepository(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 4fcc43ac506..268b06ee85f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -31,6 +31,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import org.jackhuang.hmcl.Metadata; +import org.jackhuang.hmcl.setting.EnumUpdateMode; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; @@ -139,6 +140,15 @@ protected int getTrailingTextIndex() { updatePaneList.getContent().add(updatePane); } + { + LineSelectButton updateModePane = new LineSelectButton<>(); + updateModePane.setTitle(i18n("update.mode")); + updateModePane.valueProperty().bindBidirectional(settings().updateModeProperty()); + updateModePane.setConverter(mode -> i18n("update.mode." + mode.name().toLowerCase())); + updateModePane.setItems(EnumUpdateMode.values()); + updatePaneList.getContent().add(updateModePane); + } + { LineToggleButton previewPane = new LineToggleButton(); previewPane.setTitle(i18n("update.preview")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java index 868c500524d..cff396b23db 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java @@ -22,7 +22,7 @@ import java.nio.file.Files; import java.nio.file.Path; -final class HMCLDownloadTask extends FileDownloadTask { +public final class HMCLDownloadTask extends FileDownloadTask { private final RemoteVersion.Type archiveFormat; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java index ec28b67daff..c90edf8c03a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java @@ -74,14 +74,14 @@ public static boolean processArguments(String[] args) { return true; } - if (args.length == 2 && args[0].equals("--apply-to")) { + if (args.length > 0 && args[0].equals("--apply-to")) { if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && !OperatingSystem.isWindows7OrLater()) { SwingUtils.showErrorDialog(i18n("fatal.apply_update_need_win7", Metadata.PUBLISH_URL)); return true; } try { - applyUpdate(Paths.get(args[1])); + applyUpdate(Paths.get(args[1]), args.length > 2 && args[2].equals("--silent")); } catch (IOException e) { LOG.warning("Failed to apply update", e); SwingUtils.showErrorDialog(i18n("fatal.apply_update_failure", Metadata.MANUAL_UPDATE_URL) + "\n" + StringUtils.getStackTrace(e)); @@ -97,6 +97,40 @@ public static boolean processArguments(String[] args) { return false; } + public static void finishUpdate(Path downloaded, boolean silent) throws IOException { + if (!IntegrityChecker.isSelfVerified() && !IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK) { + throw new IOException("Current JAR is not verified"); + } + + if (Controllers.getStage() != null) { + CompletableFuture future = new CompletableFuture<>(); + + Platform.runLater(() -> { + try { + Controllers.saveWindowStates(); + } finally { + future.complete(null); + } + }); + + try { + future.get(); + } catch (ExecutionException | InterruptedException ignored) { + // Ignore + } + + + try { + FileSaver.waitForAllSaves(); + } catch (InterruptedException ignored) { + // Ignore + } + } + + requestUpdate(downloaded, getCurrentLocation(), silent); + EntryPoint.exit(0); + } + public static void updateFrom(RemoteVersion version) { checkFxUserThread(); @@ -123,35 +157,7 @@ public static void updateFrom(RemoteVersion version) { if (success) { try { - if (!IntegrityChecker.isSelfVerified() && !IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK) { - throw new IOException("Current JAR is not verified"); - } - - CompletableFuture future = new CompletableFuture<>(); - - Platform.runLater(() -> { - try { - Controllers.saveWindowStates(); - } finally { - future.complete(null); - } - }); - - try { - future.get(); - } catch (ExecutionException | InterruptedException ignored) { - // Ignore - } - - - try { - FileSaver.waitForAllSaves(); - } catch (InterruptedException ignored) { - // Ignore - } - - requestUpdate(downloaded, getCurrentLocation()); - EntryPoint.exit(0); + finishUpdate(downloaded, false); } catch (IOException e) { LOG.warning("Failed to update to " + version, e); Platform.runLater(() -> Controllers.dialog(StringUtils.getStackTrace(e), i18n("update.failed"), MessageType.ERROR)); @@ -170,7 +176,7 @@ public static void updateFrom(RemoteVersion version) { })); } - private static void applyUpdate(Path target) throws IOException { + private static void applyUpdate(Path target, boolean silent) throws IOException { LOG.info("Applying update to " + target); Path self = getCurrentLocation(); @@ -190,14 +196,14 @@ private static void applyUpdate(Path target) throws IOException { } } - startJava(target); + if (!silent) startJava(target); } - private static void requestUpdate(Path updateTo, Path self) throws IOException { + private static void requestUpdate(Path updateTo, Path self, boolean silent) throws IOException { if (!IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK) { IntegrityChecker.verifyJar(updateTo); } - startJava(updateTo, "--apply-to", self.toString()); + startJava(updateTo, "--apply-to", self.toString(), silent ? "--silent" : ""); } public static void startJava(Path jar, String... appArgs) throws IOException { @@ -256,7 +262,7 @@ private static void performMigration() throws IOException { Path location = getParentApplicationLocation() .orElseThrow(() -> new IOException("Failed to get parent application location")); - requestUpdate(getCurrentLocation(), location); + requestUpdate(getCurrentLocation(), location, false); } /** diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 6edb943783e..f9448af484f 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -383,6 +383,14 @@ -fx-background-radius: 2px; } +.bubble .jfx-spinner { + -jfx-radius: 12; +} + +.bubble .jfx-spinner .arc { + -fx-stroke: -monet-inverse-on-surface; +} + .bubble > HBox > .two-line-list-item > .first-line > .title, .bubble > HBox > .two-line-list-item > HBox > .subtitle { -fx-text-fill: -monet-inverse-on-surface; 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 ab94a81a318..cf04b3533b9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1513,8 +1513,20 @@ update.disable_auto_show_update_dialog.subtitle=启用此选项,HMCL 将不会 update.failed=更新失败 update.found=发现更新 update.newest_version=最新版本为:%s -update.bubble.title=发现更新:%s -update.bubble.subtitle=点击此处进行升级 +update.bubble.notify.title=发现更新:%s +update.bubble.notify.subtitle=点击此处进行更新 +update.bubble.downloading.title=正在下载更新 +update.bubble.downloading.subtitle=点击查看更新日志 +update.bubble.success.title=更新下载成功 +update.bubble.success.subtitle.silent=将在退出 HMCL 后完成更新 +update.bubble.success.subtitle.auto=即将自动更新 +update.bubble.success.subtitle.download=点击此处完成更新 +update.bubble.failed.subtitle=点击此处查看详情 +update.mode=自动更新 +update.mode.notify=在有更新时提醒我 +update.mode.download=在有更新时自动下载 +update.mode.silent=在有更新时自动下载,并在退出后安装更新 +update.mode.auto=在有更新时自动下载,并在下载完成后立即安装更新 update.note.stable=适合优先追求软件稳定性的用户使用。\n新功能在经过充分测试后才会被添加到稳定版中。 update.note.dev=适合希望优先体验新功能的用户使用。\n包含最新功能和错误修复,但未经充分测试,可能会存在更多问题。 update.latest=当前版本为最新版本 From 1f6ff8edfce8a290dc04c5b681802c6b79d1c489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Sun, 28 Jun 2026 14:07:13 +0800 Subject: [PATCH 2/8] qwq --- .../org/jackhuang/hmcl/ui/main/SettingsPage.java | 3 ++- .../main/resources/assets/lang/I18N.properties | 16 ++++++++++++++-- .../resources/assets/lang/I18N_zh.properties | 16 ++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 268b06ee85f..53d7b1b3bcb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -60,6 +60,7 @@ import java.time.format.DateTimeFormatter; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.zip.GZIPInputStream; @@ -144,7 +145,7 @@ protected int getTrailingTextIndex() { LineSelectButton updateModePane = new LineSelectButton<>(); updateModePane.setTitle(i18n("update.mode")); updateModePane.valueProperty().bindBidirectional(settings().updateModeProperty()); - updateModePane.setConverter(mode -> i18n("update.mode." + mode.name().toLowerCase())); + updateModePane.setConverter(mode -> i18n("update.mode." + mode.name().toLowerCase(Locale.ROOT))); updateModePane.setItems(EnumUpdateMode.values()); updatePaneList.getContent().add(updateModePane); } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 81308bee8a0..81d95662f67 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1721,8 +1721,20 @@ update.disable_auto_show_update_dialog.subtitle=Enable this option to prevent HM update.failed=Failed to update update.found=Update Available! update.newest_version=Latest version: %s -update.bubble.title=Update Available: %s -update.bubble.subtitle=Click here to update +update.bubble.notify.title=Update Available: %s +update.bubble.notify.subtitle=Click here to update +update.bubble.downloading.title=Downloading update +update.bubble.downloading.subtitle=Click to view changelog +update.bubble.success.title=Update downloaded successfully +update.bubble.success.subtitle.silent=Update will be installed after exiting HMCL +update.bubble.success.subtitle.auto=Auto-update will start soon +update.bubble.success.subtitle.download=Click here to finish updating +update.bubble.failed.subtitle=Click here for details +update.mode=Auto Update +update.mode.notify=Notify me when updates are available +update.mode.download=Auto-download updates when available +update.mode.silent=Auto-download updates and install them after exit +update.mode.auto=Auto-download updates and install them immediately after download update.note.stable=Suitable for users who prioritize software stability.\nNew features are added to the stable version only after thorough testing. update.note.dev=Suitable for users who want to experience new features first.\nThe development version includes the latest features and bug fixes,\nbut may also have more issues due to insufficient testing. update.latest=This is the latest version diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 4a4bb270424..4b4f7e0fb8e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1508,8 +1508,20 @@ update.disable_auto_show_update_dialog.subtitle=啟用此選項,HMCL 將不會 update.failed=更新失敗 update.found=發現到更新 update.newest_version=最新版本為:%s -update.bubble.title=發現更新:%s -update.bubble.subtitle=點選此處進行升級 +update.bubble.notify.title=發現更新:%s +update.bubble.notify.subtitle=點選此處進行更新 +update.bubble.downloading.title=正在下載更新 +update.bubble.downloading.subtitle=點選檢視更新日誌 +update.bubble.success.title=更新下載成功 +update.bubble.success.subtitle.silent=將在退出 HMCL 後完成更新 +update.bubble.success.subtitle.auto=即將自動更新 +update.bubble.success.subtitle.download=點選此處完成更新 +update.bubble.failed.subtitle=點選此處檢視詳情 +update.mode=自動更新 +update.mode.notify=在有更新時提醒我 +update.mode.download=在有更新時自動下載 +update.mode.silent=在有更新時自動下載,並在退出後安裝更新 +update.mode.auto=在有更新時自動下載,並在下載完成後立即安裝更新 update.note.stable=適合優先追求軟體穩定性的使用者使用。\n新功能在經過充分測試後才會被添加到穩定版中。 update.note.dev=適合希望優先體驗新功能的使用者使用。\n開發版會包含最新功能和錯誤修復,但未經充分測試,可能會存在更多問題。 update.latest=目前版本為最新版本 From 421cf6e98c47c6aa544700aaa539f01e7e99f3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Sun, 28 Jun 2026 14:26:32 +0800 Subject: [PATCH 3/8] qwq --- .../hmcl/setting/LauncherSettings.java | 9 ------ .../org/jackhuang/hmcl/ui/Controllers.java | 10 +------ .../org/jackhuang/hmcl/ui/main/MainPage.java | 28 ------------------- .../jackhuang/hmcl/ui/main/SettingsPage.java | 8 ------ .../resources/assets/lang/I18N.properties | 2 -- .../resources/assets/lang/I18N_zh.properties | 2 -- .../assets/lang/I18N_zh_CN.properties | 2 -- 7 files changed, 1 insertion(+), 60 deletions(-) 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 7804f4f0606..bd31dad566b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java @@ -179,15 +179,6 @@ public ObjectProperty updateModeProperty() { return updateMode; } - /// Whether automatic update dialogs are disabled. - @SerializedName("disableAutoShowUpdateDialog") - private final BooleanProperty disableAutoShowUpdateDialog = new SimpleBooleanProperty(false); - - /// Returns the automatic update dialog disable property. - public BooleanProperty disableAutoShowUpdateDialogProperty() { - return disableAutoShowUpdateDialog; - } - /// Whether April Fools features are disabled. @SerializedName("disableAprilFools") private final BooleanProperty disableAprilFools = new SimpleBooleanProperty(false); 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 3a7a11c6d1e..6cd49148a43 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -77,10 +77,7 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -import static org.jackhuang.hmcl.setting.SettingsManager.settings; -import static org.jackhuang.hmcl.setting.SettingsManager.getAuthlibInjectorServers; -import static org.jackhuang.hmcl.setting.SettingsManager.state; -import static org.jackhuang.hmcl.setting.SettingsManager.userState; +import static org.jackhuang.hmcl.setting.SettingsManager.*; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -382,11 +379,6 @@ public static void initialize(Stage stage) { decorator = new DecoratorController(stage, getRootPage()); getRootPage().getMainPage().showUpdateProperty().bind(UpdateChecker.checkingUpdateProperty().not().and(UpdateChecker.outdatedProperty())); - getRootPage().getMainPage().showUpdateDialogProperty().bind( - decorator.backableProperty().not() - .and(getRootPage().getMainPage().showUpdateProperty()) - .and(settings().disableAutoShowUpdateDialogProperty().not()) - ); if (settings().commonDirectoryTypeProperty().get() == EnumCommonDirectory.CUSTOM && !FileUtils.canCreateDirectory(settings().getResolvedCommonDirectory())) { 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 6b991fe57ca..c7eb7598b30 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 @@ -116,7 +116,6 @@ public final class MainPage extends StackPane implements DecoratorPage { latestVersion.bind(UpdateChecker.latestVersionProperty()); FXUtils.onChange(showUpdateProperty(), MainPage.this::doAnimation); FXUtils.onChange(showUpdateProperty(), MainPage.this::doUpdateIfNeeded); - FXUtils.onChange(showUpdateDialogProperty(), MainPage.this::showUpdateDialog); HBox titleNode = new HBox(8); titleNode.setPadding(new Insets(0, 0, 0, 2)); @@ -304,21 +303,6 @@ private void doUpdateIfNeeded(Boolean show) { } } - private void showUpdateDialog(boolean show) { - if (show && latestVersion.get() != null && !Objects.equals(latestVersion.get(), lastShownVersion) - && !Objects.equals(state().getPromptedVersion(), latestVersion.get().version()) - ) { - lastShownVersion = latestVersion.get(); - Controllers.dialogLater(new MessageDialogPane.Builder("", i18n("update.bubble.title", latestVersion.get().version()), MessageDialogPane.MessageType.INFO) - .addAction(i18n("button.view"), () -> { - state().setPromptedVersion(latestVersion.get().version()); - onUpgrade(); - }) - .addCancel(null) - .build()); - } - } - private void doAnimation(boolean show) { if (AnimationUtils.isAnimationEnabled()) { Duration duration = Duration.millis(320); @@ -428,18 +412,6 @@ public BooleanProperty showUpdateProperty() { return showUpdate; } - public void setShowUpdate(boolean showUpdate) { - this.showUpdate.set(showUpdate); - } - - public boolean isShowUpdateDialog() { - return showUpdateDialog.get(); - } - - public BooleanProperty showUpdateDialogProperty() { - return showUpdateDialog; - } - public void initVersions(Profile profile, List versions) { FXUtils.checkFxUserThread(); this.profile = profile; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 53d7b1b3bcb..3c6211779c2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -165,14 +165,6 @@ protected int getTrailingTextIndex() { updatePaneList.getContent().add(previewPane); } - { - LineToggleButton disableAutoShowUpdateDialogPane = new LineToggleButton(); - disableAutoShowUpdateDialogPane.setTitle(i18n("update.disable_auto_show_update_dialog")); - disableAutoShowUpdateDialogPane.setSubtitle(i18n("update.disable_auto_show_update_dialog.subtitle")); - disableAutoShowUpdateDialogPane.selectedProperty().bindBidirectional(settings().disableAutoShowUpdateDialogProperty()); - updatePaneList.getContent().add(disableAutoShowUpdateDialogPane); - } - rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("update")), updatePaneList); } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 81d95662f67..d5345a2d52e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1716,8 +1716,6 @@ update.channel.nightly.hint=You are currently using a Nightly channel build of t update.channel.nightly.title=Nightly Channel Notice update.channel.stable=Stable update.checking=Checking for updates -update.disable_auto_show_update_dialog=Do not show update dialog automatically -update.disable_auto_show_update_dialog.subtitle=Enable this option to prevent HMCL from automatically showing the update dialog. update.failed=Failed to update update.found=Update Available! update.newest_version=Latest version: %s diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 4b4f7e0fb8e..31c91a7a1bd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1503,8 +1503,6 @@ update.channel.nightly.hint=你正在使用 HMCL 預覽版。預覽版更新較 update.channel.nightly.title=預覽版提示 update.channel.stable=穩定版 update.checking=正在檢查更新 -update.disable_auto_show_update_dialog=不自動顯示更新彈窗 -update.disable_auto_show_update_dialog.subtitle=啟用此選項,HMCL 將不會自動彈出更新彈窗。 update.failed=更新失敗 update.found=發現到更新 update.newest_version=最新版本為:%s 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 cf04b3533b9..a054645a226 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1508,8 +1508,6 @@ update.channel.nightly.hint=你正在使用 HMCL 预览版。预览版更新较 update.channel.nightly.title=预览版提示 update.channel.stable=稳定版 update.checking=正在检查更新 -update.disable_auto_show_update_dialog=不自动显示更新弹窗 -update.disable_auto_show_update_dialog.subtitle=启用此选项,HMCL 将不会自动弹出更新弹窗。 update.failed=更新失败 update.found=发现更新 update.newest_version=最新版本为:%s From f3d893fc4a0a7627c0c6e00fa5d06d674d3170df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Sun, 28 Jun 2026 14:32:09 +0800 Subject: [PATCH 4/8] qwq --- .../main/java/org/jackhuang/hmcl/ui/main/MainPage.java | 10 +++++----- .../java/org/jackhuang/hmcl/ui/main/SettingsPage.java | 1 + 2 files changed, 6 insertions(+), 5 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 c7eb7598b30..2f5da5eeb50 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 @@ -110,8 +110,8 @@ public final class MainPage extends StackPane implements DecoratorPage { private final UpdateBubble updateBubble = new UpdateBubble(); private final JFXButton menuButton; - private RemoteVersion lastShownVersion; - + public static final EnumUpdateMode UPDATE_MODE = settings().updateModeProperty().get(); + { latestVersion.bind(UpdateChecker.latestVersionProperty()); FXUtils.onChange(showUpdateProperty(), MainPage.this::doAnimation); @@ -261,7 +261,7 @@ public void accept(String currentGame) { private void doUpdateIfNeeded(Boolean show) { if (!show) return; - var mode = settings().updateModeProperty().get(); + var mode = UPDATE_MODE; if (mode == EnumUpdateMode.NOTIFY) return; updateState.set(UpdateBubble.State.DOWNLOADING); @@ -459,7 +459,7 @@ public UpdateBubble() { this.getChildren().setAll(hBox); } else if (updateState.get() == State.SUCCESS) { - var mode = settings().updateModeProperty().get(); + var mode = UPDATE_MODE; if (mode == EnumUpdateMode.SILENT) { item.setSubtitle(i18n("update.bubble.success.subtitle.silent")); @@ -500,7 +500,7 @@ public void onClink() { } else if (updateState.get() == State.SUCCESS) { Task.runAsync(() -> { try { - UpdateHandler.finishUpdate(downloadedHmcl, settings().updateModeProperty().get() == EnumUpdateMode.SILENT); + UpdateHandler.finishUpdate(downloadedHmcl, UPDATE_MODE == EnumUpdateMode.SILENT); } catch (IOException e) { LOG.warning("Failed to apply update", e); javafx.application.Platform.runLater(() -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 3c6211779c2..513b190e70f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -144,6 +144,7 @@ protected int getTrailingTextIndex() { { LineSelectButton updateModePane = new LineSelectButton<>(); updateModePane.setTitle(i18n("update.mode")); + updateModePane.setSubtitle(i18n("settings.take_effect_after_restart")); updateModePane.valueProperty().bindBidirectional(settings().updateModeProperty()); updateModePane.setConverter(mode -> i18n("update.mode." + mode.name().toLowerCase(Locale.ROOT))); updateModePane.setItems(EnumUpdateMode.values()); From 475a19b35a050e6b34ef24d54593c515c6f61298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Fri, 3 Jul 2026 23:17:52 +0800 Subject: [PATCH 5/8] qwq --- .../org/jackhuang/hmcl/ui/main/MainPage.java | 21 +++++++------------ .../org/jackhuang/hmcl/ui/main/RootPage.java | 1 - 2 files changed, 8 insertions(+), 14 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 3739f462dcb..965db8fe363 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 @@ -112,7 +112,7 @@ public final class MainPage extends StackPane implements DecoratorPage { private final JFXButton menuButton; public static final EnumUpdateMode UPDATE_MODE = settings().updateModeProperty().get(); - + { latestVersion.bind(UpdateChecker.latestVersionProperty()); FXUtils.onChange(showUpdateProperty(), MainPage.this::doAnimation); @@ -417,12 +417,6 @@ public BooleanProperty showUpdateProperty() { return showUpdate; } - public void initVersions(Profile profile, List versions) { - FXUtils.checkFxUserThread(); - this.profile = profile; - this.versions.setAll(versions); - } - private class UpdateBubble extends StackPane { public UpdateBubble() { this.setVisible(false); @@ -516,10 +510,11 @@ public void onClink() { Controllers.dialog(e, i18n("update.failed"), MessageDialogPane.MessageType.ERROR); } } - - public void initVersions(HMCLGameRepository repository, List versions) { - FXUtils.checkFxUserThread(); - this.repository = repository; - this.versions.setAll(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 9f3f8231bf6..f3825f88e88 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 @@ -119,7 +119,6 @@ public MainPage getMainPage() { }); FXUtils.onChangeAndOperate(GameDirectoryManager.selectedInstanceProperty(), mainPage::setCurrentGame); - mainPage.latestVersionProperty().bind(UpdateChecker.latestVersionProperty()); GameDirectoryManager.registerVersionsListener(repository -> { GameDirectory gameDirectory = repository.getGameDirectory(); From a6554bba537d36fa42aca19fa9255df16eadc657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:10:49 +0800 Subject: [PATCH 6/8] qwq --- .../org/jackhuang/hmcl/ui/main/MainPage.java | 4 +-- .../hmcl/upgrade/HMCLDownloadTask.java | 28 ------------------- .../jackhuang/hmcl/upgrade/RemoteVersion.java | 14 ++-------- 3 files changed, 4 insertions(+), 42 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 965db8fe363..d424abb4812 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 @@ -494,10 +494,10 @@ public void onClink() { onUpgrade(); } else if (updateState.get() == State.DOWNLOADING) { Controllers.dialog(new UpgradeDialog(latestVersion.get(), null)); - } else if (updateState.get() == State.SUCCESS) { + } else if (updateState.get() == State.SUCCESS && !UPDATE_MODE.equals(EnumUpdateMode.SILENT)) { Task.runAsync(() -> { try { - UpdateHandler.finishUpdate(downloadedHmcl, UPDATE_MODE == EnumUpdateMode.SILENT); + UpdateHandler.finishUpdate(downloadedHmcl, false); } catch (IOException e) { LOG.warning("Failed to apply update", e); javafx.application.Platform.runLater(() -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java index cff396b23db..ecf33db4da0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java @@ -19,38 +19,10 @@ import org.jackhuang.hmcl.task.FileDownloadTask; -import java.nio.file.Files; import java.nio.file.Path; public final class HMCLDownloadTask extends FileDownloadTask { - - private final RemoteVersion.Type archiveFormat; - public HMCLDownloadTask(RemoteVersion version, Path target) { super(version.url(), target, version.integrityCheck()); - archiveFormat = version.type(); - } - - @Override - public void execute() throws Exception { - super.execute(); - - try { - Path target = getPath(); - switch (archiveFormat) { - case JAR: - break; - default: - throw new IllegalArgumentException("Unknown format: " + archiveFormat); - } - } catch (Throwable e) { - try { - Files.deleteIfExists(getPath()); - } catch (Throwable e2) { - e.addSuppressed(e2); - } - throw e; - } } - } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java index b24ce19491a..d04e2e9e864 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java @@ -23,12 +23,11 @@ import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.NetworkUtils; -import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.Optional; -public record RemoteVersion(UpdateChannel channel, String version, String url, Type type, IntegrityCheck integrityCheck, +public record RemoteVersion(UpdateChannel channel, String version, String url, IntegrityCheck integrityCheck, boolean preview, boolean force) { public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String url) throws IOException { @@ -39,7 +38,7 @@ public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null); boolean force = Optional.ofNullable(response.get("force")).map(JsonElement::getAsBoolean).orElse(false); if (jarUrl != null && jarHash != null) { - return new RemoteVersion(channel, version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash), preview, force); + return new RemoteVersion(channel, version, jarUrl, new IntegrityCheck("SHA-1", jarHash), preview, force); } else { throw new IOException("No download url is available"); } @@ -47,13 +46,4 @@ public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String throw new IOException("Malformed response", e); } } - - @Override - public @NotNull String toString() { - return "[" + version + " from " + url + "]"; - } - - public enum Type { - JAR - } } From 6d4241518f2e6dbb98e9538458b4f68803260de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:43:10 +0800 Subject: [PATCH 7/8] qwq --- .../jackhuang/hmcl/ui/main/SettingsPage.java | 34 +++++++++++--- .../jackhuang/hmcl/upgrade/UpdateChecker.java | 12 ++++- .../resources/assets/lang/I18N.properties | 4 ++ .../resources/assets/lang/I18N_zh.properties | 4 ++ .../assets/lang/I18N_zh_CN.properties | 4 ++ docs/Contributing.md | 45 +++++++++--------- docs/Contributing_zh.md | 45 +++++++++--------- docs/Contributing_zh_Hant.md | 46 +++++++++---------- 8 files changed, 119 insertions(+), 75 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index e3228a5bde3..d994bf0967c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -38,16 +38,14 @@ import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; -import org.jackhuang.hmcl.upgrade.RemoteVersion; -import org.jackhuang.hmcl.upgrade.UpdateChannel; -import org.jackhuang.hmcl.upgrade.UpdateChecker; -import org.jackhuang.hmcl.upgrade.UpdateHandler; +import org.jackhuang.hmcl.upgrade.*; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.i18n.SupportedLocale; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.IOUtils; +import org.jetbrains.annotations.Nullable; import org.tukaani.xz.XZInputStream; import java.io.IOException; @@ -72,7 +70,8 @@ public final class SettingsPage extends ScrollPane { @SuppressWarnings("FieldCanBeLocal") - private final InvalidationListener updateListener; + @Nullable + private InvalidationListener updateListener; public SettingsPage() { this.setFitToWidth(true); @@ -84,7 +83,7 @@ public SettingsPage() { { ComponentList updatePaneList = new ComponentList(); - { + if (UpdateChecker.SHOULD_CHECK_UPDATE) { ObjectProperty updateChannel; { @@ -164,10 +163,31 @@ protected int getTrailingTextIndex() { updatePaneList.getContent().add(previewPane); } + } else { + var alertLineButton = new LineButton(); + alertLineButton.setLargeTitle(true); + alertLineButton.setLeading(SVG.INFO, 32); + + if (UpdateChecker.DISABLE_UPDATE_PROPERTY.equalsIgnoreCase("true")) { + alertLineButton.setTitle(i18n("update.disabled.title")); + alertLineButton.setSubtitle(i18n("update.disabled.subtitle")); + } else if (!IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK && !IntegrityChecker.isSelfVerified()) { + alertLineButton.setLeading(SVG.WARNING, 32); + + alertLineButton.setTitle(i18n("update.unofficial.title")); + alertLineButton.setSubtitle(i18n("update.unofficial.subtitle")); + + alertLineButton.setTrailingIcon(SVG.OPEN_IN_NEW); + alertLineButton.setOnAction(event -> FXUtils.openLink(Metadata.DOWNLOAD_URL)); + } else { + // should not happen + } - rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("update")), updatePaneList); + updatePaneList.getContent().add(alertLineButton); } + rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("update")), updatePaneList); + { ComponentList languagePaneList = new ComponentList(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index 3db51606969..22ff8eb0245 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -30,13 +30,16 @@ import java.util.LinkedHashMap; import static org.jackhuang.hmcl.setting.SettingsManager.settings; -import static org.jackhuang.hmcl.util.Lang.*; +import static org.jackhuang.hmcl.util.Lang.thread; import static org.jackhuang.hmcl.util.logging.Logger.LOG; public final class UpdateChecker { private UpdateChecker() { } + public static final String DISABLE_UPDATE_PROPERTY = System.getProperty("hmcl.update.disable", ""); + public static final boolean SHOULD_CHECK_UPDATE = shouldCheckUpdate(); + private static final ObjectProperty latestVersion = new SimpleObjectProperty<>(); private static final BooleanBinding outdated = Bindings.createBooleanBinding( () -> { @@ -55,6 +58,11 @@ private UpdateChecker() { latestVersion); private static final ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false); + private static boolean shouldCheckUpdate() { + if (DISABLE_UPDATE_PROPERTY.equalsIgnoreCase("true")) return false; + else return IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK || IntegrityChecker.isSelfVerified(); + } + public static void init() { requestCheckUpdate(UpdateChannel.getChannel(), settings().acceptPreviewUpdateProperty().get()); } @@ -102,6 +110,8 @@ private static boolean isDevelopmentVersion(String version) { } public static void requestCheckUpdate(UpdateChannel channel, boolean preview) { + if (!SHOULD_CHECK_UPDATE) return; + Platform.runLater(() -> { if (isCheckingUpdate()) return; diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 756358c1281..ac3c19c0b52 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1784,6 +1784,10 @@ update.channel.nightly.hint=You are currently using a Nightly channel build of t update.channel.nightly.title=Nightly Channel Notice update.channel.stable=Stable update.checking=Checking for updates +update.disabled.title=Update has been disabled +update.disabled.subtitle=You have disabled update checking via system properties. +update.unofficial.title=Unofficial build +update.unofficial.subtitle=You are using an unofficial build of HMCL. Please click here to download the official version to use update features. update.failed=Failed to update update.found=Update Available! update.newest_version=Latest version: %s diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 40a64912a29..4c7869a2f27 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1571,6 +1571,10 @@ update.channel.nightly.hint=你正在使用 HMCL 預覽版。預覽版更新較 update.channel.nightly.title=預覽版提示 update.channel.stable=穩定版 update.checking=正在檢查更新 +update.disabled.title=更新功能已停用 +update.disabled.subtitle=你已透過系統參數設定禁止更新檢查。 +update.unofficial.title=非官方構建 +update.unofficial.subtitle=你正在使用非官方構建的 HMCL,請點擊此處下載官方版本以使用更新功能。 update.failed=更新失敗 update.found=發現到更新 update.newest_version=最新版本為:%s 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 af0b2b4acc6..a455cf79f3a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1576,6 +1576,10 @@ update.channel.nightly.hint=你正在使用 HMCL 预览版。预览版更新较 update.channel.nightly.title=预览版提示 update.channel.stable=稳定版 update.checking=正在检查更新 +update.disabled.title=更新功能已禁用 +update.disabled.subtitle=你已通过系统参数配置了禁止更新检查。 +update.unofficial.title=非官方构建 +update.unofficial.subtitle=你正在使用非官方构建的 HMCL,请点击此处下载官方版本以使用更新功能。 update.failed=更新失败 update.found=发现更新 update.newest_version=最新版本为:%s diff --git a/docs/Contributing.md b/docs/Contributing.md index f189dc53f7a..c7ff9a5fdd1 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -81,25 +81,26 @@ HMCL provides a series of debug options to control the behavior of the launcher. These options can be specified via environment variables or JVM parameters. If both are present, JVM parameters will override the environment variable settings. -| Environment Variable | JVM Parameter | Function | Default Value | Additional Notes | -|-----------------------------|----------------------------------------------|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|---------------------------| -| `HMCL_JAVA_HOME` | | Specifies the Java used to launch HMCL | | Only effective for exe/sh | -| `HMCL_JAVA_OPTS` | | Specifies the default JVM parameters when launching HMCL | | Only effective for exe/sh | -| `HMCL_FORCE_GPU` | | Specifies whether to force GPU-accelerated rendering | `false` | | -| `HMCL_ANIMATION_FRAME_RATE` | | Specifies the animation frame rate of HMCL | `60` | | -| `HMCL_LANGUAGE` | | Specifies the default language of HMCL | Uses the system default language | | -| `HMCL_UI_SCALE` | | Specifies the UI scaling for HMCL | Uses the system's current scaling | Supports scale factor (1.5), percentage (150%), or DPI (144dpi). | -| | `-Dhmcl.dir=` | Specifies the current data folder of HMCL | `./.hmcl` | | -| | `-Dhmcl.home=` | Specifies the user data folder of HMCL | Windows: `%APPDATA%\.hmcl`
Linux/BSD: `$XDG_DATA_HOME/hmcl`
macOS: `~Library/Application Support/hmcl` | | -| | `-Dhmcl.self_integrity_check.disable=true` | Disables self-integrity checks during updates | | | -| | `-Dhmcl.bmclapi.override=` | Specifies the API Root for BMCLAPI | `https://bmclapi2.bangbang93.com` | | -| | `-Dhmcl.discoapi.override=` | Specifies the API Root for foojay Disco API | `https://api.foojay.io/disco/v3.0` | | -| `HMCL_FONT` | `-Dhmcl.font.override=` | Specifies the default font for HMCL | Uses the system default font | | -| | `-Dhmcl.update_source.override=` | Specifies the update source for HMCL | `https://hmcl.huangyuhui.net/api/update_link` | | -| | `-Dhmcl.authlibinjector.location=` | Specifies the location of the authlib-injector JAR file | Uses the built-in authlib-injector | | -| | `-Dhmcl.openjfx.repo=` | Adds a custom Maven repository for downloading OpenJFX | | | -| | `-Dhmcl.native.encoding=` | Specifies the native encoding | Uses the system's native encoding | | -| | `-Dhmcl.microsoft.auth.id=` | Specifies the Microsoft OAuth App ID | Uses the built-in Microsoft OAuth App ID | | -| | `-Dhmcl.curseforge.apikey=` | Specifies the CurseForge API key | Uses the built-in CurseForge API key | | -| | `-Dhmcl.native.backend=` | Specifies the native backend used by HMCL | `auto` | | -| | `-Dhmcl.hardware.fastfetch=` | Specifies whether to use fastfetch for hardware detection | `true` | | +| Environment Variable | JVM Parameter | Function | Default Value | Additional Notes | +|-----------------------------|----------------------------------------------|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------| +| `HMCL_JAVA_HOME` | | Specifies the Java used to launch HMCL | | Only effective for exe/sh | +| `HMCL_JAVA_OPTS` | | Specifies the default JVM parameters when launching HMCL | | Only effective for exe/sh | +| `HMCL_FORCE_GPU` | | Specifies whether to force GPU-accelerated rendering | `false` | | +| `HMCL_ANIMATION_FRAME_RATE` | | Specifies the animation frame rate of HMCL | `60` | | +| `HMCL_LANGUAGE` | | Specifies the default language of HMCL | Uses the system default language | | +| `HMCL_UI_SCALE` | | Specifies the UI scaling for HMCL | Uses the system's current scaling | Supports scale factor (1.5), percentage (150%), or DPI (144dpi). | +| | `-Dhmcl.dir=` | Specifies the current data folder of HMCL | `./.hmcl` | | +| | `-Dhmcl.home=` | Specifies the user data folder of HMCL | Windows: `%APPDATA%\.hmcl`
Linux/BSD: `$XDG_DATA_HOME/hmcl`
macOS: `~Library/Application Support/hmcl` | | +| | `-Dhmcl.self_integrity_check.disable=true` | Disables self-integrity checks during updates | | | +| | `-Dhmcl.bmclapi.override=` | Specifies the API Root for BMCLAPI | `https://bmclapi2.bangbang93.com` | | +| | `-Dhmcl.discoapi.override=` | Specifies the API Root for foojay Disco API | `https://api.foojay.io/disco/v3.0` | | +| `HMCL_FONT` | `-Dhmcl.font.override=` | Specifies the default font for HMCL | Uses the system default font | | +| | `-Dhmcl.update_source.override=` | Specifies the update source for HMCL | `https://hmcl.huangyuhui.net/api/update_link` | | +| | `-Dhmcl.authlibinjector.location=` | Specifies the location of the authlib-injector JAR file | Uses the built-in authlib-injector | | +| | `-Dhmcl.openjfx.repo=` | Adds a custom Maven repository for downloading OpenJFX | | | +| | `-Dhmcl.native.encoding=` | Specifies the native encoding | Uses the system's native encoding | | +| | `-Dhmcl.microsoft.auth.id=` | Specifies the Microsoft OAuth App ID | Uses the built-in Microsoft OAuth App ID | | +| | `-Dhmcl.curseforge.apikey=` | Specifies the CurseForge API key | Uses the built-in CurseForge API key | | +| | `-Dhmcl.native.backend=` | Specifies the native backend used by HMCL | `auto` | | +| | `-Dhmcl.hardware.fastfetch=` | Specifies whether to use fastfetch for hardware detection | `true` | | +| | `-Dhmcl.update.disable=` | Specifies whether to disable HMCL update checking | `false` | \ No newline at end of file diff --git a/docs/Contributing_zh.md b/docs/Contributing_zh.md index 93162be666e..9a117468f53 100644 --- a/docs/Contributing_zh.md +++ b/docs/Contributing_zh.md @@ -80,26 +80,27 @@ HMCL 提供了一系列调试选项,用于控制启动器的行为。 这些选项可以通过环境变量或 JVM 参数指定。如果两者同时存在,那么 JVM 参数会覆盖环境变量的设置。 -| 环境变量 | JVM 参数 | 功能 | 默认值 | 额外说明 | -|-----------------------------|----------------------------------------------|--------------------------------|-------------------------------------------------------------------------------------------------------------|--------------| -| `HMCL_JAVA_HOME` | | 指定用于启动 HMCL 的 Java | | 仅对 exe/sh 生效 | -| `HMCL_JAVA_OPTS` | | 指定启动 HMCL 时的默认 JVM 参数 | | 仅对 exe/sh 生效 | -| `HMCL_FORCE_GPU` | | 指定是否强制使用 GPU 加速渲染 | `false` | -| `HMCL_ANIMATION_FRAME_RATE` | | 指定 HMCL 的动画帧率 | `60` | | -| `HMCL_LANGUAGE` | | 指定 HMCL 的默认语言 | 使用系统默认语言 | -| `HMCL_UI_SCALE` | | 指定 HMCL 的 UI 缩放比例 | 遵循系统当前的缩放比例 | 支持倍数 (1.5)、百分比 (150%) 或 DPI (144dpi) | -| | `-Dhmcl.dir=` | 指定 HMCL 的当前数据文件夹 | `./.hmcl` | | -| | `-Dhmcl.home=` | 指定 HMCL 的用户数据文件夹 | Windows: `%APPDATA%\.hmcl`
Linux/BSD: `$XDG_DATA_HOME/hmcl`
macOS: `~Library/Application Support/hmcl` | | -| | `-Dhmcl.self_integrity_check.disable=true` | 检查更新时不检查本体完整性 | | | -| | `-Dhmcl.bmclapi.override=` | 指定 BMCLAPI 的 API Root | `https://bmclapi2.bangbang93.com` | | -| | `-Dhmcl.discoapi.override=` | 指定 foojay Disco API 的 API Root | `https://api.foojay.io/disco/v3.0` | -| `HMCL_FONT` | `-Dhmcl.font.override=` | 指定 HMCL 默认字体 | 使用系统默认字体 | | -| | `-Dhmcl.update_source.override=` | 指定 HMCL 更新源 | `https://hmcl.huangyuhui.net/api/update_link` | | -| | `-Dhmcl.authlibinjector.location=` | 指定 authlib-injector JAR 文件的位置 | 使用 HMCL 内嵌的 authlib-injector | | -| | `-Dhmcl.openjfx.repo=` | 添加用于下载 OpenJFX 的自定义 Maven 仓库 | | | -| | `-Dhmcl.native.encoding=` | 指定原生编码 | 使用系统的本机编码 | | -| | `-Dhmcl.microsoft.auth.id=` | 指定 Microsoft OAuth App ID | 使用 HMCL 内置的 Microsoft OAuth App ID | | -| | `-Dhmcl.curseforge.apikey=` | 指定 CurseForge API 密钥 | 使用 HMCL 内置的 CurseForge API 密钥 | | -| | `-Dhmcl.native.backend=` | 指定HMCL使用的本机后端 | `auto` | -| | `-Dhmcl.hardware.fastfetch=` | 指定是否使用 fastfetch 检测硬件信息 | `true` | +| 环境变量 | JVM 参数 | 功能 | 默认值 | 额外说明 | +|-----------------------------|----------------------------------------------|--------------------------------|--------------------------------------------------------------------------------------------------------------|--------------------------------------| +| `HMCL_JAVA_HOME` | | 指定用于启动 HMCL 的 Java | | 仅对 exe/sh 生效 | +| `HMCL_JAVA_OPTS` | | 指定启动 HMCL 时的默认 JVM 参数 | | 仅对 exe/sh 生效 | +| `HMCL_FORCE_GPU` | | 指定是否强制使用 GPU 加速渲染 | `false` | +| `HMCL_ANIMATION_FRAME_RATE` | | 指定 HMCL 的动画帧率 | `60` | | +| `HMCL_LANGUAGE` | | 指定 HMCL 的默认语言 | 使用系统默认语言 | +| `HMCL_UI_SCALE` | | 指定 HMCL 的 UI 缩放比例 | 遵循系统当前的缩放比例 | 支持倍数 (1.5)、百分比 (150%) 或 DPI (144dpi) | +| | `-Dhmcl.dir=` | 指定 HMCL 的当前数据文件夹 | `./.hmcl` | | +| | `-Dhmcl.home=` | 指定 HMCL 的用户数据文件夹 | Windows: `%APPDATA%\.hmcl`
Linux/BSD: `$XDG_DATA_HOME/hmcl`
macOS: `~Library/Application Support/hmcl` | | +| | `-Dhmcl.self_integrity_check.disable=true` | 检查更新时不检查本体完整性 | | | +| | `-Dhmcl.bmclapi.override=` | 指定 BMCLAPI 的 API Root | `https://bmclapi2.bangbang93.com` | | +| | `-Dhmcl.discoapi.override=` | 指定 foojay Disco API 的 API Root | `https://api.foojay.io/disco/v3.0` | +| `HMCL_FONT` | `-Dhmcl.font.override=` | 指定 HMCL 默认字体 | 使用系统默认字体 | | +| | `-Dhmcl.update_source.override=` | 指定 HMCL 更新源 | `https://hmcl.huangyuhui.net/api/update_link` | | +| | `-Dhmcl.authlibinjector.location=` | 指定 authlib-injector JAR 文件的位置 | 使用 HMCL 内嵌的 authlib-injector | | +| | `-Dhmcl.openjfx.repo=` | 添加用于下载 OpenJFX 的自定义 Maven 仓库 | | | +| | `-Dhmcl.native.encoding=` | 指定原生编码 | 使用系统的本机编码 | | +| | `-Dhmcl.microsoft.auth.id=` | 指定 Microsoft OAuth App ID | 使用 HMCL 内置的 Microsoft OAuth App ID | | +| | `-Dhmcl.curseforge.apikey=` | 指定 CurseForge API 密钥 | 使用 HMCL 内置的 CurseForge API 密钥 | | +| | `-Dhmcl.native.backend=` | 指定HMCL使用的本机后端 | `auto` | +| | `-Dhmcl.hardware.fastfetch=` | 指定是否使用 fastfetch 检测硬件信息 | `true` | +| | `-Dhmcl.update.disable=` | 指定是否禁用 HMCL 更新检测 | `false` | diff --git a/docs/Contributing_zh_Hant.md b/docs/Contributing_zh_Hant.md index a4f9cc080e3..e07f93bd373 100644 --- a/docs/Contributing_zh_Hant.md +++ b/docs/Contributing_zh_Hant.md @@ -80,26 +80,26 @@ HMCL 提供了一系列除錯選項,用於控制啟動器的行為。 這些選項可以透過環境變數或 JVM 參數設定。如果兩者同時存在,那麼 JVM 參數會覆蓋環境變數的設定。 -| 環境變數 | JVM 參數 | 功能 | 預設值 | 額外說明 | -|-----------------------------|----------------------------------------------|--------------------------------|-------------------------------------------------------------------------------------------------------------|--------------| -| `HMCL_JAVA_HOME` | | 設定用於開啟 HMCL 的 Java | | 僅對 exe/sh 生效 | -| `HMCL_JAVA_OPTS` | | 設定開啟 HMCL 時的預設 JVM 參數 | | 僅對 exe/sh 生效 | -| `HMCL_FORCE_GPU` | | 設定是否強制使用 GPU 加速繪製 | `false` | -| `HMCL_ANIMATION_FRAME_RATE` | | 設定 HMCL 的動畫幀率 | `60` | | -| `HMCL_LANGUAGE` | | 設定 HMCL 的預設語言 | 使用系統預設語言 | -| `HMCL_UI_SCALE` | | 設定 HMCL 的 UI 縮放比例 | 遵循系統目前的縮放比例 | 支援倍數 (1.5)、百分比 (150%) 或 DPI (144dpi) | -| | `-Dhmcl.dir=` | 設定 HMCL 的目前資料存放位置 | `./.hmcl` | | -| | `-Dhmcl.home=` | 設定 HMCL 的使用者資料存放位置 | Windows: `%APPDATA%\.hmcl`
Linux/BSD: `$XDG_DATA_HOME/hmcl`
macOS: `~Library/Application Support/hmcl` | | -| | `-Dhmcl.self_integrity_check.disable=true` | 檢查更新時不檢查程式完整性 | | | -| | `-Dhmcl.bmclapi.override=` | 設定 BMCLAPI 的 API Root | `https://bmclapi2.bangbang93.com` | | -| | `-Dhmcl.discoapi.override=` | 設定 foojay Disco API 的 API Root | `https://api.foojay.io/disco/v3.0` | -| `HMCL_FONT` | `-Dhmcl.font.override=` | 設定 HMCL 預設字體 | 使用系統預設字體 | | -| | `-Dhmcl.update_source.override=` | 設定 HMCL 更新來源 | `https://hmcl.huangyuhui.net/api/update_link` | | -| | `-Dhmcl.authlibinjector.location=` | 設定 authlib-injector JAR 檔的位置 | 使用 HMCL 內置的 authlib-injector | | -| | `-Dhmcl.openjfx.repo=` | 添加用於下載 OpenJFX 的自訂 Maven 倉庫 | | | -| | `-Dhmcl.native.encoding=` | 設定原生編碼 | 使用系統的本機編碼 | | -| | `-Dhmcl.microsoft.auth.id=` | 設定 Microsoft OAuth App ID | 使用 HMCL 內建的 Microsoft OAuth App ID | | -| | `-Dhmcl.curseforge.apikey=` | 設定 CurseForge API 金鑰 | 使用 HMCL 內建的 CurseForge API 金鑰 | | -| | `-Dhmcl.native.backend=` | 設定 HMCL 使用的本機後端 | `auto` | -| | `-Dhmcl.hardware.fastfetch=` | 設定是否使用 fastfetch 檢測硬體資訊 | `true` | - +| 環境變數 | JVM 參數 | 功能 | 預設值 | 額外說明 | +|-----------------------------|----------------------------------------------|--------------------------------|--------------------------------------------------------------------------------------------------------------|--------------------------------------| +| `HMCL_JAVA_HOME` | | 設定用於開啟 HMCL 的 Java | | 僅對 exe/sh 生效 | +| `HMCL_JAVA_OPTS` | | 設定開啟 HMCL 時的預設 JVM 參數 | | 僅對 exe/sh 生效 | +| `HMCL_FORCE_GPU` | | 設定是否強制使用 GPU 加速繪製 | `false` | +| `HMCL_ANIMATION_FRAME_RATE` | | 設定 HMCL 的動畫幀率 | `60` | | +| `HMCL_LANGUAGE` | | 設定 HMCL 的預設語言 | 使用系統預設語言 | +| `HMCL_UI_SCALE` | | 設定 HMCL 的 UI 縮放比例 | 遵循系統目前的縮放比例 | 支援倍數 (1.5)、百分比 (150%) 或 DPI (144dpi) | +| | `-Dhmcl.dir=` | 設定 HMCL 的目前資料存放位置 | `./.hmcl` | | +| | `-Dhmcl.home=` | 設定 HMCL 的使用者資料存放位置 | Windows: `%APPDATA%\.hmcl`
Linux/BSD: `$XDG_DATA_HOME/hmcl`
macOS: `~Library/Application Support/hmcl` | | +| | `-Dhmcl.self_integrity_check.disable=true` | 檢查更新時不檢查程式完整性 | | | +| | `-Dhmcl.bmclapi.override=` | 設定 BMCLAPI 的 API Root | `https://bmclapi2.bangbang93.com` | | +| | `-Dhmcl.discoapi.override=` | 設定 foojay Disco API 的 API Root | `https://api.foojay.io/disco/v3.0` | +| `HMCL_FONT` | `-Dhmcl.font.override=` | 設定 HMCL 預設字體 | 使用系統預設字體 | | +| | `-Dhmcl.update_source.override=` | 設定 HMCL 更新來源 | `https://hmcl.huangyuhui.net/api/update_link` | | +| | `-Dhmcl.authlibinjector.location=` | 設定 authlib-injector JAR 檔的位置 | 使用 HMCL 內置的 authlib-injector | | +| | `-Dhmcl.openjfx.repo=` | 添加用於下載 OpenJFX 的自訂 Maven 倉庫 | | | +| | `-Dhmcl.native.encoding=` | 設定原生編碼 | 使用系統的本機編碼 | | +| | `-Dhmcl.microsoft.auth.id=` | 設定 Microsoft OAuth App ID | 使用 HMCL 內建的 Microsoft OAuth App ID | | +| | `-Dhmcl.curseforge.apikey=` | 設定 CurseForge API 金鑰 | 使用 HMCL 內建的 CurseForge API 金鑰 | | +| | `-Dhmcl.native.backend=` | 設定 HMCL 使用的本機後端 | `auto` | +| | `-Dhmcl.hardware.fastfetch=` | 設定是否使用 fastfetch 檢測硬體資訊 | `true` | +| | `-Dhmcl.update.disable=` | 指定是否禁用 HMCL 更新檢測 | `false` | From aec69b0862de042355cc1987b3e780da9a4d8312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Sat, 4 Jul 2026 11:49:50 +0800 Subject: [PATCH 8/8] qwq --- HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index d994bf0967c..b7aacbc6f46 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -171,7 +171,7 @@ protected int getTrailingTextIndex() { if (UpdateChecker.DISABLE_UPDATE_PROPERTY.equalsIgnoreCase("true")) { alertLineButton.setTitle(i18n("update.disabled.title")); alertLineButton.setSubtitle(i18n("update.disabled.subtitle")); - } else if (!IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK && !IntegrityChecker.isSelfVerified()) { + } else if (!IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK && !IntegrityChecker.isSelfVerified()) { alertLineButton.setLeading(SVG.WARNING, 32); alertLineButton.setTitle(i18n("update.unofficial.title"));