summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorAviana Cruz2022-12-07 00:29:07 +0800
committerAviana Cruz2022-12-07 00:29:07 +0800
commitfffb6267f66f4cffbfcd8bae70bd0f6f11f43e13 (patch)
treec48b5b83494935ee9f1f62cea40bb8942d45a9fa
parent5c90ea933db165337f72f58133c14c6d71b4375d (diff)
downloadaur-fffb6267f66f4cffbfcd8bae70bd0f6f11f43e13.tar.gz
update 3.5.3.228
-rw-r--r--.SRCINFO14
-rw-r--r--0001-Target-java-17.patch20
-rw-r--r--0002-Cleanup.patch1748
-rw-r--r--0003-Disable-Pack200.patch8
-rw-r--r--PKGBUILD15
-rwxr-xr-xhmcl-launch-script6
6 files changed, 103 insertions, 1708 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 3bcd82517e4f..2d25ec93c8bd 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,6 +1,6 @@
pkgbase = hmcl-new
pkgdesc = An unofficial build of HMCL that trying to compile and run HMCL with the latest LTS version of java.
- pkgver = 3.5.3.227
+ pkgver = 3.5.3.228
pkgrel = 1
url = https://github.com/huanghongxun/HMCL
arch = any
@@ -13,16 +13,16 @@ pkgbase = hmcl-new
source = hmcl.desktop
source = hmcl-launch-script
source = craft_table.png
- source = hmcl-new-3.5.3.227.tar.gz::https://github.com/huanghongxun/HMCL/archive/refs/tags/v3.5.3.227.tar.gz
+ source = hmcl-new-3.5.3.228.tar.gz::https://github.com/huanghongxun/HMCL/archive/refs/tags/v3.5.3.228.tar.gz
source = 0001-Target-java-17.patch
source = 0002-Cleanup.patch
source = 0003-Disable-Pack200.patch
sha256sums = b4e8aa0f349bb3f5dd15a31c5a13ac3e10e5a5bcd2f97cf390041924275e43ef
- sha256sums = 9adb4243a5123ff82cb3678ebb3e889250d745973859d57ab5a14b2867b7cb04
+ sha256sums = 858b5082bc58ddc44fe74625937876e78442fdf5ff948d1e91cddbf170af9eeb
sha256sums = 2989a1b5301b8c7b9afdae5696c6a4e5246afa2d4f1f3d3dad5c192f036a9b4c
- sha256sums = 6d0b1fa5d4a7cab1024e62c12ea6baf19197175e2d2d3af9b23099878057b92f
- sha256sums = cbaf1534ea1f68fdecf6fdbe3fc8dd77db0f73277deb6847719f2c4cc694a498
- sha256sums = 79084c5d3bd61639b0055b8dc07becc2f96b79178d0f7ce7eb7ba8a059cafbcc
- sha256sums = 5df17a227e1cf79cd14145320bfd0fcc3646a65e9b24cf9c48d4c13e4f0e2fa5
+ sha256sums = 4e7511e23bdf6c6742444924c650e38ef458f3e59e68406d379225bb5c551e4d
+ sha256sums = 8f3bc4a0ebb04734cf254233736326429ddcb5b2dd7a04e6ab57f592f71c0331
+ sha256sums = 0e100dbe2c18156749b55d57b11593cef8fe30cf4442796d762f89d0ff646c2a
+ sha256sums = 2d307e19328faffb49fa83836c0cb5233623c75e1a4f52e4c6ffe7215b744ada
pkgname = hmcl-new
diff --git a/0001-Target-java-17.patch b/0001-Target-java-17.patch
index 72f55f0d89b5..8c4eb9a3bdfc 100644
--- a/0001-Target-java-17.patch
+++ b/0001-Target-java-17.patch
@@ -1,27 +1,13 @@
-From 6b9acc420e1898fc3c83e981e872fd084f7e9c4b Mon Sep 17 00:00:00 2001
+From 90c231868a64602632093eee5bc810152f3ba858 Mon Sep 17 00:00:00 2001
From: Aviana Cruz <gwencroft@proton.me>
Date: Sun, 6 Nov 2022 13:28:59 +0800
Subject: [PATCH 1/3] Target java 17
Signed-off-by: Aviana Cruz <gwencroft@proton.me>
---
- HMCL/build.gradle.kts | 2 +-
- build.gradle.kts | 4 ++--
- 2 files changed, 3 insertions(+), 3 deletions(-)
+ build.gradle.kts | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
-diff --git a/HMCL/build.gradle.kts b/HMCL/build.gradle.kts
-index 90756b10..6f5c6e14 100644
---- a/HMCL/build.gradle.kts
-+++ b/HMCL/build.gradle.kts
-@@ -28,7 +28,7 @@ buildscript {
- }
-
- plugins {
-- id("com.github.johnrengelman.shadow") version "7.0.0"
-+ id("com.github.johnrengelman.shadow") version "7.1.2"
- }
-
- val buildNumber = System.getenv("BUILD_NUMBER")?.toInt().let { number ->
diff --git a/build.gradle.kts b/build.gradle.kts
index 8d6a9ce2..674cf430 100644
--- a/build.gradle.kts
diff --git a/0002-Cleanup.patch b/0002-Cleanup.patch
index cb4af5704747..2ad7b356eea7 100644
--- a/0002-Cleanup.patch
+++ b/0002-Cleanup.patch
@@ -1,4 +1,4 @@
-From f9c1f1b68c22d8fb62f6222c1b45a093db7b46a8 Mon Sep 17 00:00:00 2001
+From 08b7e6cd2ccb3576a067542923c44f4f42494825 Mon Sep 17 00:00:00 2001
From: Aviana Cruz <gwencroft@proton.me>
Date: Sun, 6 Nov 2022 13:33:14 +0800
Subject: [PATCH 2/3] Cleanup
@@ -9,36 +9,28 @@ Signed-off-by: Aviana Cruz <gwencroft@proton.me>
.../java/org/jackhuang/hmcl/Launcher.java | 8 -
.../java/org/jackhuang/hmcl/Metadata.java | 1 -
.../jackhuang/hmcl/setting/ConfigHolder.java | 1 -
- .../org/jackhuang/hmcl/ui/Controllers.java | 11 -
+ .../org/jackhuang/hmcl/ui/Controllers.java | 4 -
.../org/jackhuang/hmcl/ui/CrashWindow.java | 6 +-
.../org/jackhuang/hmcl/ui/UpgradeDialog.java | 77 ---
- .../jackhuang/hmcl/ui/main/FeedbackPage.java | 471 ---------------
+ .../jackhuang/hmcl/ui/main/FeedbackPage.java | 470 ------------------
.../hmcl/ui/main/LauncherSettingsPage.java | 22 +-
- .../org/jackhuang/hmcl/ui/main/MainPage.java | 117 +---
- .../org/jackhuang/hmcl/ui/main/RootPage.java | 22 -
- .../jackhuang/hmcl/ui/main/SettingsPage.java | 60 --
- .../jackhuang/hmcl/ui/main/SettingsView.java | 58 --
- .../jackhuang/hmcl/ui/main/SponsorPage.java | 166 ------
- .../multiplayer/LocalServerBroadcaster.java | 153 -----
- .../ui/multiplayer/MultiplayerManager.java | 553 ------------------
- .../hmcl/ui/multiplayer/MultiplayerPage.java | 367 ------------
- .../ui/multiplayer/MultiplayerPageSkin.java | 461 ---------------
- .../hmcl/upgrade/ExecutableHeaderHelper.java | 123 ----
+ .../org/jackhuang/hmcl/ui/main/MainPage.java | 117 +----
+ .../org/jackhuang/hmcl/ui/main/RootPage.java | 17 -
+ .../jackhuang/hmcl/ui/main/SettingsPage.java | 60 ---
+ .../jackhuang/hmcl/ui/main/SettingsView.java | 58 ---
+ .../jackhuang/hmcl/ui/main/SponsorPage.java | 174 -------
+ .../hmcl/upgrade/ExecutableHeaderHelper.java | 123 -----
.../hmcl/upgrade/HMCLDownloadTask.java | 68 ---
.../hmcl/upgrade/IntegrityChecker.java | 134 -----
- .../jackhuang/hmcl/upgrade/RemoteVersion.java | 96 ---
+ .../jackhuang/hmcl/upgrade/RemoteVersion.java | 96 ----
.../jackhuang/hmcl/upgrade/UpdateChannel.java | 42 --
- .../jackhuang/hmcl/upgrade/UpdateChecker.java | 125 ----
- .../jackhuang/hmcl/upgrade/UpdateHandler.java | 257 --------
+ .../jackhuang/hmcl/upgrade/UpdateChecker.java | 125 -----
+ .../jackhuang/hmcl/upgrade/UpdateHandler.java | 257 ----------
.../jackhuang/hmcl/util/CrashReporter.java | 5 -
- 25 files changed, 3 insertions(+), 3401 deletions(-)
+ 21 files changed, 3 insertions(+), 1862 deletions(-)
delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java
delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/main/FeedbackPage.java
delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SponsorPage.java
- delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/LocalServerBroadcaster.java
- delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java
- delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java
- delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java
delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/upgrade/ExecutableHeaderHelper.java
delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java
delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/upgrade/IntegrityChecker.java
@@ -48,28 +40,28 @@ Signed-off-by: Aviana Cruz <gwencroft@proton.me>
delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java
diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java
-index bcd49e1d..0d851357 100644
+index e5aecd61..7d260abc 100644
--- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java
+++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java
-@@ -30,8 +30,6 @@ import org.jackhuang.hmcl.task.AsyncTaskExecutor;
+@@ -29,8 +29,6 @@ import org.jackhuang.hmcl.setting.SambaException;
+ import org.jackhuang.hmcl.task.AsyncTaskExecutor;
import org.jackhuang.hmcl.task.Schedulers;
- import org.jackhuang.hmcl.ui.AwtUtils;
import org.jackhuang.hmcl.ui.Controllers;
-import org.jackhuang.hmcl.upgrade.UpdateChecker;
-import org.jackhuang.hmcl.upgrade.UpdateHandler;
import org.jackhuang.hmcl.util.CrashReporter;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.StringUtils;
-@@ -133,8 +131,6 @@ public final class Launcher extends Application {
-
- initIcon();
+@@ -129,8 +127,6 @@ public final class Launcher extends Application {
+ Platform.setImplicitExit(false);
+ Controllers.initialize(primaryStage);
- UpdateChecker.init();
-
primaryStage.show();
});
} catch (Throwable e) {
-@@ -155,10 +151,6 @@ public final class Launcher extends Application {
+@@ -145,10 +141,6 @@ public final class Launcher extends Application {
}
public static void main(String[] args) {
@@ -105,38 +97,10 @@ index 17342bcd..f7572c81 100644
}
} catch (JsonParseException e) {
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 ba598ac5..47330c98 100644
+index 6b13766f..af18ea1b 100644
--- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java
+++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java
-@@ -45,7 +45,6 @@ import org.jackhuang.hmcl.ui.download.DownloadPage;
- import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
- import org.jackhuang.hmcl.ui.main.LauncherSettingsPage;
- import org.jackhuang.hmcl.ui.main.RootPage;
--import org.jackhuang.hmcl.ui.multiplayer.MultiplayerPage;
- import org.jackhuang.hmcl.ui.versions.GameListPage;
- import org.jackhuang.hmcl.ui.versions.VersionPage;
- import org.jackhuang.hmcl.util.FutureCallback;
-@@ -93,7 +92,6 @@ public final class Controllers {
- accountListPage.authServersProperty().bindContentBidirectional(config().getAuthlibInjectorServers());
- return accountListPage;
- });
-- private static Lazy<MultiplayerPage> multiplayerPage = new Lazy<>(MultiplayerPage::new);
- private static Lazy<LauncherSettingsPage> settingsPage = new Lazy<>(LauncherSettingsPage::new);
-
- private Controllers() {
-@@ -122,11 +120,6 @@ public final class Controllers {
- return rootPage.get();
- }
-
-- // FXThread
-- public static MultiplayerPage getMultiplayerPage() {
-- return multiplayerPage.get();
-- }
--
- // FXThread
- public static LauncherSettingsPage getSettingsPage() {
- return settingsPage.get();
-@@ -305,10 +298,6 @@ public final class Controllers {
+@@ -304,10 +304,6 @@ public final class Controllers {
public static void onHyperlinkAction(String href) {
if (href.startsWith("hmcl://")) {
@@ -173,7 +137,7 @@ index 46ce19ab..daafd6f6 100644
TextArea textArea = new TextArea();
diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java
deleted file mode 100644
-index 0063690c..00000000
+index b43133a1..00000000
--- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java
+++ /dev/null
@@ -1,77 +0,0 @@
@@ -220,7 +184,7 @@ index 0063690c..00000000
- }
-
- {
-- String url = CHANGELOG_URL + remoteVersion.getChannel().channelName + ".html";
+- String url = CHANGELOG_URL + remoteVersion.getChannel().channelName + ".html#nowchange";
- try {
- WebView webView = new WebView();
- webView.getEngine().setUserDataDirectory(Metadata.HMCL_DIRECTORY.toFile());
@@ -256,10 +220,10 @@ index 0063690c..00000000
-}
diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/FeedbackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/FeedbackPage.java
deleted file mode 100644
-index f67736d5..00000000
+index bca7f749..00000000
--- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/FeedbackPage.java
+++ /dev/null
-@@ -1,471 +0,0 @@
+@@ -1,470 +0,0 @@
-/*
- * Hello Minecraft! Launcher
- * Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
@@ -292,6 +256,7 @@ index f67736d5..00000000
-import javafx.geometry.Pos;
-import javafx.scene.control.Label;
-import javafx.scene.layout.*;
+-import org.apache.commons.lang3.mutable.MutableObject;
-import org.jackhuang.hmcl.Metadata;
-import org.jackhuang.hmcl.game.OAuthServer;
-import org.jackhuang.hmcl.setting.Accounts;
@@ -379,7 +344,8 @@ index f67736d5..00000000
- JFXListView<FeedbackResponse> listView = new JFXListView<>();
- spinnerPane.setContent(listView);
- Bindings.bindContent(listView.getItems(), feedbacks);
-- listView.setCellFactory(x -> new MDListCell<FeedbackResponse>(listView) {
+- MutableObject<Object> lastCell = new MutableObject<>();
+- listView.setCellFactory(x -> new MDListCell<FeedbackResponse>(listView, lastCell) {
- private final TwoLineListItem content = new TwoLineListItem();
- private final JFXButton likeButton = new JFXButton();
- private final JFXButton unlikeButton = new JFXButton();
@@ -492,7 +458,7 @@ index f67736d5..00000000
- Controllers.dialog(new AddFeedbackDialog());
- }
-
-- private class LoginDialog extends JFXDialogLayout {
+- private static final class LoginDialog extends JFXDialogLayout {
- private final SpinnerPane spinnerPane = new SpinnerPane();
- private final Label errorLabel = new Label();
- private final BooleanProperty logging = new SimpleBooleanProperty();
@@ -503,10 +469,7 @@ index f67736d5..00000000
- VBox vbox = new VBox(8);
- setBody(vbox);
- HintPane hintPane = new HintPane(MessageDialogPane.MessageType.INFO);
-- hintPane.textProperty().bind(BindingMapping.of(logging).map(logging ->
-- logging
-- ? i18n("account.hmcl.hint")
-- : i18n("account.hmcl.hint")));
+- hintPane.textProperty().bind(BindingMapping.of(logging).map(logging -> i18n("account.hmcl.hint")));
- hintPane.setOnMouseClicked(e -> {
- if (logging.get() && OAuthServer.lastlyOpenedURL != null) {
- FXUtils.copyText(OAuthServer.lastlyOpenedURL);
@@ -787,7 +750,7 @@ index d6c11595..d7586282 100644
public ReadOnlyObjectProperty<State> stateProperty() {
return state.getReadOnlyProperty();
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 3cf4ef93..1b0f9571 100644
+index 111fb82d..add45e23 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
@@ -52,9 +52,6 @@ import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
@@ -799,8 +762,8 @@ index 3cf4ef93..1b0f9571 100644
-import org.jackhuang.hmcl.upgrade.UpdateHandler;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import org.jackhuang.hmcl.util.javafx.MappedObservableList;
-
-@@ -73,14 +70,11 @@ public final class MainPage extends StackPane implements DecoratorPage {
+ import org.jackhuang.hmcl.util.platform.JavaVersion;
+@@ -76,14 +73,11 @@ public final class MainPage extends StackPane implements DecoratorPage {
private final JFXPopup popup = new JFXPopup(menu);
private final StringProperty currentGame = new SimpleStringProperty(this, "currentGame");
@@ -815,7 +778,7 @@ index 3cf4ef93..1b0f9571 100644
private final JFXButton menuButton;
{
-@@ -101,44 +95,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
+@@ -113,44 +107,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
announcementPane = new VBox(16);
@@ -860,7 +823,7 @@ index 3cf4ef93..1b0f9571 100644
StackPane launchPane = new StackPane();
launchPane.getStyleClass().add("launch-pane");
launchPane.setMaxWidth(230);
-@@ -208,7 +164,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
+@@ -220,7 +176,7 @@ public final class MainPage extends StackPane implements DecoratorPage {
launchPane.getChildren().setAll(launchButton, separator, menuButton);
}
@@ -869,7 +832,7 @@ index 3cf4ef93..1b0f9571 100644
menu.setMaxHeight(365);
menu.setMaxWidth(545);
-@@ -222,40 +178,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
+@@ -234,40 +190,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
Bindings.bindContent(menu.getContent(), versionNodes);
}
@@ -910,7 +873,7 @@ index 3cf4ef93..1b0f9571 100644
private void launch() {
Versions.launch(Profiles.getSelectedProfile());
}
-@@ -264,19 +186,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
+@@ -276,19 +198,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
popup.show(menuButton, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.RIGHT, 0, -menuButton.getHeight());
}
@@ -930,7 +893,7 @@ index 3cf4ef93..1b0f9571 100644
@Override
public ReadOnlyObjectWrapper<State> stateProperty() {
return state;
-@@ -294,30 +203,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
+@@ -306,30 +215,6 @@ public final class MainPage extends StackPane implements DecoratorPage {
this.currentGame.set(currentGame);
}
@@ -962,7 +925,7 @@ index 3cf4ef93..1b0f9571 100644
FXUtils.checkFxUserThread();
this.profile = profile;
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 a2156067..94de43a7 100644
+index 94c282c0..1a4885d0 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
@@ -41,7 +41,6 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
@@ -972,8 +935,8 @@ index a2156067..94de43a7 100644
-import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.io.CompressingUtils;
- import org.jackhuang.hmcl.util.io.JarUtils;
-@@ -93,8 +92,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
+ import org.jackhuang.hmcl.util.versioning.VersionNumber;
+@@ -92,8 +91,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
});
FXUtils.onChangeAndOperate(Profiles.selectedVersionProperty(), mainPage::setCurrentGame);
@@ -982,7 +945,7 @@ index a2156067..94de43a7 100644
Profiles.registerVersionsListener(profile -> {
HMCLGameRepository repository = profile.getRepository();
-@@ -151,24 +148,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
+@@ -150,19 +147,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
downloadItem.setTitle(i18n("download"));
downloadItem.setOnAction(e -> Controllers.navigate(Controllers.getDownloadPage()));
@@ -991,23 +954,18 @@ index a2156067..94de43a7 100644
- multiplayerItem.setLeftGraphic(wrap(SVG::lan));
- multiplayerItem.setActionButtonVisible(false);
- multiplayerItem.setTitle(i18n("multiplayer"));
-- if ("true".equalsIgnoreCase(JarUtils.getManifestAttribute("Enable-HiPer", "")))
-- multiplayerItem.setOnAction(e -> Controllers.navigate(Controllers.getMultiplayerPage()));
-- else {
-- JFXHyperlink link = new JFXHyperlink(i18n("multiplayer.hint.details"));
-- link.setOnAction(e -> FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/multiplayer-migrate"));
-- multiplayerItem.setOnAction(e ->
-- Controllers.dialog(
-- new MessageDialogPane.Builder(i18n("multiplayer.hint"), null, MessageDialogPane.MessageType.INFO)
-- .addAction(link)
-- .ok(null)
-- .build()));
-- }
+- JFXHyperlink link = new JFXHyperlink(i18n("multiplayer.hint.details"));
+- link.setOnAction(e -> FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/multiplayer-migrate"));
+- multiplayerItem.setOnAction(e -> Controllers.dialog(
+- new MessageDialogPane.Builder(i18n("multiplayer.hint"), null, MessageDialogPane.MessageType.INFO)
+- .addAction(link)
+- .ok(null)
+- .build()));
-
// sixth item in left sidebar
AdvancedListItem launcherSettingsItem = new AdvancedListItem();
launcherSettingsItem.setLeftGraphic(wrap(SVG::gearOutline));
-@@ -185,7 +164,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
+@@ -179,7 +163,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
.add(gameItem)
.add(downloadItem)
.startCategory(i18n("settings.launcher.general").toUpperCase())
@@ -1016,7 +974,7 @@ index a2156067..94de43a7 100644
// the root page, with the sidebar in left, navigator in center.
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 8e8f5390..9b58e9d5 100644
+index b5347094..622b21e6 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
@@ -27,10 +27,6 @@ import org.jackhuang.hmcl.setting.Settings;
@@ -1030,7 +988,7 @@ index 8e8f5390..9b58e9d5 100644
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.i18n.Locales;
import org.jackhuang.hmcl.util.io.FileUtils;
-@@ -69,57 +65,6 @@ public final class SettingsPage extends SettingsView {
+@@ -68,57 +64,6 @@ public final class SettingsPage extends SettingsView {
Bindings.createObjectBinding(() -> Optional.ofNullable(Settings.instance().getCommonDirectory())
.orElse(i18n("launcher.cache_directory.disabled")),
config().commonDirectoryProperty(), config().commonDirTypeProperty()));
@@ -1088,7 +1046,7 @@ index 8e8f5390..9b58e9d5 100644
}
@Override
-@@ -148,11 +93,6 @@ public final class SettingsPage extends SettingsView {
+@@ -142,11 +87,6 @@ public final class SettingsPage extends SettingsView {
});
}
@@ -1194,10 +1152,10 @@ index af9a3477..44b6dfed 100644
}
diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SponsorPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SponsorPage.java
deleted file mode 100644
-index bd60c8ef..00000000
+index ff685971..00000000
--- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SponsorPage.java
+++ /dev/null
-@@ -1,166 +0,0 @@
+@@ -1,174 +0,0 @@
-/*
- * Hello Minecraft! Launcher
- * Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
@@ -1227,6 +1185,7 @@ index bd60c8ef..00000000
-import javafx.scene.control.Label;
-import javafx.scene.layout.*;
-import javafx.scene.text.TextAlignment;
+-import org.apache.commons.lang3.mutable.MutableObject;
-import org.jackhuang.hmcl.task.Schedulers;
-import org.jackhuang.hmcl.task.Task;
-import org.jackhuang.hmcl.ui.FXUtils;
@@ -1284,10 +1243,17 @@ index bd60c8ef..00000000
- StackPane pane = new StackPane();
- pane.getStyleClass().add("card");
- listView = new JFXListView<>();
+- MutableObject<Object> lastCell = new MutableObject<>();
- listView.setCellFactory((listView) -> new JFXListCell<Sponsor>() {
- @Override
- public void updateItem(Sponsor item, boolean empty) {
- super.updateItem(item, empty);
+-
+- // https://mail.openjdk.org/pipermail/openjfx-dev/2022-July/034764.html
+- if (this == lastCell.getValue() && !isVisible())
+- return;
+- lastCell.setValue(this);
+-
- if (!empty) {
- setText(item.getName());
- setGraphic(null);
@@ -1364,1564 +1330,6 @@ index bd60c8ef..00000000
- }
- }
-}
-diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/LocalServerBroadcaster.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/LocalServerBroadcaster.java
-deleted file mode 100644
-index bdb105a3..00000000
---- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/LocalServerBroadcaster.java
-+++ /dev/null
-@@ -1,153 +0,0 @@
--/*
-- * Hello Minecraft! Launcher
-- * Copyright (C) 2022 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
-- */
--package org.jackhuang.hmcl.ui.multiplayer;
--
--import org.jackhuang.hmcl.event.Event;
--import org.jackhuang.hmcl.event.EventManager;
--import org.jackhuang.hmcl.util.Lang;
--
--import java.io.IOException;
--import java.io.InputStream;
--import java.io.OutputStream;
--import java.net.*;
--import java.nio.channels.UnresolvedAddressException;
--import java.nio.charset.StandardCharsets;
--import java.util.logging.Level;
--import java.util.regex.Matcher;
--import java.util.regex.Pattern;
--
--import static org.jackhuang.hmcl.util.Logging.LOG;
--import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
--
--public class LocalServerBroadcaster implements AutoCloseable {
-- private final String address;
-- private final ThreadGroup threadGroup = new ThreadGroup("JoinSession");
--
-- private final EventManager<Event> onExit = new EventManager<>();
--
-- private boolean running = true;
--
-- public LocalServerBroadcaster(String address) {
-- this.address = address;
-- }
--
-- private Thread newThread(Runnable task, String name) {
-- Thread thread = new Thread(threadGroup, task, name);
-- thread.setDaemon(true);
-- return thread;
-- }
--
-- @Override
-- public void close() {
-- running = false;
-- threadGroup.interrupt();
-- }
--
-- public String getAddress() {
-- return address;
-- }
--
-- public EventManager<Event> onExit() {
-- return onExit;
-- }
--
-- public static final Pattern ADDRESS_PATTERN = Pattern.compile("^\\s*(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d{1,5})\\s*$");
--
-- public void start() {
-- Thread forwardPortThread = newThread(this::forwardPort, "ForwardPort");
-- forwardPortThread.start();
-- }
--
-- private void forwardPort() {
-- try {
-- Matcher matcher = ADDRESS_PATTERN.matcher(address);
-- if (!matcher.find()) {
-- throw new MalformedURLException();
-- }
-- try (Socket forwardingSocket = new Socket();
-- ServerSocket serverSocket = new ServerSocket()) {
-- forwardingSocket.setSoTimeout(30000);
-- forwardingSocket.connect(new InetSocketAddress(matcher.group(1), Lang.parseInt(matcher.group(2), 0)));
--
-- serverSocket.bind(null);
--
-- Thread broadcastMOTDThread = newThread(() -> broadcastMOTD(serverSocket.getLocalPort()), "BroadcastMOTD");
-- broadcastMOTDThread.start();
--
-- LOG.log(Level.INFO, "Listening " + serverSocket.getLocalSocketAddress());
--
-- while (running) {
-- Socket forwardedSocket = serverSocket.accept();
-- LOG.log(Level.INFO, "Accepting client");
-- newThread(() -> forwardTraffic(forwardingSocket, forwardedSocket), "Forward S->D").start();
-- newThread(() -> forwardTraffic(forwardedSocket, forwardingSocket), "Forward D->S").start();
-- }
-- }
-- } catch (IOException | UnresolvedAddressException e) {
-- LOG.log(Level.WARNING, "Error in forwarding port", e);
-- } finally {
-- close();
-- onExit.fireEvent(new Event(this));
-- }
-- }
--
-- private void forwardTraffic(Socket src, Socket dest) {
-- try (InputStream is = src.getInputStream(); OutputStream os = dest.getOutputStream()) {
-- byte[] buf = new byte[1024];
-- while (true) {
-- int len = is.read(buf, 0, buf.length);
-- if (len < 0) break;
-- LOG.log(Level.INFO, "Forwarding buffer " + len);
-- os.write(buf, 0, len);
-- }
-- } catch (IOException e) {
-- LOG.log(Level.WARNING, "Disconnected", e);
-- }
-- }
--
-- private void broadcastMOTD(int port) {
-- DatagramSocket socket;
-- InetAddress broadcastAddress;
-- try {
-- socket = new DatagramSocket();
-- broadcastAddress = InetAddress.getByName("224.0.2.60");
-- } catch (IOException e) {
-- LOG.log(Level.WARNING, "Failed to create datagram socket", e);
-- return;
-- }
--
-- while (running) {
-- try {
-- byte[] data = String.format("[MOTD]%s[/MOTD][AD]%d[/AD]", i18n("multiplayer.session.name.motd"), port).getBytes(StandardCharsets.UTF_8);
-- DatagramPacket packet = new DatagramPacket(data, 0, data.length, broadcastAddress, 4445);
-- socket.send(packet);
-- LOG.finest("Broadcast server 0.0.0.0:" + port);
-- } catch (IOException e) {
-- LOG.log(Level.WARNING, "Failed to send motd packet", e);
-- }
--
-- try {
-- Thread.sleep(1500);
-- } catch (InterruptedException ignored) {
-- return;
-- }
-- }
--
-- socket.close();
-- }
--}
-diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java
-deleted file mode 100644
-index 86d9539b..00000000
---- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerManager.java
-+++ /dev/null
-@@ -1,553 +0,0 @@
--/*
-- * Hello Minecraft! Launcher
-- * Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
-- */
--package org.jackhuang.hmcl.ui.multiplayer;
--
--import com.google.gson.JsonParseException;
--import javafx.application.Platform;
--import javafx.beans.binding.Bindings;
--import javafx.beans.binding.BooleanBinding;
--import org.jackhuang.hmcl.Metadata;
--import org.jackhuang.hmcl.event.Event;
--import org.jackhuang.hmcl.event.EventManager;
--import org.jackhuang.hmcl.setting.ConfigHolder;
--import org.jackhuang.hmcl.task.FileDownloadTask;
--import org.jackhuang.hmcl.task.Task;
--import org.jackhuang.hmcl.ui.Controllers;
--import org.jackhuang.hmcl.ui.FXUtils;
--import org.jackhuang.hmcl.util.*;
--import org.jackhuang.hmcl.util.gson.JsonUtils;
--import org.jackhuang.hmcl.util.io.FileUtils;
--import org.jackhuang.hmcl.util.io.HttpRequest;
--import org.jackhuang.hmcl.util.io.NetworkUtils;
--import org.jackhuang.hmcl.util.platform.Architecture;
--import org.jackhuang.hmcl.util.platform.CommandBuilder;
--import org.jackhuang.hmcl.util.platform.ManagedProcess;
--import org.jackhuang.hmcl.util.platform.OperatingSystem;
--
--import java.io.BufferedWriter;
--import java.io.IOException;
--import java.io.OutputStreamWriter;
--import java.nio.charset.StandardCharsets;
--import java.nio.file.*;
--import java.nio.file.attribute.PosixFilePermission;
--import java.text.DateFormat;
--import java.text.ParseException;
--import java.text.SimpleDateFormat;
--import java.util.*;
--import java.util.concurrent.CompletableFuture;
--import java.util.concurrent.TimeUnit;
--import java.util.function.BiFunction;
--import java.util.logging.Level;
--
--import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig;
--import static org.jackhuang.hmcl.util.Lang.*;
--import static org.jackhuang.hmcl.util.Logging.LOG;
--import static org.jackhuang.hmcl.util.Pair.pair;
--import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
--import static org.jackhuang.hmcl.util.io.ChecksumMismatchException.verifyChecksum;
--
--/**
-- * Cato Management.
-- */
--public final class MultiplayerManager {
-- // static final String HIPER_VERSION = "1.2.2";
-- private static final String HIPER_DOWNLOAD_URL = "https://gitcode.net/to/hiper/-/raw/master/";
-- private static final String HIPER_PACKAGES_URL = HIPER_DOWNLOAD_URL + "packages.sha1";
-- private static final String HIPER_POINTS_URL = "https://cert.mcer.cn/point.yml";
-- private static final Path HIPER_TEMP_CONFIG_PATH = Metadata.HMCL_DIRECTORY.resolve("hiper.yml");
-- private static final Path HIPER_CONFIG_DIR = Metadata.HMCL_DIRECTORY.resolve("hiper-config");
-- public static final Path HIPER_PATH = getHiperLocalDirectory().resolve(getHiperFileName());
-- public static final int HIPER_AGREEMENT_VERSION = 3;
-- private static final String REMOTE_ADDRESS = "127.0.0.1";
-- private static final String LOCAL_ADDRESS = "0.0.0.0";
--
-- private static final Map<Architecture, String> archMap = mapOf(
-- pair(Architecture.ARM32, "arm-7"),
-- pair(Architecture.ARM64, "arm64"),
-- pair(Architecture.X86, "386"),
-- pair(Architecture.X86_64, "amd64"),
-- pair(Architecture.LOONGARCH64, "loong64"),
-- pair(Architecture.MIPS, "mips"),
-- pair(Architecture.MIPS64, "mips64"),
-- pair(Architecture.MIPS64EL, "mips64le"),
-- pair(Architecture.PPC64LE, "ppc64le"),
-- pair(Architecture.RISCV64, "riscv64"),
-- pair(Architecture.MIPSEL, "mipsle")
-- );
--
-- private static final Map<OperatingSystem, String> osMap = mapOf(
-- pair(OperatingSystem.LINUX, "linux"),
-- pair(OperatingSystem.WINDOWS, "windows"),
-- pair(OperatingSystem.OSX, "darwin")
-- );
--
-- private static final String HIPER_TARGET_NAME = String.format("%s-%s",
-- osMap.getOrDefault(OperatingSystem.CURRENT_OS, "windows"),
-- archMap.getOrDefault(Architecture.SYSTEM_ARCH, "amd64"));
--
-- private static final String GSUDO_VERSION = "1.7.1";
-- private static final String GSUDO_TARGET_ARCH = Architecture.SYSTEM_ARCH == Architecture.X86_64 ? "amd64" : "x86";
-- private static final String GSUDO_FILE_NAME = "gsudo.exe";
-- private static final String GSUDO_DOWNLOAD_URL = "https://gitcode.net/glavo/gsudo-release/-/raw/75c952ea3afe8792b0db4fe9bab87d41b21e5895/" + GSUDO_TARGET_ARCH + "/" + GSUDO_FILE_NAME;
-- private static final Path GSUDO_LOCAL_FILE = Metadata.HMCL_DIRECTORY.resolve("libraries").resolve("gsudo").resolve("gsudo").resolve(GSUDO_VERSION).resolve(GSUDO_TARGET_ARCH).resolve(GSUDO_FILE_NAME);
-- private static final boolean USE_GSUDO;
--
-- static final boolean IS_ADMINISTRATOR;
--
-- static final BooleanBinding tokenInvalid = Bindings.createBooleanBinding(
-- () -> {
-- String token = globalConfig().multiplayerTokenProperty().getValue();
-- return token == null || token.isEmpty() || !StringUtils.isAlphabeticOrNumber(token);
-- },
-- globalConfig().multiplayerTokenProperty());
--
-- private static final DateFormat HIPER_VALID_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
--
-- static {
-- boolean isAdministrator = false;
-- if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
-- try {
-- Process process = Runtime.getRuntime().exec(new String[]{"net.exe", "session"});
-- if (!process.waitFor(1, TimeUnit.SECONDS)) {
-- process.destroy();
-- } else {
-- isAdministrator = process.exitValue() == 0;
-- }
-- } catch (Throwable ignored) {
-- }
-- USE_GSUDO = !isAdministrator && OperatingSystem.SYSTEM_BUILD_NUMBER >= 10000;
-- } else {
-- isAdministrator = "root".equals(System.getProperty("user.name"));
-- USE_GSUDO = false;
-- }
-- IS_ADMINISTRATOR = isAdministrator;
-- }
--
-- private static CompletableFuture<Map<String, String>> HASH;
--
-- private MultiplayerManager() {
-- }
--
-- public static Path getConfigPath(String token) {
-- return HIPER_CONFIG_DIR.resolve(Hex.encodeHex(DigestUtils.digest("SHA-1", token)) + ".yml");
-- }
--
-- public static void clearConfiguration() {
-- try {
-- Files.deleteIfExists(HIPER_TEMP_CONFIG_PATH);
-- Files.deleteIfExists(getConfigPath(ConfigHolder.globalConfig().getMultiplayerToken()));
-- } catch (IOException e) {
-- LOG.log(Level.WARNING, "Failed to delete config", e);
-- }
-- }
--
-- private static CompletableFuture<Map<String, String>> getPackagesHash() {
-- FXUtils.checkFxUserThread();
-- if (HASH == null) {
-- HASH = CompletableFuture.supplyAsync(wrap(() -> {
-- String hashList = HttpRequest.GET(HIPER_PACKAGES_URL).getString();
-- Map<String, String> hashes = new HashMap<>();
-- for (String line : hashList.split("\n")) {
-- String[] items = line.trim().split(" {2}");
-- if (items.length == 2 && items[0].length() == 40) {
-- hashes.put(items[1], items[0]);
-- } else {
-- LOG.warning("Failed to parse Hiper packages.sha1 file, line: " + line);
-- }
-- }
-- if (USE_GSUDO) {
-- hashes.put(GSUDO_FILE_NAME, HttpRequest.GET(GSUDO_DOWNLOAD_URL + ".sha1").getString().trim());
-- }
-- return hashes;
-- }));
-- }
-- return HASH;
-- }
--
-- public static Task<Void> downloadHiper() {
-- return Task.fromCompletableFuture(getPackagesHash()).thenComposeAsync(packagesHash -> {
--
-- BiFunction<String, String, FileDownloadTask> getFileDownloadTask = (String remotePath, String localFileName) -> {
-- String hash = packagesHash.get(remotePath);
-- return new FileDownloadTask(
-- NetworkUtils.toURL(String.format("%s%s", HIPER_DOWNLOAD_URL, remotePath)),
-- getHiperLocalDirectory().resolve(localFileName).toFile(),
-- hash == null ? null : new FileDownloadTask.IntegrityCheck("SHA-1", hash));
-- };
--
-- List<Task<?>> tasks;
-- if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
-- if (!packagesHash.containsKey(String.format("%s/hiper.exe", HIPER_TARGET_NAME))) {
-- throw new HiperUnsupportedPlatformException();
-- }
-- tasks = new ArrayList<>(4);
--
-- tasks.add(getFileDownloadTask.apply(String.format("%s/hiper.exe", HIPER_TARGET_NAME), "hiper.exe"));
-- tasks.add(getFileDownloadTask.apply(String.format("%s/wintun.dll", HIPER_TARGET_NAME), "wintun.dll"));
-- // tasks.add(getFileDownloadTask.apply("tap-windows-9.21.2.exe", "tap-windows-9.21.2.exe"));
-- if (USE_GSUDO)
-- tasks.add(new FileDownloadTask(
-- NetworkUtils.toURL(GSUDO_DOWNLOAD_URL),
-- GSUDO_LOCAL_FILE.toFile(),
-- new FileDownloadTask.IntegrityCheck("SHA-1", packagesHash.get(GSUDO_FILE_NAME))
-- ));
-- } else {
-- if (!packagesHash.containsKey(String.format("%s/hiper", HIPER_TARGET_NAME))) {
-- throw new HiperUnsupportedPlatformException();
-- }
-- tasks = Collections.singletonList(getFileDownloadTask.apply(String.format("%s/hiper", HIPER_TARGET_NAME), "hiper"));
-- }
-- return Task.allOf(tasks).thenRunAsync(() -> {
-- if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX || OperatingSystem.CURRENT_OS == OperatingSystem.OSX) {
-- Set<PosixFilePermission> perm = Files.getPosixFilePermissions(HIPER_PATH);
-- perm.add(PosixFilePermission.OWNER_EXECUTE);
-- Files.setPosixFilePermissions(HIPER_PATH, perm);
-- }
-- });
-- });
-- }
--
-- public static void downloadHiperConfig(String token, Path configPath) throws IOException {
-- String certFileContent = HttpRequest.GET(String.format("https://cert.mcer.cn/%s.yml", token)).getString();
-- if (!certFileContent.equals("")) {
-- FileUtils.writeText(configPath, certFileContent);
-- }
-- }
--
-- public static CompletableFuture<HiperSession> startHiper(String token) {
-- return getPackagesHash().thenComposeAsync(packagesHash -> {
-- CompletableFuture<Void> future = new CompletableFuture<>();
-- try {
-- if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
-- verifyChecksum(getHiperLocalDirectory().resolve("hiper.exe"), "SHA-1", packagesHash.get(String.format("%s/hiper.exe", HIPER_TARGET_NAME)));
-- verifyChecksum(getHiperLocalDirectory().resolve("wintun.dll"), "SHA-1", packagesHash.get(String.format("%s/wintun.dll", HIPER_TARGET_NAME)));
-- // verifyChecksumAndDeleteIfNotMatched(getHiperLocalDirectory().resolve("tap-windows-9.21.2.exe"), packagesHash.get("tap-windows-9.21.2.exe"));
-- if (USE_GSUDO)
-- verifyChecksum(GSUDO_LOCAL_FILE, "SHA-1", packagesHash.get(GSUDO_FILE_NAME));
-- } else {
-- verifyChecksum(getHiperLocalDirectory().resolve("hiper"), "SHA-1", packagesHash.get(String.format("%s/hiper", HIPER_TARGET_NAME)));
-- }
--
-- future.complete(null);
-- } catch (IOException e) {
-- LOG.log(Level.WARNING, "Failed to verify HiPer files", e);
-- Platform.runLater(() -> Controllers.taskDialog(MultiplayerManager.downloadHiper()
-- .whenComplete(exception -> {
-- if (exception == null)
-- future.complete(null);
-- else
-- future.completeExceptionally(exception);
-- }), i18n("multiplayer.download"), TaskCancellationAction.NORMAL));
-- }
-- return future;
-- }).thenApplyAsync(wrap(ignored -> {
-- Path configPath = getConfigPath(token);
-- Files.createDirectories(configPath.getParent());
--
-- // 下载 HiPer 配置文件
-- Logging.registerForbiddenToken(token, "<hiper token>");
-- try {
-- downloadHiperConfig(token, configPath);
-- } catch (IOException e) {
-- LOG.log(Level.WARNING, "configuration file cloud cache token has been not available, try to use the local configuration file", e);
-- }
--
-- if (Files.exists(configPath)) {
-- Files.copy(configPath, HIPER_TEMP_CONFIG_PATH, StandardCopyOption.REPLACE_EXISTING);
-- try (BufferedWriter output = Files.newBufferedWriter(HIPER_TEMP_CONFIG_PATH, StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
-- output.write("\n");
-- output.write("logging:\n");
-- output.write(" format: json\n");
-- output.write(" file_path: '" + Metadata.HMCL_DIRECTORY.resolve("logs").resolve("hiper.log").toString().replace("'", "''") + "'\n");
-- }
-- }
--
-- String[] commands = new String[]{HIPER_PATH.toString(), "-config", HIPER_TEMP_CONFIG_PATH.toString()};
--
-- if (!IS_ADMINISTRATOR) {
-- switch (OperatingSystem.CURRENT_OS) {
-- case WINDOWS:
-- if (USE_GSUDO)
-- commands = new String[]{GSUDO_LOCAL_FILE.toString(), HIPER_PATH.toString(), "-config", HIPER_TEMP_CONFIG_PATH.toString()};
-- break;
-- case LINUX:
-- String askpass = System.getProperty("hmcl.askpass", System.getenv("HMCL_ASKPASS"));
-- if ("user".equalsIgnoreCase(askpass))
-- commands = new String[]{"sudo", "-A", HIPER_PATH.toString(), "-config", HIPER_TEMP_CONFIG_PATH.toString()};
-- else if ("false".equalsIgnoreCase(askpass))
-- commands = new String[]{"sudo", "--non-interactive", HIPER_PATH.toString(), "-config", HIPER_TEMP_CONFIG_PATH.toString()};
-- else {
-- if (Files.exists(Paths.get("/usr/bin/pkexec")))
-- commands = new String[]{"/usr/bin/pkexec", HIPER_PATH.toString(), "-config", HIPER_TEMP_CONFIG_PATH.toString()};
-- else
-- commands = new String[]{"sudo", "--non-interactive", HIPER_PATH.toString(), "-config", HIPER_TEMP_CONFIG_PATH.toString()};
-- }
-- break;
-- case OSX:
-- commands = new String[]{"sudo", "--non-interactive", HIPER_PATH.toString(), "-config", HIPER_TEMP_CONFIG_PATH.toString()};
-- break;
-- }
-- }
--
-- Process process = new ProcessBuilder()
-- .command(commands)
-- .start();
--
-- return new HiperSession(process, Arrays.asList(commands));
-- }));
-- }
--
-- public static String getHiperFileName() {
-- if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
-- return "hiper.exe";
-- } else {
-- return "hiper";
-- }
-- }
--
-- public static Path getHiperLocalDirectory() {
-- return Metadata.HMCL_DIRECTORY.resolve("libraries").resolve("hiper").resolve("hiper").resolve("binary");
-- }
--
-- public static class HiperSession extends ManagedProcess {
-- private final EventManager<HiperExitEvent> onExit = new EventManager<>();
-- private final EventManager<HiperIPEvent> onIPAllocated = new EventManager<>();
-- private final EventManager<HiperShowValidUntilEvent> onValidUntil = new EventManager<>();
-- private final BufferedWriter writer;
-- private int error = 0;
--
-- HiperSession(Process process, List<String> commands) {
-- super(process, commands);
--
-- Runtime.getRuntime().addShutdownHook(new Thread(this::stop));
--
-- LOG.info("Started hiper with command: " + new CommandBuilder().addAll(commands));
--
-- addRelatedThread(Lang.thread(this::waitFor, "HiperExitWaiter", true));
-- pumpInputStream(this::onLog);
-- pumpErrorStream(this::onLog);
--
-- writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8));
-- }
--
-- private void onLog(String log) {
-- if (!log.startsWith("{")) {
-- LOG.warning("[HiPer] " + log);
--
-- if (log.startsWith("failed to load config"))
-- error = HiperExitEvent.INVALID_CONFIGURATION;
-- else if (log.startsWith("sudo: ") || log.startsWith("Error getting authority") || log.startsWith("Error: An error occurred trying to start process"))
-- error = HiperExitEvent.NO_SUDO_PRIVILEGES;
-- else if (log.startsWith("Failed to write to log, can't rename log file")) {
-- error = HiperExitEvent.NO_SUDO_PRIVILEGES;
-- stop();
-- }
--
-- return;
-- }
--
-- try {
-- Map<?, ?> logJson = JsonUtils.fromNonNullJson(log, Map.class);
-- String msg = "";
-- if (logJson.containsKey("msg")) {
-- msg = tryCast(logJson.get("msg"), String.class).orElse("");
-- if (msg.contains("Failed to get a tun/tap device")) {
-- error = HiperExitEvent.FAILED_GET_DEVICE;
-- }
-- if (msg.contains("Failed to load certificate from config")) {
-- error = HiperExitEvent.FAILED_LOAD_CONFIG;
-- }
-- if (msg.contains("Validity of client certificate")) {
-- Optional<String> validUntil = tryCast(logJson.get("valid"), String.class);
-- if (validUntil.isPresent()) {
-- try {
-- synchronized (HIPER_VALID_TIME_FORMAT) {
-- Date date = HIPER_VALID_TIME_FORMAT.parse(validUntil.get());
-- onValidUntil.fireEvent(new HiperShowValidUntilEvent(this, date));
-- }
-- } catch (JsonParseException | ParseException e) {
-- LOG.log(Level.WARNING, "Failed to parse certification expire time string: " + validUntil.get());
-- }
-- }
-- }
-- }
--
-- if (logJson.containsKey("network")) {
-- Map<?, ?> network = tryCast(logJson.get("network"), Map.class).orElse(Collections.emptyMap());
-- if (network.containsKey("IP") && msg.contains("Main HostMap created")) {
-- Optional<String> ip = tryCast(network.get("IP"), String.class);
-- ip.ifPresent(s -> onIPAllocated.fireEvent(new HiperIPEvent(this, s)));
-- }
-- }
-- } catch (JsonParseException e) {
-- LOG.log(Level.WARNING, "Failed to parse hiper log: " + log, e);
-- }
-- }
--
-- private void waitFor() {
-- try {
-- int exitCode = getProcess().waitFor();
-- LOG.info("Hiper exited with exitcode " + exitCode);
-- if (error != 0) {
-- onExit.fireEvent(new HiperExitEvent(this, error));
-- } else {
-- onExit.fireEvent(new HiperExitEvent(this, exitCode));
-- }
-- } catch (InterruptedException e) {
-- onExit.fireEvent(new HiperExitEvent(this, HiperExitEvent.INTERRUPTED));
-- } finally {
-- try {
-- if (writer != null)
-- writer.close();
-- } catch (IOException e) {
-- LOG.log(Level.WARNING, "Failed to close Hiper stdin writer", e);
-- }
-- }
-- destroyRelatedThreads();
-- }
--
-- @Override
-- public void stop() {
-- try {
-- writer.write("quit\n");
-- writer.flush();
-- } catch (IOException e) {
-- LOG.log(Level.WARNING, "Failed to quit HiPer", e);
-- }
-- try {
-- getProcess().waitFor(1, TimeUnit.SECONDS);
-- } catch (InterruptedException ignored) {
-- }
-- super.stop();
-- }
--
-- public EventManager<HiperExitEvent> onExit() {
-- return onExit;
-- }
--
-- public EventManager<HiperIPEvent> onIPAllocated() {
-- return onIPAllocated;
-- }
--
-- public EventManager<HiperShowValidUntilEvent> onValidUntil() {
-- return onValidUntil;
-- }
--
-- }
--
-- public static class HiperExitEvent extends Event {
-- private final int exitCode;
--
-- public HiperExitEvent(Object source, int exitCode) {
-- super(source);
-- this.exitCode = exitCode;
-- }
--
-- public int getExitCode() {
-- return exitCode;
-- }
--
-- public static final int INTERRUPTED = -1;
-- public static final int INVALID_CONFIGURATION = -2;
-- public static final int CERTIFICATE_EXPIRED = -3;
-- public static final int FAILED_GET_DEVICE = -4;
-- public static final int FAILED_LOAD_CONFIG = -5;
-- public static final int NO_SUDO_PRIVILEGES = -6;
-- }
--
-- public static class HiperIPEvent extends Event {
-- private final String ip;
--
-- public HiperIPEvent(Object source, String ip) {
-- super(source);
-- this.ip = ip;
-- }
--
-- public String getIP() {
-- return ip;
-- }
-- }
--
-- public static class HiperShowValidUntilEvent extends Event {
-- private final Date validAt;
--
-- public HiperShowValidUntilEvent(Object source, Date validAt) {
-- super(source);
-- this.validAt = validAt;
-- }
--
-- public Date getValidUntil() {
-- return validAt;
-- }
-- }
--
-- public static class HiperExitException extends RuntimeException {
-- private final int exitCode;
-- private final boolean ready;
--
-- public HiperExitException(int exitCode, boolean ready) {
-- this.exitCode = exitCode;
-- this.ready = ready;
-- }
--
-- public int getExitCode() {
-- return exitCode;
-- }
--
-- public boolean isReady() {
-- return ready;
-- }
-- }
--
-- public static class HiperExitTimeoutException extends RuntimeException {
-- }
--
-- public static class HiperSessionExpiredException extends HiperInvalidConfigurationException {
-- }
--
-- public static class HiperInvalidConfigurationException extends RuntimeException {
-- }
--
-- public static class JoinRequestTimeoutException extends RuntimeException {
-- }
--
-- public static class PeerConnectionTimeoutException extends RuntimeException {
-- }
--
-- public static class ConnectionErrorException extends RuntimeException {
-- }
--
-- public static class KickedException extends RuntimeException {
-- private final String reason;
--
-- public KickedException(String reason) {
-- this.reason = reason;
-- }
--
-- public String getReason() {
-- return reason;
-- }
-- }
--
-- public static class HiperInvalidTokenException extends RuntimeException {
-- }
--
-- public static class HiperUnsupportedPlatformException extends RuntimeException {
-- }
--
--}
-diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java
-deleted file mode 100644
-index 3cc29f8c..00000000
---- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPage.java
-+++ /dev/null
-@@ -1,367 +0,0 @@
--/*
-- * Hello Minecraft! Launcher
-- * Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
-- */
--package org.jackhuang.hmcl.ui.multiplayer;
--
--import com.jfoenix.controls.JFXButton;
--import com.jfoenix.controls.JFXDialogLayout;
--import javafx.beans.property.*;
--import javafx.scene.control.Label;
--import javafx.scene.control.Skin;
--import org.jackhuang.hmcl.auth.Account;
--import org.jackhuang.hmcl.auth.offline.OfflineAccount;
--import org.jackhuang.hmcl.event.Event;
--import org.jackhuang.hmcl.setting.DownloadProviders;
--import org.jackhuang.hmcl.setting.Profile;
--import org.jackhuang.hmcl.setting.Profiles;
--import org.jackhuang.hmcl.task.Schedulers;
--import org.jackhuang.hmcl.ui.Controllers;
--import org.jackhuang.hmcl.ui.FXUtils;
--import org.jackhuang.hmcl.ui.construct.*;
--import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
--import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
--import org.jackhuang.hmcl.ui.versions.Versions;
--import org.jackhuang.hmcl.util.HMCLService;
--import org.jackhuang.hmcl.util.StringUtils;
--import org.jackhuang.hmcl.util.TaskCancellationAction;
--import org.jackhuang.hmcl.util.io.ChecksumMismatchException;
--import org.jackhuang.hmcl.util.io.FileUtils;
--import org.jackhuang.hmcl.util.platform.CommandBuilder;
--import org.jackhuang.hmcl.util.platform.OperatingSystem;
--import org.jackhuang.hmcl.util.platform.SystemUtils;
--
--import java.io.File;
--import java.util.Date;
--import java.util.concurrent.CancellationException;
--import java.util.function.Consumer;
--import java.util.logging.Level;
--
--import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig;
--import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
--import static org.jackhuang.hmcl.util.Lang.resolveException;
--import static org.jackhuang.hmcl.util.Logging.LOG;
--import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
--
--public class MultiplayerPage extends DecoratorAnimatedPage implements DecoratorPage, PageAware {
-- private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(State.fromTitle(i18n("multiplayer")));
--
-- private final ReadOnlyObjectWrapper<MultiplayerManager.HiperSession> session = new ReadOnlyObjectWrapper<>();
-- private final IntegerProperty port = new SimpleIntegerProperty();
-- private final StringProperty address = new SimpleStringProperty();
-- private final ReadOnlyObjectWrapper<Date> expireTime = new ReadOnlyObjectWrapper<>();
--
-- private Consumer<MultiplayerManager.HiperExitEvent> onExit;
-- private Consumer<MultiplayerManager.HiperIPEvent> onIPAllocated;
-- private Consumer<MultiplayerManager.HiperShowValidUntilEvent> onValidUntil;
--
-- private final ReadOnlyObjectWrapper<LocalServerBroadcaster> broadcaster = new ReadOnlyObjectWrapper<>();
-- private Consumer<Event> onBroadcasterExit = null;
--
-- public MultiplayerPage() {
-- }
--
-- @Override
-- public void onPageShown() {
-- checkAgreement(this::downloadHiPerIfNecessary);
-- }
--
-- @Override
-- protected Skin<?> createDefaultSkin() {
-- return new MultiplayerPageSkin(this);
-- }
--
-- public int getPort() {
-- return port.get();
-- }
--
-- public IntegerProperty portProperty() {
-- return port;
-- }
--
-- public void setPort(int port) {
-- this.port.set(port);
-- }
--
-- public String getAddress() {
-- return address.get();
-- }
--
-- public StringProperty addressProperty() {
-- return address;
-- }
--
-- public void setAddress(String address) {
-- this.address.set(address);
-- }
--
-- public LocalServerBroadcaster getBroadcaster() {
-- return broadcaster.get();
-- }
--
-- public ReadOnlyObjectWrapper<LocalServerBroadcaster> broadcasterProperty() {
-- return broadcaster;
-- }
--
-- public void setBroadcaster(LocalServerBroadcaster broadcaster) {
-- this.broadcaster.set(broadcaster);
-- }
--
-- public Date getExpireTime() {
-- return expireTime.get();
-- }
--
-- public ReadOnlyObjectWrapper<Date> expireTimeProperty() {
-- return expireTime;
-- }
--
-- public void setExpireTime(Date expireTime) {
-- this.expireTime.set(expireTime);
-- }
--
-- public MultiplayerManager.HiperSession getSession() {
-- return session.get();
-- }
--
-- public ReadOnlyObjectProperty<MultiplayerManager.HiperSession> sessionProperty() {
-- return session.getReadOnlyProperty();
-- }
--
-- void launchGame() {
-- Profile profile = Profiles.getSelectedProfile();
-- Versions.launch(profile, profile.getSelectedVersion(), (launcherHelper) -> {
-- launcherHelper.setKeep();
-- Account account = launcherHelper.getAccount();
-- if (account instanceof OfflineAccount && !(account instanceof MultiplayerOfflineAccount)) {
-- OfflineAccount offlineAccount = (OfflineAccount) account;
-- launcherHelper.setAccount(new MultiplayerOfflineAccount(
-- offlineAccount.getDownloader(),
-- offlineAccount.getUsername(),
-- offlineAccount.getUUID(),
-- offlineAccount.getSkin()
-- ));
-- }
-- });
-- }
--
-- private void checkAgreement(Runnable runnable) {
-- if (globalConfig().getMultiplayerAgreementVersion() < MultiplayerManager.HIPER_AGREEMENT_VERSION) {
-- JFXDialogLayout agreementPane = new JFXDialogLayout();
-- agreementPane.setHeading(new Label(i18n("launcher.agreement")));
-- agreementPane.setBody(new Label(i18n("multiplayer.agreement.prompt")));
-- JFXHyperlink agreementLink = new JFXHyperlink(i18n("launcher.agreement"));
-- agreementLink.setOnAction(e -> HMCLService.openRedirectLink("multiplayer-agreement"));
-- JFXButton yesButton = new JFXButton(i18n("launcher.agreement.accept"));
-- yesButton.getStyleClass().add("dialog-accept");
-- yesButton.setOnAction(e -> {
-- globalConfig().setMultiplayerAgreementVersion(MultiplayerManager.HIPER_AGREEMENT_VERSION);
-- runnable.run();
-- agreementPane.fireEvent(new DialogCloseEvent());
-- });
-- JFXButton noButton = new JFXButton(i18n("launcher.agreement.decline"));
-- noButton.getStyleClass().add("dialog-cancel");
-- noButton.setOnAction(e -> {
-- agreementPane.fireEvent(new DialogCloseEvent());
-- fireEvent(new PageCloseEvent());
-- });
-- agreementPane.setActions(agreementLink, yesButton, noButton);
-- Controllers.dialog(agreementPane);
-- } else {
-- runnable.run();
-- }
-- }
--
-- private void downloadHiPerIfNecessary() {
-- if (!MultiplayerManager.HIPER_PATH.toFile().exists()) {
-- setDisabled(true);
-- Controllers.taskDialog(MultiplayerManager.downloadHiper()
-- .whenComplete(Schedulers.javafx(), exception -> {
-- setDisabled(false);
-- if (exception != null) {
-- if (exception instanceof CancellationException) {
-- Controllers.showToast(i18n("message.cancelled"));
-- } else if (exception instanceof MultiplayerManager.HiperUnsupportedPlatformException) {
-- Controllers.dialog(i18n("multiplayer.download.unsupported"), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR);
-- fireEvent(new PageCloseEvent());
-- } else {
-- Controllers.dialog(DownloadProviders.localizeErrorMessage(exception), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR);
-- fireEvent(new PageCloseEvent());
-- }
-- } else {
-- Controllers.showToast(i18n("multiplayer.download.success"));
-- }
-- }), i18n("multiplayer.download"), TaskCancellationAction.NORMAL);
-- } else {
-- setDisabled(false);
-- }
-- }
--
-- private String localizeErrorMessage(Throwable t) {
-- Throwable e = resolveException(t);
-- if (e instanceof CancellationException) {
-- LOG.info("Connection rejected by the server");
-- return i18n("message.cancelled");
-- } else if (e instanceof MultiplayerManager.HiperInvalidConfigurationException) {
-- LOG.warning("HiPer invalid configuration");
-- return i18n("multiplayer.token.malformed");
-- } else if (e instanceof ChecksumMismatchException) {
-- LOG.log(Level.WARNING, "Failed to verify HiPer files", e);
-- return i18n("multiplayer.error.file_not_found");
-- } else if (e instanceof MultiplayerManager.HiperExitException) {
-- int exitCode = ((MultiplayerManager.HiperExitException) e).getExitCode();
-- LOG.warning("HiPer exited unexpectedly with exit code " + exitCode);
-- return i18n("multiplayer.exit", exitCode);
-- } else if (e instanceof MultiplayerManager.HiperInvalidTokenException) {
-- LOG.warning("invalid token");
-- return i18n("multiplayer.token.invalid");
-- } else {
-- LOG.log(Level.WARNING, "Unknown HiPer exception", e);
-- return e.getLocalizedMessage() + "\n" + StringUtils.getStackTrace(e);
-- }
-- }
--
-- public void start() {
-- MultiplayerManager.startHiper(globalConfig().getMultiplayerToken())
-- .thenAcceptAsync(session -> {
-- this.session.set(session);
-- onExit = session.onExit().registerWeak(this::onExit);
-- onIPAllocated = session.onIPAllocated().registerWeak(this::onIPAllocated);
-- onValidUntil = session.onValidUntil().registerWeak(this::onValidUntil);
-- }, Schedulers.javafx())
-- .exceptionally(throwable -> {
-- runInFX(() -> Controllers.dialog(localizeErrorMessage(throwable), null, MessageDialogPane.MessageType.ERROR));
-- return null;
-- });
-- }
--
-- public void stop() {
-- if (getSession() != null) {
-- getSession().stop();
-- }
-- if (getBroadcaster() != null) {
-- getBroadcaster().close();
-- }
-- clearSession();
-- }
--
-- public void broadcast(String url) {
-- LocalServerBroadcaster broadcaster = new LocalServerBroadcaster(url);
-- this.onBroadcasterExit = broadcaster.onExit().registerWeak(this::onBroadcasterExit);
-- broadcaster.start();
-- this.broadcaster.set(broadcaster);
-- }
--
-- public void stopBroadcasting() {
-- if (getBroadcaster() != null) {
-- getBroadcaster().close();
-- setBroadcaster(null);
-- }
-- }
--
-- private void onBroadcasterExit(Event event) {
-- runInFX(() -> {
-- if (this.broadcaster.get() == event.getSource()) {
-- this.broadcaster.set(null);
-- }
-- });
-- }
--
-- private void clearSession() {
-- this.session.set(null);
-- this.expireTime.set(null);
-- this.onExit = null;
-- this.onIPAllocated = null;
-- this.onValidUntil = null;
-- this.broadcaster.set(null);
-- this.onBroadcasterExit = null;
-- }
--
-- private void onIPAllocated(MultiplayerManager.HiperIPEvent event) {
-- runInFX(() -> this.address.set(event.getIP()));
-- }
--
-- private void onValidUntil(MultiplayerManager.HiperShowValidUntilEvent event) {
-- runInFX(() -> this.expireTime.set(event.getValidUntil()));
-- }
--
-- private void onExit(MultiplayerManager.HiperExitEvent event) {
-- runInFX(() -> {
-- switch (event.getExitCode()) {
-- case 0:
-- break;
-- case MultiplayerManager.HiperExitEvent.CERTIFICATE_EXPIRED:
-- MultiplayerManager.clearConfiguration();
-- Controllers.dialog(i18n("multiplayer.token.expired"));
-- break;
-- case MultiplayerManager.HiperExitEvent.INVALID_CONFIGURATION:
-- MultiplayerManager.clearConfiguration();
-- Controllers.dialog(i18n("multiplayer.token.malformed"));
-- break;
-- case MultiplayerManager.HiperExitEvent.NO_SUDO_PRIVILEGES:
-- if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
-- Controllers.confirm(i18n("multiplayer.error.failed_sudo.windows"), null, MessageDialogPane.MessageType.WARNING, () -> {
-- FXUtils.openLink("https://docs.hmcl.net/multiplayer/admin.html");
-- }, null);
-- } else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) {
-- Controllers.dialog(i18n("multiplayer.error.failed_sudo.linux", MultiplayerManager.HIPER_PATH.toString()), null, MessageDialogPane.MessageType.WARNING);
-- } else if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX) {
-- Controllers.confirm(i18n("multiplayer.error.failed_sudo.mac"), null, MessageDialogPane.MessageType.INFO, () -> {
-- try {
-- String text = "%hmcl-hiper ALL=(ALL:ALL) NOPASSWD: " + MultiplayerManager.HIPER_PATH.toString().replaceAll("[ @!(),:=\\\\]", "\\\\$0") + "\n";
--
-- File sudoersTmp = File.createTempFile("sudoer", ".tmp");
-- sudoersTmp.deleteOnExit();
-- FileUtils.writeText(sudoersTmp, text);
--
-- SystemUtils.callExternalProcess(
-- "osascript", "-e", String.format("do shell script \"%s\" with administrator privileges", String.join(";",
-- "dscl . create /Groups/hmcl-hiper PrimaryGroupID 758",
-- "dscl . merge /Groups/hmcl-hiper GroupMembership " + CommandBuilder.toShellStringLiteral(System.getProperty("user.name")) + "",
-- "mkdir -p /private/etc/sudoers.d",
-- "mv -f " + CommandBuilder.toShellStringLiteral(sudoersTmp.toString()) + " /private/etc/sudoers.d/hmcl-hiper",
-- "chown root /private/etc/sudoers.d/hmcl-hiper",
-- "chmod 0440 /private/etc/sudoers.d/hmcl-hiper"
-- ).replaceAll("[\\\\\"]", "\\\\$0"))
-- );
-- } catch (Throwable e) {
-- LOG.log(Level.WARNING, "Failed to modify sudoers", e);
-- }
-- }, null);
-- }
-- break;
-- case MultiplayerManager.HiperExitEvent.INTERRUPTED:
-- // do nothing
-- break;
-- case MultiplayerManager.HiperExitEvent.FAILED_GET_DEVICE:
-- Controllers.dialog(i18n("multiplayer.error.failed_get_device"));
-- break;
-- case MultiplayerManager.HiperExitEvent.FAILED_LOAD_CONFIG:
-- Controllers.dialog(i18n("multiplayer.error.failed_load_config"));
-- break;
-- default:
-- Controllers.dialog(i18n("multiplayer.exit", event.getExitCode()));
-- break;
-- }
--
-- clearSession();
-- });
-- }
--
-- @Override
-- public ReadOnlyObjectProperty<State> stateProperty() {
-- return state;
-- }
--}
-diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java
-deleted file mode 100644
-index 3110bceb..00000000
---- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/multiplayer/MultiplayerPageSkin.java
-+++ /dev/null
-@@ -1,461 +0,0 @@
--/*
-- * Hello Minecraft! Launcher
-- * Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
-- */
--package org.jackhuang.hmcl.ui.multiplayer;
--
--import com.jfoenix.controls.JFXButton;
--import com.jfoenix.controls.JFXPasswordField;
--import com.jfoenix.controls.JFXTextField;
--import javafx.application.Platform;
--import javafx.beans.InvalidationListener;
--import javafx.beans.WeakInvalidationListener;
--import javafx.beans.binding.Bindings;
--import javafx.collections.ObservableList;
--import javafx.geometry.Insets;
--import javafx.geometry.Pos;
--import javafx.scene.Node;
--import javafx.scene.control.Label;
--import javafx.scene.control.ScrollPane;
--import javafx.scene.layout.*;
--import javafx.stage.FileChooser;
--import javafx.util.StringConverter;
--import org.jackhuang.hmcl.task.Schedulers;
--import org.jackhuang.hmcl.ui.Controllers;
--import org.jackhuang.hmcl.ui.FXUtils;
--import org.jackhuang.hmcl.ui.SVG;
--import org.jackhuang.hmcl.ui.construct.*;
--import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
--import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
--import org.jackhuang.hmcl.util.HMCLService;
--import org.jackhuang.hmcl.util.Lang;
--import org.jackhuang.hmcl.util.StringUtils;
--import org.jackhuang.hmcl.util.i18n.Locales;
--
--import java.io.File;
--import java.io.IOException;
--import java.nio.file.Files;
--import java.nio.file.Path;
--import java.nio.file.StandardCopyOption;
--import java.util.concurrent.CompletableFuture;
--import java.util.logging.Level;
--
--import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig;
--import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
--import static org.jackhuang.hmcl.util.Logging.LOG;
--import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
--
--public class MultiplayerPageSkin extends DecoratorAnimatedPage.DecoratorAnimatedPageSkin<MultiplayerPage> {
--
-- private ObservableList<Node> clients;
--
-- /**
-- * Constructor for all SkinBase instances.
-- *
-- * @param control The control for which this Skin should attach to.
-- */
-- protected MultiplayerPageSkin(MultiplayerPage control) {
-- super(control);
--
-- {
-- AdvancedListBox sideBar = new AdvancedListBox()
-- .addNavigationDrawerItem(item -> {
-- item.setTitle(i18n("version.launch"));
-- item.setLeftGraphic(wrap(SVG::rocketLaunchOutline));
-- item.setOnAction(e -> {
-- control.launchGame();
-- });
-- })
-- .startCategory(i18n("help"))
-- .addNavigationDrawerItem(item -> {
-- item.setTitle(i18n("help"));
-- item.setLeftGraphic(wrap(SVG::helpCircleOutline));
-- item.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/multiplayer"));
-- })
--// .addNavigationDrawerItem(item -> {
--// item.setTitle(i18n("multiplayer.help.1"));
--// item.setLeftGraphic(wrap(SVG::helpCircleOutline));
--// item.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/multiplayer/admin.html"));
--// })
-- .addNavigationDrawerItem(item -> {
-- item.setTitle(i18n("multiplayer.help.2"));
-- item.setLeftGraphic(wrap(SVG::helpCircleOutline));
-- item.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/multiplayer/help.html"));
-- })
-- .addNavigationDrawerItem(item -> {
-- item.setTitle(i18n("multiplayer.help.3"));
-- item.setLeftGraphic(wrap(SVG::helpCircleOutline));
-- item.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/multiplayer/help.html#%E5%88%9B%E5%BB%BA%E6%96%B9"));
-- })
-- .addNavigationDrawerItem(item -> {
-- item.setTitle(i18n("multiplayer.help.4"));
-- item.setLeftGraphic(wrap(SVG::helpCircleOutline));
-- item.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/multiplayer/help.html#%E5%8F%82%E4%B8%8E%E8%80%85"));
-- })
-- .addNavigationDrawerItem(item -> {
-- item.setTitle(i18n("multiplayer.help.text"));
-- item.setLeftGraphic(wrap(SVG::rocketLaunchOutline));
-- item.setOnAction(e -> FXUtils.openLink("https://docs.hmcl.net/multiplayer/text.html"));
-- })
-- .addNavigationDrawerItem(report -> {
-- report.setTitle(i18n("feedback"));
-- report.setLeftGraphic(wrap(SVG::messageAlertOutline));
-- report.setOnAction(e -> HMCLService.openRedirectLink("multiplayer-feedback"));
-- });
-- FXUtils.setLimitWidth(sideBar, 200);
-- setLeft(sideBar);
-- }
--
-- {
-- VBox content = new VBox(16);
-- content.setPadding(new Insets(10));
-- content.setFillWidth(true);
-- ScrollPane scrollPane = new ScrollPane(content);
-- scrollPane.setFitToWidth(true);
-- setCenter(scrollPane);
--
-- VBox mainPane = new VBox(16);
-- {
-- ComponentList offPane = new ComponentList();
-- {
-- HintPane hintPane = new HintPane(MessageType.WARNING);
-- hintPane.setText(i18n("multiplayer.off.hint"));
--
-- BorderPane tokenPane = new BorderPane();
-- {
-- Label tokenTitle = new Label(i18n("multiplayer.token"));
-- BorderPane.setAlignment(tokenTitle, Pos.CENTER_LEFT);
-- tokenPane.setLeft(tokenTitle);
-- // Token acts like password, we hide it here preventing users from accidentally leaking their token when taking screenshots.
-- JFXPasswordField tokenField = new JFXPasswordField();
-- BorderPane.setAlignment(tokenField, Pos.CENTER_LEFT);
-- BorderPane.setMargin(tokenField, new Insets(0, 8, 0, 8));
-- tokenPane.setCenter(tokenField);
-- tokenField.textProperty().bindBidirectional(globalConfig().multiplayerTokenProperty());
-- tokenField.setPromptText(i18n("multiplayer.token.prompt"));
--
-- Validator validator = new Validator("multiplayer.token.format_invalid", StringUtils::isAlphabeticOrNumber);
-- InvalidationListener listener = any -> tokenField.validate();
-- validator.getProperties().put(validator, listener);
-- tokenField.textProperty().addListener(new WeakInvalidationListener(listener));
-- tokenField.getValidators().add(validator);
--
-- JFXHyperlink applyLink = new JFXHyperlink(i18n("multiplayer.token.apply"));
-- BorderPane.setAlignment(applyLink, Pos.CENTER_RIGHT);
-- applyLink.setOnAction(e -> HMCLService.openRedirectLink("multiplayer-static-token"));
-- tokenPane.setRight(applyLink);
-- }
--
-- HBox startPane = new HBox();
-- {
-- JFXButton startButton = new JFXButton(i18n("multiplayer.off.start"));
-- startButton.getStyleClass().add("jfx-button-raised");
-- startButton.setButtonType(JFXButton.ButtonType.RAISED);
-- startButton.setOnMouseClicked(e -> control.start());
-- startButton.disableProperty().bind(MultiplayerManager.tokenInvalid);
--
-- startPane.getChildren().setAll(startButton);
-- startPane.setAlignment(Pos.CENTER_RIGHT);
-- }
--
-- if (!MultiplayerManager.IS_ADMINISTRATOR)
-- offPane.getContent().add(hintPane);
-- offPane.getContent().addAll(tokenPane, startPane);
-- }
--
-- ComponentList onPane = new ComponentList();
-- {
-- BorderPane expirationPane = new BorderPane();
-- expirationPane.setLeft(new Label(i18n("multiplayer.session.expiration")));
-- Label expirationLabel = new Label();
-- expirationLabel.textProperty().bind(Bindings.createStringBinding(() ->
-- control.getExpireTime() == null ? "" : Locales.SIMPLE_DATE_FORMAT.get().format(control.getExpireTime()),
-- control.expireTimeProperty()));
-- expirationPane.setRight(expirationLabel);
--
-- GridPane masterPane = new GridPane();
-- masterPane.setVgap(8);
-- masterPane.setHgap(16);
-- ColumnConstraints titleColumn = new ColumnConstraints();
-- ColumnConstraints valueColumn = new ColumnConstraints();
-- ColumnConstraints rightColumn = new ColumnConstraints();
-- masterPane.getColumnConstraints().setAll(titleColumn, valueColumn, rightColumn);
-- valueColumn.setFillWidth(true);
-- valueColumn.setHgrow(Priority.ALWAYS);
-- {
-- BorderPane titlePane = new BorderPane();
-- GridPane.setColumnSpan(titlePane, 3);
-- Label title = new Label(i18n("multiplayer.master"));
-- titlePane.setLeft(title);
--
-- JFXHyperlink tutorial = new JFXHyperlink(i18n("multiplayer.master.video_tutorial"));
-- titlePane.setRight(tutorial);
-- tutorial.setOnAction(e -> HMCLService.openRedirectLink("multiplayer-tutorial-master"));
-- masterPane.addRow(0, titlePane);
--
-- HintPane hintPane = new HintPane(MessageType.INFO);
-- GridPane.setColumnSpan(hintPane, 3);
-- hintPane.setText(i18n("multiplayer.master.hint"));
-- masterPane.addRow(1, hintPane);
--
-- Label portTitle = new Label(i18n("multiplayer.master.port"));
-- BorderPane.setAlignment(portTitle, Pos.CENTER_LEFT);
--
-- JFXTextField portTextField = new JFXTextField();
-- GridPane.setColumnSpan(portTextField, 2);
-- FXUtils.setValidateWhileTextChanged(portTextField, true);
-- portTextField.getValidators().add(new Validator(i18n("multiplayer.master.port.validate"), (text) -> {
-- Integer value = Lang.toIntOrNull(text);
-- return value != null && 0 <= value && value <= 65535;
-- }));
-- portTextField.textProperty().bindBidirectional(control.portProperty(), new StringConverter<Number>() {
-- @Override
-- public String toString(Number object) {
-- return Integer.toString(object.intValue());
-- }
--
-- @Override
-- public Number fromString(String string) {
-- return Lang.parseInt(string, 0);
-- }
-- });
-- masterPane.addRow(2, portTitle, portTextField);
--
-- Label serverAddressTitle = new Label(i18n("multiplayer.master.server_address"));
-- BorderPane.setAlignment(serverAddressTitle, Pos.CENTER_LEFT);
-- Label serverAddressLabel = new Label();
-- BorderPane.setAlignment(serverAddressLabel, Pos.CENTER_LEFT);
-- serverAddressLabel.textProperty().bind(Bindings.createStringBinding(() -> {
-- return (control.getAddress() == null ? "" : control.getAddress()) + ":" + control.getPort();
-- }, control.addressProperty(), control.portProperty()));
-- JFXButton copyButton = new JFXButton(i18n("multiplayer.master.server_address.copy"));
-- copyButton.setOnAction(e -> FXUtils.copyText(serverAddressLabel.getText()));
-- masterPane.addRow(3, serverAddressTitle, serverAddressLabel, copyButton);
-- }
--
-- VBox slavePane = new VBox(8);
-- {
-- BorderPane titlePane = new BorderPane();
-- Label title = new Label(i18n("multiplayer.slave"));
-- titlePane.setLeft(title);
--
-- JFXHyperlink tutorial = new JFXHyperlink(i18n("multiplayer.slave.video_tutorial"));
-- tutorial.setOnAction(e -> HMCLService.openRedirectLink("multiplayer-tutorial-slave"));
-- titlePane.setRight(tutorial);
--
-- HintPane hintPane = new HintPane(MessageType.INFO);
-- GridPane.setColumnSpan(hintPane, 3);
-- hintPane.setText(i18n("multiplayer.slave.hint"));
-- slavePane.getChildren().add(hintPane);
--
-- HintPane hintPane2 = new HintPane(MessageType.WARNING);
-- GridPane.setColumnSpan(hintPane2, 3);
-- hintPane2.setText(i18n("multiplayer.slave.hint2"));
-- slavePane.getChildren().add(hintPane2);
--
-- GridPane notBroadcastingPane = new GridPane();
-- {
-- notBroadcastingPane.setVgap(8);
-- notBroadcastingPane.setHgap(16);
-- notBroadcastingPane.getColumnConstraints().setAll(titleColumn, valueColumn, rightColumn);
--
-- Label addressTitle = new Label(i18n("multiplayer.slave.server_address"));
--
-- JFXTextField addressField = new JFXTextField();
-- FXUtils.setValidateWhileTextChanged(addressField, true);
-- addressField.getValidators().add(new ServerAddressValidator());
--
-- JFXButton startButton = new JFXButton(i18n("multiplayer.slave.server_address.start"));
-- startButton.setOnAction(e -> control.broadcast(addressField.getText()));
-- notBroadcastingPane.addRow(0, addressTitle, addressField, startButton);
-- }
--
-- GridPane broadcastingPane = new GridPane();
-- {
-- broadcastingPane.setVgap(8);
-- broadcastingPane.setHgap(16);
-- broadcastingPane.getColumnConstraints().setAll(titleColumn, valueColumn, rightColumn);
--
-- Label addressTitle = new Label(i18n("multiplayer.slave.server_address"));
-- Label addressLabel = new Label();
-- addressLabel.textProperty().bind(Bindings.createStringBinding(() ->
-- control.getBroadcaster() != null ? control.getBroadcaster().getAddress() : "",
-- control.broadcasterProperty()));
--
-- JFXButton stopButton = new JFXButton(i18n("multiplayer.slave.server_address.stop"));
-- stopButton.setOnAction(e -> control.stopBroadcasting());
-- broadcastingPane.addRow(0, addressTitle, addressLabel, stopButton);
-- }
--
-- FXUtils.onChangeAndOperate(control.broadcasterProperty(), broadcaster -> {
-- if (broadcaster == null) {
-- slavePane.getChildren().setAll(titlePane, hintPane, hintPane2, notBroadcastingPane);
-- } else {
-- slavePane.getChildren().setAll(titlePane, hintPane, hintPane2, broadcastingPane);
-- }
-- });
-- }
--
-- FXUtils.onChangeAndOperate(control.expireTimeProperty(), t -> {
-- if (t == null) {
-- onPane.getContent().setAll(masterPane, slavePane);
-- } else {
-- onPane.getContent().setAll(expirationPane, masterPane, slavePane);
-- }
-- });
-- }
--
-- FXUtils.onChangeAndOperate(getSkinnable().sessionProperty(), session -> {
-- if (session == null) {
-- mainPane.getChildren().setAll(offPane);
-- } else {
-- mainPane.getChildren().setAll(onPane);
-- }
-- });
-- }
--
-- ComponentList persistencePane = new ComponentList();
-- {
-- HintPane hintPane = new HintPane(MessageType.WARNING);
-- hintPane.setText(i18n("multiplayer.persistence.hint"));
--
-- BorderPane importPane = new BorderPane();
-- {
-- Label left = new Label(i18n("multiplayer.persistence.import"));
-- BorderPane.setAlignment(left, Pos.CENTER_LEFT);
-- importPane.setLeft(left);
--
-- JFXButton importButton = new JFXButton(i18n("multiplayer.persistence.import.button"));
-- importButton.setOnMouseClicked(e -> {
-- Path targetPath = MultiplayerManager.getConfigPath(globalConfig().getMultiplayerToken());
-- if (Files.exists(targetPath)) {
-- LOG.warning("License file " + targetPath + " already exists");
-- Controllers.dialog(i18n("multiplayer.persistence.import.file_already_exists"), null, MessageType.ERROR);
-- return;
-- }
--
-- FileChooser fileChooser = new FileChooser();
-- fileChooser.setTitle(i18n("multiplayer.persistence.import.title"));
-- fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("multiplayer.persistence.license_file"), "*.yml"));
--
-- File file = fileChooser.showOpenDialog(Controllers.getStage());
-- if (file == null)
-- return;
--
-- CompletableFuture<Boolean> future = new CompletableFuture<>();
-- if (file.getName().matches("[a-z0-9]{40}.yml") && !targetPath.getFileName().toString().equals(file.getName())) {
-- Controllers.confirm(i18n("multiplayer.persistence.import.token_not_match"), null, MessageType.QUESTION,
-- () -> future.complete(true),
-- () -> future.complete(false)) ;
-- } else {
-- future.complete(true);
-- }
-- future.thenAcceptAsync(Lang.wrapConsumer(c -> {
-- if (c) Files.copy(file.toPath(), targetPath);
-- })).exceptionally(exception -> {
-- LOG.log(Level.WARNING, "Failed to import license file", exception);
-- Platform.runLater(() -> Controllers.dialog(i18n("multiplayer.persistence.import.failed"), null, MessageType.ERROR));
-- return null;
-- });
-- });
-- importButton.disableProperty().bind(MultiplayerManager.tokenInvalid);
-- importButton.getStyleClass().add("jfx-button-border");
-- importPane.setRight(importButton);
-- }
--
-- BorderPane exportPane = new BorderPane();
-- {
-- Label left = new Label(i18n("multiplayer.persistence.export"));
-- BorderPane.setAlignment(left, Pos.CENTER_LEFT);
-- exportPane.setLeft(left);
--
-- JFXButton exportButton = new JFXButton(i18n("multiplayer.persistence.export.button"));
-- exportButton.setOnMouseClicked(e -> {
-- String token = globalConfig().getMultiplayerToken();
-- Path configPath = MultiplayerManager.getConfigPath(token);
--
-- FileChooser fileChooser = new FileChooser();
-- fileChooser.setTitle(i18n("multiplayer.persistence.export.title"));
-- fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("multiplayer.persistence.license_file"), "*.yml"));
-- fileChooser.setInitialFileName(configPath.getFileName().toString());
--
-- File file = fileChooser.showSaveDialog(Controllers.getStage());
-- if (file == null)
-- return;
--
-- CompletableFuture.runAsync(Lang.wrap(() -> MultiplayerManager.downloadHiperConfig(token, configPath)), Schedulers.io())
-- .handleAsync((ignored, exception) -> {
-- if (exception != null) {
-- LOG.log(Level.INFO, "Unable to download hiper config file", e);
-- }
--
-- if (!Files.isRegularFile(configPath)) {
-- LOG.warning("License file " + configPath + " not exists");
-- Platform.runLater(() -> Controllers.dialog(i18n("multiplayer.persistence.export.file_not_exists"), null, MessageType.ERROR));
-- return null;
-- }
--
-- try {
-- Files.copy(configPath, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
-- } catch (IOException ioException) {
-- LOG.log(Level.WARNING, "Failed to export license file", ioException);
-- Platform.runLater(() -> Controllers.dialog(i18n("multiplayer.persistence.export.failed"), null, MessageType.ERROR));
-- }
--
-- return null;
-- });
--
-- });
-- exportButton.disableProperty().bind(MultiplayerManager.tokenInvalid);
-- exportButton.getStyleClass().add("jfx-button-border");
-- exportPane.setRight(exportButton);
-- }
--
-- persistencePane.getContent().setAll(hintPane, importPane, exportPane);
-- }
--
--
-- ComponentList thanksPane = new ComponentList();
-- {
-- HBox pane = new HBox();
-- pane.setAlignment(Pos.CENTER_LEFT);
--
-- JFXHyperlink aboutLink = new JFXHyperlink(i18n("about"));
-- aboutLink.setOnAction(e -> HMCLService.openRedirectLink("multiplayer-about"));
--
-- HBox placeholder = new HBox();
-- HBox.setHgrow(placeholder, Priority.ALWAYS);
--
-- pane.getChildren().setAll(
-- new Label("Based on HiPer"),
-- aboutLink,
-- placeholder,
-- FXUtils.segmentToTextFlow(i18n("multiplayer.powered_by"), Controllers::onHyperlinkAction));
--
-- thanksPane.getContent().addAll(pane);
-- }
--
-- content.getChildren().setAll(
-- mainPane,
-- ComponentList.createComponentListTitle(i18n("multiplayer.persistence")),
-- persistencePane,
-- ComponentList.createComponentListTitle(i18n("about")),
-- thanksPane
-- );
-- }
-- }
--
--}
diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/ExecutableHeaderHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/ExecutableHeaderHelper.java
deleted file mode 100644
index 66a0f0ee..00000000
@@ -3127,7 +1535,7 @@ index 8b6fdc06..00000000
-}
diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/IntegrityChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/IntegrityChecker.java
deleted file mode 100644
-index 0bb03df9..00000000
+index 8c387047..00000000
--- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/IntegrityChecker.java
+++ /dev/null
@@ -1,134 +0,0 @@
@@ -3157,10 +1565,7 @@ index 0bb03df9..00000000
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
--import java.security.GeneralSecurityException;
--import java.security.KeyFactory;
--import java.security.PublicKey;
--import java.security.Signature;
+-import java.security.*;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.Map;
-import java.util.Map.Entry;
@@ -3196,6 +1601,7 @@ index 0bb03df9..00000000
-
- private static boolean verifyJar(Path jarPath) throws IOException {
- PublicKey publickey = getPublicKey();
+- MessageDigest md = DigestUtils.getDigest("SHA-512");
-
- byte[] signature = null;
- Map<String, byte[]> fileFingerprints = new TreeMap<>();
@@ -3210,7 +1616,8 @@ index 0bb03df9..00000000
- if (SIGNATURE_FILE.equals(filename)) {
- signature = IOUtils.readFullyAsByteArray(in);
- } else {
-- fileFingerprints.put(filename, DigestUtils.digest("SHA-512", in));
+- md.reset();
+- fileFingerprints.put(filename, DigestUtils.digest(md, in));
- }
- }
- }
@@ -3224,7 +1631,8 @@ index 0bb03df9..00000000
- Signature verifier = Signature.getInstance("SHA512withRSA");
- verifier.initVerify(publickey);
- for (Entry<String, byte[]> entry : fileFingerprints.entrySet()) {
-- verifier.update(DigestUtils.digest("SHA-512", entry.getKey().getBytes(UTF_8)));
+- md.reset();
+- verifier.update(md.digest(entry.getKey().getBytes(UTF_8)));
- verifier.update(entry.getValue());
- }
- return verifier.verify(signature);
@@ -3417,7 +1825,7 @@ index 998a3da7..00000000
-}
diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java
deleted file mode 100644
-index 21ed94d2..00000000
+index cc7ce8f2..00000000
--- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java
+++ /dev/null
@@ -1,125 +0,0 @@
@@ -3517,7 +1925,7 @@ index 21ed94d2..00000000
-
- private static boolean isDevelopmentVersion(String version) {
- return version.contains("@") || // eg. @develop@
-- version.contains("SNAPSHOT"); // eg. 3.1.SNAPSHOT
+- version.contains("SNAPSHOT"); // eg. 3.5.SNAPSHOT
- }
-
- public static void requestCheckUpdate(UpdateChannel channel) {
@@ -3548,7 +1956,7 @@ index 21ed94d2..00000000
-}
diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java
deleted file mode 100644
-index 4fa0e2ca..00000000
+index 4cd06ef8..00000000
--- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java
+++ /dev/null
@@ -1,257 +0,0 @@
@@ -3583,12 +1991,12 @@ index 4fa0e2ca..00000000
-import org.jackhuang.hmcl.ui.UpgradeDialog;
-import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
-import org.jackhuang.hmcl.util.StringUtils;
+-import org.jackhuang.hmcl.ui.SwingUtils;
-import org.jackhuang.hmcl.util.TaskCancellationAction;
-import org.jackhuang.hmcl.util.io.FileUtils;
-import org.jackhuang.hmcl.util.io.JarUtils;
-import org.jackhuang.hmcl.util.platform.JavaVersion;
-
--import javax.swing.*;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
@@ -3619,7 +2027,7 @@ index 4fa0e2ca..00000000
- performMigration();
- } catch (IOException e) {
- LOG.log(Level.WARNING, "Failed to perform migration", e);
-- JOptionPane.showMessageDialog(null, i18n("fatal.apply_update_failure", Metadata.PUBLISH_URL) + "\n" + StringUtils.getStackTrace(e), "Error", JOptionPane.ERROR_MESSAGE);
+- SwingUtils.showErrorDialog(i18n("fatal.apply_update_failure", Metadata.PUBLISH_URL) + "\n" + StringUtils.getStackTrace(e));
- }
- return true;
- }
@@ -3629,13 +2037,13 @@ index 4fa0e2ca..00000000
- applyUpdate(Paths.get(args[1]));
- } catch (IOException e) {
- LOG.log(Level.WARNING, "Failed to apply update", e);
-- JOptionPane.showMessageDialog(null, i18n("fatal.apply_update_failure", Metadata.PUBLISH_URL) + "\n" + StringUtils.getStackTrace(e), "Error", JOptionPane.ERROR_MESSAGE);
+- SwingUtils.showErrorDialog(i18n("fatal.apply_update_failure", Metadata.PUBLISH_URL) + "\n" + StringUtils.getStackTrace(e));
- }
- return true;
- }
-
- if (isFirstLaunchAfterUpgrade()) {
-- JOptionPane.showMessageDialog(null, i18n("fatal.migration_requires_manual_reboot"), "Info", JOptionPane.INFORMATION_MESSAGE);
+- SwingUtils.showInfoDialog(i18n("fatal.migration_requires_manual_reboot"));
- return true;
- }
-
diff --git a/0003-Disable-Pack200.patch b/0003-Disable-Pack200.patch
index 7b79ea901b71..b1114352920d 100644
--- a/0003-Disable-Pack200.patch
+++ b/0003-Disable-Pack200.patch
@@ -1,4 +1,4 @@
-From f5ad1228a262f21a5bd813727cbf0810eeecf890 Mon Sep 17 00:00:00 2001
+From e12830055ce45b4271b7c65686c98622dd36e3c7 Mon Sep 17 00:00:00 2001
From: Aviana Cruz <gwencroft@proton.me>
Date: Wed, 23 Nov 2022 09:27:28 +0800
Subject: [PATCH 3/3] Disable Pack200
@@ -9,10 +9,10 @@ Signed-off-by: Aviana Cruz <gwencroft@proton.me>
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/HMCL/build.gradle.kts b/HMCL/build.gradle.kts
-index 6f5c6e14..4a229e53 100644
+index ecc6d792..f62e1437 100644
--- a/HMCL/build.gradle.kts
+++ b/HMCL/build.gradle.kts
-@@ -178,7 +178,6 @@ tasks.getByName<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("sha
+@@ -176,7 +176,6 @@ tasks.getByName<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("sha
}
doLast {
@@ -20,7 +20,7 @@ index 6f5c6e14..4a229e53 100644
attachSignature(jarPath)
createChecksum(jarPath)
}
-@@ -225,15 +224,12 @@ tasks.processResources {
+@@ -223,15 +222,12 @@ tasks.processResources {
dependsOn(rootProject.tasks["generateOpenJFXDependencies"])
}
diff --git a/PKGBUILD b/PKGBUILD
index b73ca62dff4d..07bb45c39d14 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -6,7 +6,7 @@
pkgname=hmcl-new
_pkgname=HMCL
_ver=3.5.3
-_build=227
+_build=228
pkgver=$_ver.$_build
pkgrel=1
pkgdesc='An unofficial build of HMCL that trying to compile and run HMCL with the latest LTS version of java.'
@@ -26,12 +26,12 @@ source=('hmcl.desktop'
"0003-Disable-Pack200.patch"
)
sha256sums=('b4e8aa0f349bb3f5dd15a31c5a13ac3e10e5a5bcd2f97cf390041924275e43ef'
- '9adb4243a5123ff82cb3678ebb3e889250d745973859d57ab5a14b2867b7cb04'
+ '858b5082bc58ddc44fe74625937876e78442fdf5ff948d1e91cddbf170af9eeb'
'2989a1b5301b8c7b9afdae5696c6a4e5246afa2d4f1f3d3dad5c192f036a9b4c'
- '6d0b1fa5d4a7cab1024e62c12ea6baf19197175e2d2d3af9b23099878057b92f'
- 'cbaf1534ea1f68fdecf6fdbe3fc8dd77db0f73277deb6847719f2c4cc694a498'
- '79084c5d3bd61639b0055b8dc07becc2f96b79178d0f7ce7eb7ba8a059cafbcc'
- '5df17a227e1cf79cd14145320bfd0fcc3646a65e9b24cf9c48d4c13e4f0e2fa5')
+ '4e7511e23bdf6c6742444924c650e38ef458f3e59e68406d379225bb5c551e4d'
+ '8f3bc4a0ebb04734cf254233736326429ddcb5b2dd7a04e6ab57f592f71c0331'
+ '0e100dbe2c18156749b55d57b11593cef8fe30cf4442796d762f89d0ff646c2a'
+ '2d307e19328faffb49fa83836c0cb5233623c75e1a4f52e4c6ffe7215b744ada')
prepare() {
cd "$_pkgname-$pkgver"
@@ -56,7 +56,8 @@ build() {
check() {
cd "$_pkgname-$pkgver"
- gradle test
+ gradle test --no-daemon \
+ -D 'org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m'
}
package() {
diff --git a/hmcl-launch-script b/hmcl-launch-script
index 35ad0c0f05f3..4a923a4e8dd7 100755
--- a/hmcl-launch-script
+++ b/hmcl-launch-script
@@ -2,9 +2,9 @@
set -eu
if [ "${1:-}" != -p ]; then
- WORKDIR=${XDG_CONFIG_HOME:-$HOME/.config}/hmcl
- mkdir -p "$WORKDIR"
- cd "$WORKDIR"
+ WORKDIR=${XDG_CONFIG_HOME:-$HOME/.config}/hmcl
+ mkdir -p "$WORKDIR"
+ cd "$WORKDIR"
fi
JAVA_HOME=/usr/lib/jvm/$(pacman -Ql java-openjfx | grep javafx | head -n 1 | cut -d / -f 5)