From 08b7e6cd2ccb3576a067542923c44f4f42494825 Mon Sep 17 00:00:00 2001 From: Aviana Cruz Date: Sun, 6 Nov 2022 13:33:14 +0800 Subject: [PATCH 2/3] Cleanup Co-authored-by: zhaose Signed-off-by: Aviana Cruz --- .../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 | 4 - .../org/jackhuang/hmcl/ui/CrashWindow.java | 6 +- .../org/jackhuang/hmcl/ui/UpgradeDialog.java | 77 --- .../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 | 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/UpdateChannel.java | 42 -- .../jackhuang/hmcl/upgrade/UpdateChecker.java | 125 ----- .../jackhuang/hmcl/upgrade/UpdateHandler.java | 257 ---------- .../jackhuang/hmcl/util/CrashReporter.java | 5 - 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/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 delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java 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 e5aecd61..7d260abc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java @@ -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.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; @@ -129,8 +127,6 @@ public final class Launcher extends Application { Platform.setImplicitExit(false); Controllers.initialize(primaryStage); - UpdateChecker.init(); - primaryStage.show(); }); } catch (Throwable e) { @@ -145,10 +141,6 @@ public final class Launcher extends Application { } public static void main(String[] args) { - if (UpdateHandler.processArguments(args)) { - return; - } - Thread.setDefaultUncaughtExceptionHandler(CRASH_REPORTER); AsyncTaskExecutor.setUncaughtExceptionHandler(new CrashReporter(false)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java b/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java index f24a2d47..7f49a55f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Metadata.java @@ -35,7 +35,6 @@ public final class Metadata { public static final String TITLE = NAME + " " + VERSION; public static final String FULL_TITLE = FULL_NAME + " v" + VERSION; - public static final String UPDATE_URL = System.getProperty("hmcl.update_source.override", "https://hmcl.huangyuhui.net/api/update_link"); public static final String CONTACT_URL = "https://github.com/huanghongxun/HMCL/issues"; public static final String HELP_URL = "https://hmcl.huangyuhui.net/help"; public static final String CHANGELOG_URL = "https://docs.hmcl.net/changelog/"; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java index 17342bcd..f7572c81 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java @@ -169,7 +169,6 @@ public final class ConfigHolder { LOG.info("Config is empty"); } else { Map raw = new Gson().fromJson(content, Map.class); - ConfigUpgrader.upgradeConfig(deserialized, raw); return deserialized; } } 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 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 @@ -304,10 +304,6 @@ public final class Controllers { public static void onHyperlinkAction(String href) { if (href.startsWith("hmcl://")) { - if ("hmcl://settings/feedback".equals(href)) { - Controllers.getSettingsPage().showFeedback(); - Controllers.navigate(Controllers.getSettingsPage()); - } } else { FXUtils.openLink(href); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/CrashWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/CrashWindow.java index 46ce19ab..daafd6f6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/CrashWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/CrashWindow.java @@ -27,7 +27,6 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.upgrade.UpdateChecker; import static org.jackhuang.hmcl.ui.FXUtils.newImage; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -39,10 +38,7 @@ public class CrashWindow extends Stage { public CrashWindow(String text) { Label lblCrash = new Label(); - if (UpdateChecker.isOutdated()) - lblCrash.setText(i18n("launcher.crash_out_dated")); - else - lblCrash.setText(i18n("launcher.crash")); + lblCrash.setText(i18n("launcher.crash")); lblCrash.setWrapText(true); 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 b43133a1..00000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/UpgradeDialog.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.ui; - -import com.jfoenix.controls.JFXButton; -import com.jfoenix.controls.JFXDialogLayout; -import javafx.concurrent.Worker; -import javafx.scene.control.Label; -import javafx.scene.web.WebEngine; -import javafx.scene.web.WebView; -import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; -import org.jackhuang.hmcl.upgrade.RemoteVersion; - -import java.util.logging.Level; - -import static org.jackhuang.hmcl.Metadata.CHANGELOG_URL; -import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; -import static org.jackhuang.hmcl.util.Logging.LOG; -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; - -public class UpgradeDialog extends JFXDialogLayout { - public UpgradeDialog(RemoteVersion remoteVersion, Runnable updateRunnable) { - { - setHeading(new Label(i18n("update.changelog"))); - } - - { - String url = CHANGELOG_URL + remoteVersion.getChannel().channelName + ".html#nowchange"; - try { - WebView webView = new WebView(); - webView.getEngine().setUserDataDirectory(Metadata.HMCL_DIRECTORY.toFile()); - WebEngine engine = webView.getEngine(); - engine.load(url); - engine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> { - if (newValue == Worker.State.FAILED) { - LOG.warning("Failed to load update log, trying to open it in browser"); - FXUtils.openLink(url); - setBody(); - } - }); - setBody(webView); - } catch (NoClassDefFoundError | UnsatisfiedLinkError e) { - LOG.log(Level.WARNING, "WebView is missing or initialization failed", e); - FXUtils.openLink(url); - } - } - - { - JFXButton updateButton = new JFXButton(i18n("update.accept")); - updateButton.getStyleClass().add("dialog-accept"); - updateButton.setOnMouseClicked(e -> updateRunnable.run()); - - JFXButton cancelButton = new JFXButton(i18n("button.cancel")); - cancelButton.getStyleClass().add("dialog-cancel"); - cancelButton.setOnMouseClicked(e -> fireEvent(new DialogCloseEvent())); - - setActions(updateButton, cancelButton); - onEscPressed(this, cancelButton::fire); - } - } -} 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 bca7f749..00000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/FeedbackPage.java +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.ui.main; - -import com.google.gson.JsonParseException; -import com.google.gson.annotations.SerializedName; -import com.google.gson.reflect.TypeToken; -import com.jfoenix.controls.*; -import javafx.beans.binding.Bindings; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.geometry.Insets; -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; -import org.jackhuang.hmcl.setting.HMCLAccounts; -import org.jackhuang.hmcl.setting.Theme; -import org.jackhuang.hmcl.task.Schedulers; -import org.jackhuang.hmcl.task.Task; -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.util.StringUtils; -import org.jackhuang.hmcl.util.io.HttpRequest; -import org.jackhuang.hmcl.util.io.NetworkUtils; -import org.jackhuang.hmcl.util.io.ResponseCodeException; -import org.jackhuang.hmcl.util.javafx.BindingMapping; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; -import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; -import static org.jackhuang.hmcl.util.Lang.mapOf; -import static org.jackhuang.hmcl.util.Pair.pair; -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; - -public class FeedbackPage extends VBox implements PageAware { - private final ObservableList feedbacks = FXCollections.observableArrayList(); - private final SpinnerPane spinnerPane = new SpinnerPane(); - - public FeedbackPage() { - setSpacing(10); - setPadding(new Insets(10)); - - { - HBox loginPane = new HBox(16); - loginPane.setAlignment(Pos.CENTER_LEFT); - loginPane.getStyleClass().add("card"); - - TwoLineListItem accountInfo = new TwoLineListItem(); - HBox.setHgrow(accountInfo, Priority.ALWAYS); - accountInfo.titleProperty().bind(BindingMapping.of(HMCLAccounts.accountProperty()) - .map(account -> account == null ? i18n("account.not_logged_in") : account.getNickname())); - accountInfo.subtitleProperty().bind(BindingMapping.of(HMCLAccounts.accountProperty()) - .map(account -> account == null ? i18n("account.not_logged_in") : account.getEmail())); - - JFXButton logButton = new JFXButton(); - logButton.textProperty().bind(BindingMapping.of(HMCLAccounts.accountProperty()) - .map(account -> account == null ? i18n("account.login") : i18n("account.logout"))); - logButton.setOnAction(e -> log()); - - loginPane.getChildren().setAll(accountInfo, logButton); - getChildren().add(loginPane); - } - - { - HBox searchPane = new HBox(8); - searchPane.getStyleClass().add("card"); - getChildren().add(searchPane); - - JFXTextField searchField = new JFXTextField(); - searchField.setOnAction(e -> search(searchField.getText(), "time", true)); - HBox.setHgrow(searchField, Priority.ALWAYS); - searchField.setPromptText(i18n("search")); - - JFXButton searchButton = new JFXButton(); - searchButton.getStyleClass().add("toggle-icon4"); - searchButton.setGraphic(SVG.magnify(Theme.blackFillBinding(), -1, -1)); - searchButton.setOnAction(e -> search(searchField.getText(), "time", true)); - - JFXButton addButton = new JFXButton(); - addButton.getStyleClass().add("toggle-icon4"); - addButton.setGraphic(SVG.plus(Theme.blackFillBinding(), -1, -1)); - addButton.setOnAction(e -> addFeedback()); - - searchPane.getChildren().setAll(searchField, searchButton, addButton); - } - - { - spinnerPane.getStyleClass().add("card"); - VBox.setVgrow(spinnerPane, Priority.ALWAYS); - JFXListView listView = new JFXListView<>(); - spinnerPane.setContent(listView); - Bindings.bindContent(listView.getItems(), feedbacks); - MutableObject lastCell = new MutableObject<>(); - listView.setCellFactory(x -> new MDListCell(listView, lastCell) { - private final TwoLineListItem content = new TwoLineListItem(); - private final JFXButton likeButton = new JFXButton(); - private final JFXButton unlikeButton = new JFXButton(); - private final HBox container; - - { - container = new HBox(8); - container.setPickOnBounds(false); - container.setAlignment(Pos.CENTER_LEFT); - HBox.setHgrow(content, Priority.ALWAYS); - content.setMouseTransparent(false); - setSelectable(); - - likeButton.getStyleClass().add("toggle-icon4"); - likeButton.setGraphic(FXUtils.limitingSize(SVG.thumbUpOutline(Theme.blackFillBinding(), 24, 24), 24, 24)); - - unlikeButton.getStyleClass().add("toggle-icon4"); - unlikeButton.setGraphic(FXUtils.limitingSize(SVG.thumbDownOutline(Theme.blackFillBinding(), 24, 24), 24, 24)); - - container.getChildren().setAll(content, likeButton, unlikeButton); - - StackPane.setMargin(container, new Insets(10, 16, 10, 16)); - getContainer().getChildren().setAll(container); - } - - @Override - protected void updateControl(FeedbackResponse feedback, boolean empty) { - if (empty) return; - content.setTitle(feedback.getTitle()); - content.setSubtitle(feedback.getAuthor()); - content.getTags().setAll( - "#" + feedback.getId(), - i18n("feedback.state." + feedback.getState().name().toLowerCase(Locale.US)), - i18n("feedback.type." + feedback.getType().name().toLowerCase(Locale.US))); - content.setOnMouseClicked(e -> { - getFeedback(feedback.getId()) - .thenAcceptAsync(Schedulers.javafx(), f -> { - Controllers.dialog(new ViewFeedbackDialog(f)); - }) - .start(); - }); - } - }); - - getChildren().add(spinnerPane); - } - } - - @Override - public void onPageShown() { - search("", "time", false); - } - - private void search(String keyword, String order, boolean showAll) { - HMCLAccounts.HMCLAccount account = HMCLAccounts.getAccount(); - Task.supplyAsync(() -> { - Map query = mapOf( - pair("keyword", keyword), - pair("order", order) - ); - if (showAll) { - query.put("showAll", "1"); - } - HttpRequest req = HttpRequest.GET(NetworkUtils.withQuery("https://hmcl.huangyuhui.net/api/feedback", query)); - if (account != null) { - req.authorization("Bearer", HMCLAccounts.getAccount().getIdToken()) - .header("Authorization-Provider", HMCLAccounts.getAccount().getProvider()); - } - return req.>getJson(new TypeToken>(){}.getType()); - }).whenComplete(Schedulers.javafx(), (result, exception) -> { - spinnerPane.hideSpinner(); - if (exception != null) { - if (exception instanceof ResponseCodeException) { - int responseCode = ((ResponseCodeException) exception).getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) { - spinnerPane.setFailedReason(i18n("feedback.failed.permission")); - return; - } else if (responseCode == 429) { - spinnerPane.setFailedReason(i18n("feedback.failed.too_frequently")); - return; - } - } - spinnerPane.setFailedReason(i18n("feedback.failed")); - } else { - feedbacks.setAll(result); - } - }).start(); - } - - private Task getFeedback(int id) { - return Task.supplyAsync(() -> HttpRequest.GET("https://hmcl.huangyuhui.net/api/feedback/" + id).getJson(FeedbackResponse.class)); - } - - private void log() { - if (HMCLAccounts.getAccount() == null) { - // login - Controllers.dialog(new LoginDialog()); - } else { - // logout - HMCLAccounts.setAccount(null); - } - } - - private void addFeedback() { - if (HMCLAccounts.getAccount() == null) { - Controllers.dialog(i18n("feedback.add.login")); - return; - } - - Controllers.dialog(new AddFeedbackDialog()); - } - - 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(); - - public LoginDialog() { - setHeading(new Label(i18n("feedback.login"))); - - VBox vbox = new VBox(8); - setBody(vbox); - HintPane hintPane = new HintPane(MessageDialogPane.MessageType.INFO); - 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); - } - }); - vbox.getChildren().setAll(hintPane); - - JFXButton loginButton = new JFXButton(); - spinnerPane.setContent(loginButton); - loginButton.setText(i18n("account.login")); - loginButton.setOnAction(e -> login()); - - JFXButton cancelButton = new JFXButton(); - cancelButton.setText(i18n("button.cancel")); - cancelButton.setOnAction(e -> fireEvent(new DialogCloseEvent())); - onEscPressed(this, cancelButton::fire); - - setActions(errorLabel, spinnerPane, cancelButton); - } - - private void login() { - spinnerPane.showSpinner(); - errorLabel.setText(""); - logging.set(true); - - HMCLAccounts.login().whenComplete(Schedulers.javafx(), (result, exception) -> { - logging.set(false); - if (exception != null) { - if (exception instanceof IOException) { - errorLabel.setText(i18n("account.failed.connect_authentication_server")); - } else if (exception instanceof JsonParseException) { - errorLabel.setText(i18n("account.failed.server_response_malformed")); - } else { - errorLabel.setText(Accounts.localizeErrorMessage(exception)); - } - } else { - fireEvent(new DialogCloseEvent()); - } - }).start(); - } - } - - private static class AddFeedbackDialog extends DialogPane { - - JFXTextField titleField = new JFXTextField(); - JFXComboBox comboBox = new JFXComboBox<>(); - JFXTextArea contentArea = new JFXTextArea(); - - public AddFeedbackDialog() { - setTitle(i18n("feedback.add")); - - GridPane body = new GridPane(); - body.setVgap(8); - body.setHgap(8); - - HintPane searchHintPane = new HintPane(MessageDialogPane.MessageType.WARNING); - GridPane.setColumnSpan(searchHintPane, 2); - searchHintPane.setText(i18n("feedback.add.hint.search_before_add")); - body.addRow(0, searchHintPane); - - HintPane titleHintPane = new HintPane(MessageDialogPane.MessageType.INFO); - GridPane.setColumnSpan(titleHintPane, 2); - titleHintPane.setText(i18n("feedback.add.hint.title")); - body.addRow(1, titleHintPane); - - titleField.setValidators(new RequiredValidator()); - body.addRow(2, new Label(i18n("feedback.title")), titleField); - - comboBox.setMaxWidth(-1); - comboBox.getItems().setAll(FeedbackType.values()); - comboBox.getSelectionModel().select(0); - comboBox.setConverter(stringConverter(e -> i18n("feedback.type." + e.name().toLowerCase()))); - body.addRow(3, new Label(i18n("feedback.type")), comboBox); - - Label contentLabel = new Label(i18n("feedback.content")); - GridPane.setColumnSpan(contentLabel, 2); - body.addRow(4, contentLabel); - - contentArea.setValidators(new RequiredValidator()); - contentArea.setPromptText(i18n("feedback.add.hint.content")); - GridPane.setColumnSpan(contentArea, 2); - body.addRow(5, contentArea); - - validProperty().bind(Bindings.createBooleanBinding(() -> { - return titleField.validate() && contentArea.validate(); - }, titleField.textProperty(), contentArea.textProperty())); - - setBody(body); - } - - @Override - protected void onAccept() { - setLoading(); - - addFeedback(titleField.getText(), comboBox.getValue(), contentArea.getText()) - .whenComplete(Schedulers.javafx(), exception -> { - if (exception != null) { - onFailure(exception.getLocalizedMessage()); - } else { - onSuccess(); - } - }) - .start(); - } - - private Task addFeedback(String title, FeedbackType feedbackType, String content) { - return Task.runAsync(() -> { - HttpRequest.POST("https://hmcl.huangyuhui.net/api/feedback") - .json(mapOf( - pair("title", title), - pair("content", content), - pair("type", feedbackType.name().toLowerCase(Locale.ROOT)), - pair("launcher_version", Metadata.VERSION) - )) - .authorization("Bearer", HMCLAccounts.getAccount().getIdToken()) - .header("Authorization-Provider", HMCLAccounts.getAccount().getProvider()) - .getString(); - }); - } - } - - private static class ViewFeedbackDialog extends JFXDialogLayout { - - public ViewFeedbackDialog(FeedbackResponse feedback) { - BorderPane heading = new BorderPane(); - TwoLineListItem left = new TwoLineListItem(); - heading.setLeft(left); - left.setTitle(feedback.getTitle()); - left.setSubtitle(feedback.getAuthor()); - left.getTags().add("#" + feedback.getId()); - left.getTags().add(i18n("feedback.state." + feedback.getState().name().toLowerCase(Locale.US))); - left.getTags().add(feedback.getLauncherVersion()); - left.getTags().add(i18n("feedback.type." + feedback.getType().name().toLowerCase())); - - setHeading(heading); - - Label content = new Label(feedback.getContent()); - content.setWrapText(true); - - TwoLineListItem response = new TwoLineListItem(); - response.getStyleClass().setAll("two-line-item-second-large"); - response.setTitle(i18n("feedback.response")); - response.setSubtitle(StringUtils.isBlank(feedback.getReason()) - ? i18n("feedback.response.empty") - : feedback.getReason()); - - VBox body = new VBox(content, response); - body.setSpacing(8); - setBody(body); - - JFXButton okButton = new JFXButton(); - okButton.setText(i18n("button.ok")); - okButton.setOnAction(e -> fireEvent(new DialogCloseEvent())); - - setActions(okButton); - } - } - - private static class FeedbackResponse { - private final int id; - private final String title; - private final String content; - private final String author; - @SerializedName("launcher_version") - private final String launcherVersion; - private final FeedbackType type; - private final FeedbackState state; - private final String reason; - - public FeedbackResponse(int id, String title, String content, String author, String launcherVersion, FeedbackType type, FeedbackState state, String reason) { - this.id = id; - this.title = title; - this.content = content; - this.author = author; - this.launcherVersion = launcherVersion; - this.type = type; - this.state = state; - this.reason = reason; - } - - public int getId() { - return id; - } - - public String getTitle() { - return title; - } - - public String getContent() { - return content; - } - - public String getAuthor() { - return author; - } - - public String getLauncherVersion() { - return launcherVersion; - } - - public FeedbackType getType() { - return type; - } - - public FeedbackState getState() { - return state; - } - - public String getReason() { - return reason; - } - } - - private enum FeedbackType { - FEATURE, - BUG - } - - private enum FeedbackState { - OPEN, - REJECTED, - ACCEPTED - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java index d6c11595..d7586282 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/LauncherSettingsPage.java @@ -44,8 +44,6 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor private final TabHeader.Tab downloadTab = new TabHeader.Tab<>("downloadSettingsPage"); private final TabHeader.Tab helpTab = new TabHeader.Tab<>("helpPage"); private final TabHeader.Tab aboutTab = new TabHeader.Tab<>("aboutPage"); - private final TabHeader.Tab feedbackTab = new TabHeader.Tab<>("feedbackPage"); - private final TabHeader.Tab sponsorTab = new TabHeader.Tab<>("sponsorPage"); private final TransitionPane transitionPane = new TransitionPane(); public LauncherSettingsPage() { @@ -54,10 +52,8 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor personalizationTab.setNodeSupplier(PersonalizationPage::new); downloadTab.setNodeSupplier(DownloadSettingsPage::new); helpTab.setNodeSupplier(HelpPage::new); - feedbackTab.setNodeSupplier(FeedbackPage::new); - sponsorTab.setNodeSupplier(SponsorPage::new); aboutTab.setNodeSupplier(AboutPage::new); - tab = new TabHeader(gameTab, settingsTab, personalizationTab, downloadTab, helpTab, feedbackTab, sponsorTab, aboutTab); + tab = new TabHeader(gameTab, settingsTab, personalizationTab, downloadTab, helpTab, aboutTab); tab.select(gameTab); gameTab.initializeIfNeeded(); @@ -100,18 +96,6 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor helpItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(helpTab)); helpItem.setOnAction(e -> tab.select(helpTab)); }) - .addNavigationDrawerItem(feedbackItem -> { - feedbackItem.setTitle(i18n("feedback")); - feedbackItem.setLeftGraphic(wrap(SVG::messageAlertOutline)); - feedbackItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(feedbackTab)); - feedbackItem.setOnAction(e -> tab.select(feedbackTab)); - }) - .addNavigationDrawerItem(sponsorItem -> { - sponsorItem.setTitle(i18n("sponsor")); - sponsorItem.setLeftGraphic(wrap(SVG::handHearOutline)); - sponsorItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(sponsorTab)); - sponsorItem.setOnAction(e -> tab.select(sponsorTab)); - }) .addNavigationDrawerItem(aboutItem -> { aboutItem.setTitle(i18n("about")); aboutItem.setLeftGraphic(wrap(SVG::informationOutline)); @@ -140,10 +124,6 @@ public class LauncherSettingsPage extends DecoratorAnimatedPage implements Decor tab.select(gameTab); } - public void showFeedback() { - tab.select(feedbackTab); - } - @Override public ReadOnlyObjectProperty 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 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; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.versions.GameItem; import org.jackhuang.hmcl.ui.versions.Versions; -import org.jackhuang.hmcl.upgrade.RemoteVersion; -import org.jackhuang.hmcl.upgrade.UpdateChecker; -import org.jackhuang.hmcl.upgrade.UpdateHandler; import org.jackhuang.hmcl.util.javafx.BindingMapping; import org.jackhuang.hmcl.util.javafx.MappedObservableList; 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"); - private final BooleanProperty showUpdate = new SimpleBooleanProperty(this, "showUpdate"); - private final ObjectProperty latestVersion = new SimpleObjectProperty<>(this, "latestVersion"); private final ObservableList versions = FXCollections.observableArrayList(); private final ObservableList versionNodes; private Profile profile; private final VBox announcementPane; - private final StackPane updatePane; private final JFXButton menuButton; { @@ -113,44 +107,6 @@ public final class MainPage extends StackPane implements DecoratorPage { announcementPane = new VBox(16); - updatePane = new StackPane(); - updatePane.setVisible(false); - updatePane.getStyleClass().add("bubble"); - FXUtils.setLimitWidth(updatePane, 230); - FXUtils.setLimitHeight(updatePane, 55); - StackPane.setAlignment(updatePane, Pos.TOP_RIGHT); - updatePane.setOnMouseClicked(e -> onUpgrade()); - FXUtils.onChange(showUpdateProperty(), this::showUpdate); - - { - 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)); - { - Label lblIcon = new Label(); - lblIcon.setGraphic(SVG.update(Theme.whiteFillBinding(), 20, 20)); - - 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.getVersion()))); - - hBox.getChildren().setAll(lblIcon, prompt); - } - - JFXButton closeUpdateButton = new JFXButton(); - closeUpdateButton.setGraphic(SVG.close(Theme.whiteFillBinding(), 10, 10)); - StackPane.setAlignment(closeUpdateButton, Pos.TOP_RIGHT); - closeUpdateButton.getStyleClass().add("toggle-icon-tiny"); - StackPane.setMargin(closeUpdateButton, new Insets(5)); - closeUpdateButton.setOnMouseClicked(e -> closeUpdateBubble()); - - updatePane.getChildren().setAll(hBox, closeUpdateButton); - } - StackPane launchPane = new StackPane(); launchPane.getStyleClass().add("launch-pane"); launchPane.setMaxWidth(230); @@ -220,7 +176,7 @@ public final class MainPage extends StackPane implements DecoratorPage { launchPane.getChildren().setAll(launchButton, separator, menuButton); } - getChildren().setAll(announcementPane, updatePane, launchPane); + getChildren().setAll(announcementPane, launchPane); menu.setMaxHeight(365); menu.setMaxWidth(545); @@ -234,40 +190,6 @@ public final class MainPage extends StackPane implements DecoratorPage { Bindings.bindContent(menu.getContent(), versionNodes); } - public MainPage() { - if (Metadata.isNightly()) { - announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.nightly.title"), i18n("update.channel.nightly.hint"))); - } else if (Metadata.isDev()) { - announcementPane.getChildren().add(new AnnouncementCard(i18n("update.channel.dev.title"), i18n("update.channel.dev.hint"))); - } - } - - private void showUpdate(boolean show) { - doAnimation(show); - - if (show && getLatestVersion() != null && !Objects.equals(config().getPromptedVersion(), getLatestVersion().getVersion())) { - Controllers.dialog("", i18n("update.bubble.title", getLatestVersion().getVersion()), MessageDialogPane.MessageType.INFO, () -> { - config().setPromptedVersion(getLatestVersion().getVersion()); - onUpgrade(); - }); - } - } - - private void doAnimation(boolean show) { - Duration duration = Duration.millis(320); - Timeline nowAnimation = new Timeline(); - nowAnimation.getKeyFrames().addAll( - new KeyFrame(Duration.ZERO, - new KeyValue(updatePane.translateXProperty(), show ? 260 : 0, SINE)), - new KeyFrame(duration, - new KeyValue(updatePane.translateXProperty(), show ? 0 : 260, SINE))); - if (show) nowAnimation.getKeyFrames().add( - new KeyFrame(Duration.ZERO, e -> updatePane.setVisible(true))); - else nowAnimation.getKeyFrames().add( - new KeyFrame(duration, e -> updatePane.setVisible(false))); - nowAnimation.play(); - } - private void launch() { Versions.launch(Profiles.getSelectedProfile()); } @@ -276,19 +198,6 @@ public final class MainPage extends StackPane implements DecoratorPage { popup.show(menuButton, JFXPopup.PopupVPosition.BOTTOM, JFXPopup.PopupHPosition.RIGHT, 0, -menuButton.getHeight()); } - private void onUpgrade() { - RemoteVersion target = UpdateChecker.getLatestVersion(); - if (target == null) { - return; - } - UpdateHandler.updateFrom(target); - } - - private void closeUpdateBubble() { - showUpdate.unbind(); - showUpdate.set(false); - } - @Override public ReadOnlyObjectWrapper stateProperty() { return state; @@ -306,30 +215,6 @@ public final class MainPage extends StackPane implements DecoratorPage { this.currentGame.set(currentGame); } - public boolean isShowUpdate() { - return showUpdate.get(); - } - - public BooleanProperty showUpdateProperty() { - return showUpdate; - } - - public void setShowUpdate(boolean showUpdate) { - this.showUpdate.set(showUpdate); - } - - public RemoteVersion getLatestVersion() { - return latestVersion.get(); - } - - public ObjectProperty latestVersionProperty() { - return latestVersion; - } - - public void setLatestVersion(RemoteVersion latestVersion) { - this.latestVersion.set(latestVersion); - } - public void initVersions(Profile profile, List versions) { 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 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; import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider; import org.jackhuang.hmcl.ui.versions.GameAdvancedListItem; import org.jackhuang.hmcl.ui.versions.Versions; -import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.versioning.VersionNumber; @@ -92,8 +91,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage { }); FXUtils.onChangeAndOperate(Profiles.selectedVersionProperty(), mainPage::setCurrentGame); - mainPage.showUpdateProperty().bind(UpdateChecker.outdatedProperty()); - mainPage.latestVersionProperty().bind(UpdateChecker.latestVersionProperty()); Profiles.registerVersionsListener(profile -> { HMCLGameRepository repository = profile.getRepository(); @@ -150,19 +147,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage { downloadItem.setTitle(i18n("download")); downloadItem.setOnAction(e -> Controllers.navigate(Controllers.getDownloadPage())); - // fifth item in left sidebar - AdvancedListItem multiplayerItem = new AdvancedListItem(); - multiplayerItem.setLeftGraphic(wrap(SVG::lan)); - multiplayerItem.setActionButtonVisible(false); - multiplayerItem.setTitle(i18n("multiplayer")); - 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)); @@ -179,7 +163,6 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage { .add(gameItem) .add(downloadItem) .startCategory(i18n("settings.launcher.general").toUpperCase()) - .add(multiplayerItem) .add(launcherSettingsItem); // 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 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; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; 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.util.Logging; import org.jackhuang.hmcl.util.i18n.Locales; import org.jackhuang.hmcl.util.io.FileUtils; @@ -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())); - - // ==== Update ==== - FXUtils.installFastTooltip(btnUpdate, i18n("update.tooltip")); - updateListener = any -> { - btnUpdate.setVisible(UpdateChecker.isOutdated()); - - if (UpdateChecker.isOutdated()) { - lblUpdateSub.setText(i18n("update.newest_version", UpdateChecker.getLatestVersion().getVersion())); - lblUpdateSub.getStyleClass().setAll("update-label"); - - lblUpdate.setText(i18n("update.found")); - lblUpdate.getStyleClass().setAll("update-label"); - } else if (UpdateChecker.isCheckingUpdate()) { - lblUpdateSub.setText(i18n("update.checking")); - lblUpdateSub.getStyleClass().setAll("subtitle-label"); - - lblUpdate.setText(i18n("update")); - lblUpdate.getStyleClass().setAll(); - } else { - lblUpdateSub.setText(i18n("update.latest")); - lblUpdateSub.getStyleClass().setAll("subtitle-label"); - - lblUpdate.setText(i18n("update")); - lblUpdate.getStyleClass().setAll(); - } - }; - UpdateChecker.latestVersionProperty().addListener(new WeakInvalidationListener(updateListener)); - UpdateChecker.outdatedProperty().addListener(new WeakInvalidationListener(updateListener)); - UpdateChecker.checkingUpdateProperty().addListener(new WeakInvalidationListener(updateListener)); - updateListener.invalidated(null); - - ToggleGroup updateChannelGroup = new ToggleGroup(); - chkUpdateDev.setToggleGroup(updateChannelGroup); - chkUpdateDev.setUserData(UpdateChannel.DEVELOPMENT); - chkUpdateStable.setToggleGroup(updateChannelGroup); - chkUpdateStable.setUserData(UpdateChannel.STABLE); - ObjectProperty updateChannel = selectedItemPropertyFor(updateChannelGroup, UpdateChannel.class); - updateChannel.set(UpdateChannel.getChannel()); - updateChannel.addListener((a, b, newValue) -> { - UpdateChecker.requestCheckUpdate(newValue); - }); - // ==== - } - - @Override - protected void onUpdate() { - RemoteVersion target = UpdateChecker.getLatestVersion(); - if (target == null) { - return; - } - UpdateHandler.updateFrom(target); } @Override @@ -142,11 +87,6 @@ public final class SettingsPage extends SettingsView { }); } - @Override - protected void onSponsor() { - FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/sponsor"); - } - @Override protected void clearCacheDirectory() { FileUtils.cleanDirectoryQuietly(new File(Settings.instance().getCommonDirectory(), "cache")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java index af9a3477..44b6dfed 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsView.java @@ -49,11 +49,6 @@ public abstract class SettingsView extends StackPane { protected final JFXComboBox cboLanguage; protected final MultiFileItem fileCommonLocation; protected final ComponentSublist fileCommonLocationSublist; - protected final Label lblUpdate; - protected final Label lblUpdateSub; - protected final JFXRadioButton chkUpdateStable; - protected final JFXRadioButton chkUpdateDev; - protected final JFXButton btnUpdate; protected final ScrollPane scroll; public SettingsView() { @@ -69,11 +64,6 @@ public abstract class SettingsView extends StackPane { ComponentList settingsPane = new ComponentList(); { { - StackPane sponsorPane = new StackPane(); - sponsorPane.setCursor(Cursor.HAND); - sponsorPane.setOnMouseClicked(e -> onSponsor()); - sponsorPane.setPadding(new Insets(8, 0, 8, 0)); - GridPane gridPane = new GridPane(); ColumnConstraints col = new ColumnConstraints(); @@ -96,51 +86,7 @@ public abstract class SettingsView extends StackPane { GridPane.setColumnIndex(label, 0); gridPane.getChildren().add(label); } - - sponsorPane.getChildren().setAll(gridPane); - settingsPane.getContent().add(sponsorPane); - } - } - - { - ComponentSublist updatePane = new ComponentSublist(); - updatePane.setTitle(i18n("update")); - updatePane.setHasSubtitle(true); - { - VBox headerLeft = new VBox(); - - lblUpdate = new Label(i18n("update")); - lblUpdateSub = new Label(); - lblUpdateSub.getStyleClass().add("subtitle-label"); - - headerLeft.getChildren().setAll(lblUpdate, lblUpdateSub); - updatePane.setHeaderLeft(headerLeft); - } - - { - btnUpdate = new JFXButton(); - btnUpdate.setOnMouseClicked(e -> onUpdate()); - btnUpdate.getStyleClass().add("toggle-icon4"); - btnUpdate.setGraphic(SVG.update(Theme.blackFillBinding(), 20, 20)); - - updatePane.setHeaderRight(btnUpdate); } - - { - VBox content = new VBox(); - content.setSpacing(8); - - chkUpdateStable = new JFXRadioButton(i18n("update.channel.stable")); - chkUpdateDev = new JFXRadioButton(i18n("update.channel.dev")); - - TextFlow noteWrapper = new TextFlow(new Text(i18n("update.note"))); - VBox.setMargin(noteWrapper, new Insets(10, 0, 0, 0)); - - content.getChildren().setAll(chkUpdateStable, chkUpdateDev, noteWrapper); - - updatePane.getContent().add(content); - } - settingsPane.getContent().add(updatePane); } { @@ -204,11 +150,7 @@ public abstract class SettingsView extends StackPane { } } - protected abstract void onUpdate(); - protected abstract void onExportLogs(); - protected abstract void onSponsor(); - protected abstract void clearCacheDirectory(); } 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 ff685971..00000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SponsorPage.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2021 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.ui.main; - -import com.google.gson.annotations.SerializedName; -import com.google.gson.reflect.TypeToken; -import com.jfoenix.controls.JFXListCell; -import com.jfoenix.controls.JFXListView; -import javafx.geometry.Insets; -import javafx.geometry.VPos; -import javafx.scene.Cursor; -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; -import org.jackhuang.hmcl.util.io.HttpRequest; - -import java.math.BigDecimal; -import java.util.Date; -import java.util.List; - -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; - -public class SponsorPage extends StackPane { - private final JFXListView listView; - - public SponsorPage() { - VBox content = new VBox(); - content.setPadding(new Insets(10)); - content.setSpacing(10); - content.setFillWidth(true); - - { - StackPane sponsorPane = new StackPane(); - sponsorPane.getStyleClass().add("card"); - sponsorPane.setCursor(Cursor.HAND); - sponsorPane.setOnMouseClicked(e -> onSponsor()); - - GridPane gridPane = new GridPane(); - - ColumnConstraints col = new ColumnConstraints(); - col.setHgrow(Priority.SOMETIMES); - col.setMaxWidth(Double.POSITIVE_INFINITY); - - gridPane.getColumnConstraints().setAll(col); - - RowConstraints row = new RowConstraints(); - row.setMinHeight(Double.NEGATIVE_INFINITY); - row.setValignment(VPos.TOP); - row.setVgrow(Priority.SOMETIMES); - gridPane.getRowConstraints().setAll(row); - - { - Label label = new Label(i18n("sponsor.hmcl")); - label.setWrapText(true); - label.setTextAlignment(TextAlignment.JUSTIFY); - GridPane.setRowIndex(label, 0); - GridPane.setColumnIndex(label, 0); - gridPane.getChildren().add(label); - } - - sponsorPane.getChildren().setAll(gridPane); - content.getChildren().add(sponsorPane); - } - - { - StackPane pane = new StackPane(); - pane.getStyleClass().add("card"); - listView = new JFXListView<>(); - MutableObject lastCell = new MutableObject<>(); - listView.setCellFactory((listView) -> new JFXListCell() { - @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); - } - } - }); - VBox.setVgrow(pane, Priority.ALWAYS); - pane.getChildren().setAll(listView); - content.getChildren().add(pane); - } - - loadSponsorList(); - - getChildren().setAll(content); - } - - private void onSponsor() { - FXUtils.openLink("https://hmcl.huangyuhui.net/api/redirect/sponsor"); - } - - private void loadSponsorList() { - Task.>supplyAsync(() -> HttpRequest.GET("https://hmcl.huangyuhui.net/api/sponsor").getJson(new TypeToken>() { - }.getType())).thenAcceptAsync(Schedulers.javafx(), sponsors -> { - listView.getItems().setAll(sponsors); - }).start(); - } - - private static class Sponsor { - @SerializedName("name") - private final String name; - - @SerializedName("create_time") - private final Date createTime; - - @SerializedName("money") - private final BigDecimal money; - - @SerializedName("contact") - private final String contact; - - @SerializedName("afdian_id") - private final String afdianId; - - public Sponsor() { - this("", new Date(), BigDecimal.ZERO, "", ""); - } - - public Sponsor(String name, Date createTime, BigDecimal money, String contact, String afdianId) { - this.name = name; - this.createTime = createTime; - this.money = money; - this.contact = contact; - this.afdianId = afdianId; - } - - public String getName() { - return name; - } - - public Date getCreateTime() { - return createTime; - } - - public BigDecimal getMoney() { - return money; - } - - public String getContact() { - return contact; - } - - public String getAfdianId() { - return afdianId; - } - } -} 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 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/ExecutableHeaderHelper.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.upgrade; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileChannel.MapMode; -import java.nio.file.Path; -import java.util.Map; -import java.util.Optional; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import org.jackhuang.hmcl.util.io.IOUtils; - -import static java.nio.file.StandardOpenOption.*; -import static org.jackhuang.hmcl.util.Lang.mapOf; -import static org.jackhuang.hmcl.util.Pair.pair; - -/** - * Helper class for adding/removing executable header from HMCL file. - * - * @author yushijinhun - */ -final class ExecutableHeaderHelper { - private ExecutableHeaderHelper() {} - - private static Map suffix2header = mapOf( - pair("exe", "assets/HMCLauncher.exe"), - pair("sh", "assets/HMCLauncher.sh") - ); - - private static Optional getSuffix(Path file) { - String filename = file.getFileName().toString(); - int idxDot = filename.lastIndexOf('.'); - if (idxDot < 0) { - return Optional.empty(); - } else { - return Optional.of(filename.substring(idxDot + 1)); - } - } - - private static Optional readHeader(ZipFile zip, String suffix) throws IOException { - String location = suffix2header.get(suffix); - if (location != null) { - ZipEntry entry = zip.getEntry(location); - if (entry != null && !entry.isDirectory()) { - try (InputStream in = zip.getInputStream(entry)) { - return Optional.of(IOUtils.readFullyAsByteArray(in)); - } - } - } - return Optional.empty(); - } - - private static int detectHeaderLength(ZipFile zip, FileChannel channel) throws IOException { - ByteBuffer buf = channel.map(MapMode.READ_ONLY, 0, channel.size()); - suffixLoop: for (String suffix : suffix2header.keySet()) { - Optional header = readHeader(zip, suffix); - if (header.isPresent()) { - buf.rewind(); - for (byte b : header.get()) { - if (!buf.hasRemaining() || b != buf.get()) { - continue suffixLoop; - } - } - return header.get().length; - } - } - return 0; - } - - /** - * Copies the executable and removes its header. - */ - public static void copyWithoutHeader(Path from, Path to) throws IOException { - try ( - FileChannel in = FileChannel.open(from, READ); - FileChannel out = FileChannel.open(to, CREATE, WRITE, TRUNCATE_EXISTING); - ZipFile zip = new ZipFile(from.toFile()) - ) { - in.transferTo(detectHeaderLength(zip, in), Long.MAX_VALUE, out); - } - } - - /** - * Copies the executable and appends the header according to the suffix. - */ - public static void copyWithHeader(Path from, Path to) throws IOException { - try ( - FileChannel in = FileChannel.open(from, READ); - FileChannel out = FileChannel.open(to, CREATE, WRITE, TRUNCATE_EXISTING); - ZipFile zip = new ZipFile(from.toFile()) - ) { - Optional suffix = getSuffix(to); - if (suffix.isPresent()) { - Optional header = readHeader(zip, suffix.get()); - if (header.isPresent()) { - out.write(ByteBuffer.wrap(header.get())); - } - } - - in.transferTo(detectHeaderLength(zip, in), Long.MAX_VALUE, out); - } - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java deleted file mode 100644 index 8b6fdc06..00000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/HMCLDownloadTask.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.upgrade; - -import org.jackhuang.hmcl.task.FileDownloadTask; -import org.jackhuang.hmcl.util.Pack200Utils; -import org.jackhuang.hmcl.util.io.NetworkUtils; -import org.tukaani.xz.XZInputStream; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.jar.JarOutputStream; - -class HMCLDownloadTask extends FileDownloadTask { - - private RemoteVersion.Type archiveFormat; - - public HMCLDownloadTask(RemoteVersion version, Path target) { - super(NetworkUtils.toURL(version.getUrl()), target.toFile(), version.getIntegrityCheck()); - archiveFormat = version.getType(); - } - - @Override - public void execute() throws Exception { - super.execute(); - - try { - Path target = getFile().toPath(); - - switch (archiveFormat) { - case JAR: - break; - - case PACK_XZ: - byte[] raw = Files.readAllBytes(target); - try (InputStream in = new XZInputStream(new ByteArrayInputStream(raw)); - JarOutputStream out = new JarOutputStream(Files.newOutputStream(target))) { - Pack200Utils.unpack(in, out); - } - break; - - default: - throw new IllegalArgumentException("Unknown format: " + archiveFormat); - } - } catch (Throwable e) { - getFile().delete(); - throw e; - } - } - -} 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 8c387047..00000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/IntegrityChecker.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.upgrade; - -import org.jackhuang.hmcl.util.DigestUtils; -import org.jackhuang.hmcl.util.io.IOUtils; -import org.jackhuang.hmcl.util.io.JarUtils; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Path; -import java.security.*; -import java.security.spec.X509EncodedKeySpec; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeMap; -import java.util.logging.Level; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.jackhuang.hmcl.util.Logging.LOG; - -/** - * A class that checks the integrity of HMCL. - * - * @author yushijinhun - */ -public final class IntegrityChecker { - private IntegrityChecker() {} - - private static final String SIGNATURE_FILE = "META-INF/hmcl_signature"; - private static final String PUBLIC_KEY_FILE = "assets/hmcl_signature_publickey.der"; - - private static PublicKey getPublicKey() throws IOException { - try (InputStream in = IntegrityChecker.class.getResourceAsStream("/" + PUBLIC_KEY_FILE)) { - if (in == null) { - throw new IOException("Public key not found"); - } - return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(IOUtils.readFullyAsByteArray(in))); - } catch (GeneralSecurityException e) { - throw new IOException("Failed to load public key", e); - } - } - - private static boolean verifyJar(Path jarPath) throws IOException { - PublicKey publickey = getPublicKey(); - MessageDigest md = DigestUtils.getDigest("SHA-512"); - - byte[] signature = null; - Map fileFingerprints = new TreeMap<>(); - try (ZipFile zip = new ZipFile(jarPath.toFile())) { - for (ZipEntry entry : zip.stream().toArray(ZipEntry[]::new)) { - String filename = entry.getName(); - try (InputStream in = zip.getInputStream(entry)) { - if (in == null) { - throw new IOException("entry is null"); - } - - if (SIGNATURE_FILE.equals(filename)) { - signature = IOUtils.readFullyAsByteArray(in); - } else { - md.reset(); - fileFingerprints.put(filename, DigestUtils.digest(md, in)); - } - } - } - } - - if (signature == null) { - throw new IOException("Signature is missing"); - } - - try { - Signature verifier = Signature.getInstance("SHA512withRSA"); - verifier.initVerify(publickey); - for (Entry entry : fileFingerprints.entrySet()) { - md.reset(); - verifier.update(md.digest(entry.getKey().getBytes(UTF_8))); - verifier.update(entry.getValue()); - } - return verifier.verify(signature); - } catch (GeneralSecurityException e) { - throw new IOException("Failed to verify signature", e); - } - } - - static void requireVerifiedJar(Path jar) throws IOException { - if (!verifyJar(jar)) { - throw new IOException("Invalid signature: " + jar); - } - } - - private static Boolean selfVerified = null; - - /** - * Checks whether the current application is verified. - * This method is blocking. - */ - public static synchronized boolean isSelfVerified() { - if (selfVerified != null) { - return selfVerified; - } - try { - verifySelf(); - LOG.info("Successfully verified current JAR"); - selfVerified = true; - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to verify myself, is the JAR corrupt?", e); - selfVerified = false; - } - return selfVerified; - } - - private static void verifySelf() throws IOException { - Path self = JarUtils.thisJar().orElseThrow(() -> new IOException("Failed to find current HMCL location")); - requireVerifiedJar(self); - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java deleted file mode 100644 index c3ac2caa..00000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.upgrade; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; -import org.jackhuang.hmcl.util.Pack200Utils; -import org.jackhuang.hmcl.util.gson.JsonUtils; -import org.jackhuang.hmcl.util.io.NetworkUtils; - -import java.io.IOException; -import java.util.Optional; - -public class RemoteVersion { - - public static RemoteVersion fetch(UpdateChannel channel, String url) throws IOException { - try { - JsonObject response = JsonUtils.fromNonNullJson(NetworkUtils.doGet(NetworkUtils.toURL(url)), JsonObject.class); - String version = Optional.ofNullable(response.get("version")).map(JsonElement::getAsString).orElseThrow(() -> new IOException("version is missing")); - String jarUrl = Optional.ofNullable(response.get("jar")).map(JsonElement::getAsString).orElse(null); - String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null); - String packXZUrl = Optional.ofNullable(response.get("packxz")).map(JsonElement::getAsString).orElse(null); - String packXZHash = Optional.ofNullable(response.get("packxzsha1")).map(JsonElement::getAsString).orElse(null); - if (Pack200Utils.isSupported() && packXZUrl != null && packXZHash != null) { - return new RemoteVersion(channel, version, packXZUrl, Type.PACK_XZ, new IntegrityCheck("SHA-1", packXZHash)); - } else if (jarUrl != null && jarHash != null) { - return new RemoteVersion(channel, version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash)); - } else { - throw new IOException("No download url is available"); - } - } catch (JsonParseException e) { - throw new IOException("Malformed response", e); - } - } - - private final UpdateChannel channel; - private final String version; - private final String url; - private final Type type; - private final IntegrityCheck integrityCheck; - - public RemoteVersion(UpdateChannel channel, String version, String url, Type type, IntegrityCheck integrityCheck) { - this.channel = channel; - this.version = version; - this.url = url; - this.type = type; - this.integrityCheck = integrityCheck; - } - - public UpdateChannel getChannel() { - return channel; - } - - public String getVersion() { - return version; - } - - public String getUrl() { - return url; - } - - public Type getType() { - return type; - } - - public IntegrityCheck getIntegrityCheck() { - return integrityCheck; - } - - @Override - public String toString() { - return "[" + version + " from " + url + "]"; - } - - public enum Type { - PACK_XZ, - JAR - } -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java deleted file mode 100644 index 998a3da7..00000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.upgrade; - -import org.jackhuang.hmcl.Metadata; - -public enum UpdateChannel { - STABLE("stable"), - DEVELOPMENT("dev"), - NIGHTLY("nightly"); - - public final String channelName; - - UpdateChannel(String channelName) { - this.channelName = channelName; - } - - public static UpdateChannel getChannel() { - if (Metadata.isDev()) { - return DEVELOPMENT; - } else if (Metadata.isNightly()) { - return NIGHTLY; - } else { - return STABLE; - } - } -} 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 cc7ce8f2..00000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.upgrade; - -import javafx.application.Platform; -import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.property.*; -import javafx.beans.value.ObservableBooleanValue; -import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.util.io.NetworkUtils; - -import java.io.IOException; -import java.util.logging.Level; - -import static org.jackhuang.hmcl.util.Lang.mapOf; -import static org.jackhuang.hmcl.util.Lang.thread; -import static org.jackhuang.hmcl.util.Logging.LOG; -import static org.jackhuang.hmcl.util.Pair.pair; -import static org.jackhuang.hmcl.util.versioning.VersionNumber.asVersion; - -public final class UpdateChecker { - private UpdateChecker() {} - - private static ObjectProperty latestVersion = new SimpleObjectProperty<>(); - private static BooleanBinding outdated = Bindings.createBooleanBinding( - () -> { - RemoteVersion latest = latestVersion.get(); - if (latest == null || isDevelopmentVersion(Metadata.VERSION)) { - return false; - } else { - // We can update from development version to stable version, - // which can be downgrading. - return asVersion(latest.getVersion()).compareTo(asVersion(Metadata.VERSION)) != 0; - } - }, - latestVersion); - private static ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false); - - public static void init() { - requestCheckUpdate(UpdateChannel.getChannel()); - } - - public static RemoteVersion getLatestVersion() { - return latestVersion.get(); - } - - public static ReadOnlyObjectProperty latestVersionProperty() { - return latestVersion; - } - - public static boolean isOutdated() { - return outdated.get(); - } - - public static ObservableBooleanValue outdatedProperty() { - return outdated; - } - - public static boolean isCheckingUpdate() { - return checkingUpdate.get(); - } - - public static ReadOnlyBooleanProperty checkingUpdateProperty() { - return checkingUpdate.getReadOnlyProperty(); - } - - private static RemoteVersion checkUpdate(UpdateChannel channel) throws IOException { - if (!IntegrityChecker.isSelfVerified() && !"true".equals(System.getProperty("hmcl.self_integrity_check.disable"))) { - throw new IOException("Self verification failed"); - } - - String url = NetworkUtils.withQuery(Metadata.UPDATE_URL, mapOf( - pair("version", Metadata.VERSION), - pair("channel", channel.channelName))); - - return RemoteVersion.fetch(channel, url); - } - - private static boolean isDevelopmentVersion(String version) { - return version.contains("@") || // eg. @develop@ - version.contains("SNAPSHOT"); // eg. 3.5.SNAPSHOT - } - - public static void requestCheckUpdate(UpdateChannel channel) { - Platform.runLater(() -> { - if (isCheckingUpdate()) - return; - checkingUpdate.set(true); - - thread(() -> { - RemoteVersion result = null; - try { - result = checkUpdate(channel); - LOG.info("Latest version (" + channel + ") is " + result); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to check for update", e); - } - - RemoteVersion finalResult = result; - Platform.runLater(() -> { - checkingUpdate.set(false); - if (finalResult != null) { - latestVersion.set(finalResult); - } - }); - }, "Update Checker", true); - }); - } -} 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 4cd06ef8..00000000 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.upgrade; - -import com.google.gson.Gson; -import com.google.gson.JsonParseException; -import javafx.application.Platform; - -import org.jackhuang.hmcl.Main; -import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.task.TaskExecutor; -import org.jackhuang.hmcl.ui.Controllers; -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 java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.*; -import java.util.concurrent.CancellationException; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static org.jackhuang.hmcl.ui.FXUtils.checkFxUserThread; -import static org.jackhuang.hmcl.util.Lang.thread; -import static org.jackhuang.hmcl.util.Logging.LOG; -import static org.jackhuang.hmcl.util.i18n.I18n.i18n; - -public final class UpdateHandler { - private UpdateHandler() {} - - /** - * @return whether to exit - */ - public static boolean processArguments(String[] args) { - breakForceUpdateFeature(); - - if (isNestedApplication()) { - // updated from old versions - try { - performMigration(); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to perform migration", e); - SwingUtils.showErrorDialog(i18n("fatal.apply_update_failure", Metadata.PUBLISH_URL) + "\n" + StringUtils.getStackTrace(e)); - } - return true; - } - - if (args.length == 2 && args[0].equals("--apply-to")) { - try { - applyUpdate(Paths.get(args[1])); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to apply update", e); - SwingUtils.showErrorDialog(i18n("fatal.apply_update_failure", Metadata.PUBLISH_URL) + "\n" + StringUtils.getStackTrace(e)); - } - return true; - } - - if (isFirstLaunchAfterUpgrade()) { - SwingUtils.showInfoDialog(i18n("fatal.migration_requires_manual_reboot")); - return true; - } - - return false; - } - - public static void updateFrom(RemoteVersion version) { - checkFxUserThread(); - - Controllers.dialog(new UpgradeDialog(version, () -> { - Path downloaded; - try { - downloaded = Files.createTempFile("hmcl-update-", ".jar"); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to create temp file", e); - return; - } - - Task task = new HMCLDownloadTask(version, downloaded); - - TaskExecutor executor = task.executor(false); - Controllers.taskDialog(executor, i18n("message.downloading"), TaskCancellationAction.NORMAL); - executor.start(); - thread(() -> { - boolean success = executor.test(); - - if (success) { - try { - if (!IntegrityChecker.isSelfVerified()) { - throw new IOException("Current JAR is not verified"); - } - - requestUpdate(downloaded, getCurrentLocation()); - System.exit(0); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to update to " + version, e); - Platform.runLater(() -> Controllers.dialog(StringUtils.getStackTrace(e), i18n("update.failed"), MessageType.ERROR)); - } - - } else { - Exception e = executor.getException(); - LOG.log(Level.WARNING, "Failed to update to " + version, e); - if (e instanceof CancellationException) { - Platform.runLater(() -> Controllers.showToast(i18n("message.cancelled"))); - } else { - Platform.runLater(() -> Controllers.dialog(e.toString(), i18n("update.failed"), MessageType.ERROR)); - } - } - }); - })); - } - - private static void applyUpdate(Path target) throws IOException { - LOG.info("Applying update to " + target); - - Path self = getCurrentLocation(); - IntegrityChecker.requireVerifiedJar(self); - ExecutableHeaderHelper.copyWithHeader(self, target); - - Optional newFilename = tryRename(target, Metadata.VERSION); - if (newFilename.isPresent()) { - LOG.info("Move " + target + " to " + newFilename.get()); - try { - Files.move(target, newFilename.get()); - target = newFilename.get(); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to move target", e); - } - } - - startJava(target); - } - - private static void requestUpdate(Path updateTo, Path self) throws IOException { - IntegrityChecker.requireVerifiedJar(updateTo); - startJava(updateTo, "--apply-to", self.toString()); - } - - private static void startJava(Path jar, String... appArgs) throws IOException { - List commandline = new ArrayList<>(); - commandline.add(JavaVersion.fromCurrentEnvironment().getBinary().toString()); - commandline.add("-jar"); - commandline.add(jar.toAbsolutePath().toString()); - commandline.addAll(Arrays.asList(appArgs)); - LOG.info("Starting process: " + commandline); - new ProcessBuilder(commandline) - .directory(Paths.get("").toAbsolutePath().toFile()) - .inheritIO() - .start(); - } - - private static Optional tryRename(Path path, String newVersion) { - String filename = path.getFileName().toString(); - Matcher matcher = Pattern.compile("^(?[hH][mM][cC][lL][.-])(?\\d+(?:\\.\\d+)*)(?\\.[^.]+)$").matcher(filename); - if (matcher.find()) { - String newFilename = matcher.group("prefix") + newVersion + matcher.group("suffix"); - if (!newFilename.equals(filename)) { - return Optional.of(path.resolveSibling(newFilename)); - } - } - return Optional.empty(); - } - - private static Path getCurrentLocation() throws IOException { - return JarUtils.thisJar().orElseThrow(() -> new IOException("Failed to find current HMCL location")); - } - - // ==== support for old versions === - private static void performMigration() throws IOException { - LOG.info("Migrating from old versions"); - - Path location = getParentApplicationLocation() - .orElseThrow(() -> new IOException("Failed to get parent application location")); - - requestUpdate(getCurrentLocation(), location); - } - - /** - * This method must be called from the main thread. - */ - private static boolean isNestedApplication() { - StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); - for (int i = 0; i < stacktrace.length; i++) { - StackTraceElement element = stacktrace[i]; - if (Main.class.getName().equals(element.getClassName()) && "main".equals(element.getMethodName())) { - // we've reached the main method - return i + 1 != stacktrace.length; - } - } - return false; - } - - private static Optional getParentApplicationLocation() { - String command = System.getProperty("sun.java.command"); - if (command != null) { - Path path = Paths.get(command); - if (Files.isRegularFile(path)) { - return Optional.of(path.toAbsolutePath()); - } - } - return Optional.empty(); - } - - private static boolean isFirstLaunchAfterUpgrade() { - Optional currentPath = JarUtils.thisJar(); - if (currentPath.isPresent()) { - Path updated = Metadata.HMCL_DIRECTORY.resolve("HMCL-" + Metadata.VERSION + ".jar"); - if (currentPath.get().toAbsolutePath().equals(updated.toAbsolutePath())) { - return true; - } - } - return false; - } - - private static void breakForceUpdateFeature() { - Path hmclVersionJson = Metadata.HMCL_DIRECTORY.resolve("hmclver.json"); - if (Files.isRegularFile(hmclVersionJson)) { - try { - Map content = new Gson().fromJson(FileUtils.readText(hmclVersionJson), Map.class); - Object ver = content.get("ver"); - if (ver instanceof String && ((String) ver).startsWith("3.")) { - Files.delete(hmclVersionJson); - LOG.info("Successfully broke the force update feature"); - } - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to break the force update feature", e); - } catch (JsonParseException e) { - hmclVersionJson.toFile().delete(); - } - } - } - // ==== -} diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/CrashReporter.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/CrashReporter.java index 252265be..53b93d43 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/CrashReporter.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/CrashReporter.java @@ -23,8 +23,6 @@ import javafx.scene.control.Alert.AlertType; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.countly.CrashReport; import org.jackhuang.hmcl.ui.CrashWindow; -import org.jackhuang.hmcl.upgrade.IntegrityChecker; -import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.io.NetworkUtils; import java.io.IOException; @@ -106,9 +104,6 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler { if (showCrashWindow) { new CrashWindow(text).show(); } - if (!UpdateChecker.isOutdated() && IntegrityChecker.isSelfVerified()) { - reportToServer(report); - } } }); } catch (Throwable handlingException) { -- 2.38.1