From 3f9d16ab8ce0f85cd2a2f3babc3f41962ad20b59 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Tue, 21 Sep 2021 18:41:50 +0800 Subject: [PATCH] feat: eula. --- .../org/jackhuang/hmcl/setting/Config.java | 2 +- .../jackhuang/hmcl/setting/ConfigHolder.java | 59 +++++++++- .../jackhuang/hmcl/setting/GlobalConfig.java | 107 ++++++++++++++++++ .../org/jackhuang/hmcl/ui/Controllers.java | 30 ++++- .../resources/assets/lang/I18N.properties | 4 + .../resources/assets/lang/I18N_zh.properties | 4 + .../assets/lang/I18N_zh_CN.properties | 4 + 7 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/setting/GlobalConfig.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java index f2fa5c081..ba8115edc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher - * Copyright (C) 2020 huangyuhui and contributors + * 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 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 47459dbf1..880cb1276 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ConfigHolder.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.setting; import com.google.gson.Gson; import com.google.gson.JsonParseException; +import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.util.InvocationDispatcher; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.io.FileUtils; @@ -40,9 +41,11 @@ public final class ConfigHolder { public static final String CONFIG_FILENAME = "hmcl.json"; public static final String CONFIG_FILENAME_LINUX = ".hmcl.json"; + public static final Path GLOBAL_CONFIG_PATH = Metadata.HMCL_DIRECTORY.resolve("config.json"); private static Path configLocation; private static Config configInstance; + private static GlobalConfig globalConfigInstance; private static boolean newlyCreated; public static Config config() { @@ -52,6 +55,13 @@ public final class ConfigHolder { return configInstance; } + public static GlobalConfig globalConfig() { + if (globalConfigInstance == null) { + throw new IllegalStateException("Configuration hasn't been loaded"); + } + return globalConfigInstance; + } + public static boolean isNewlyCreated() { return newlyCreated; } @@ -68,6 +78,9 @@ public final class ConfigHolder { configInstance = loadConfig(); configInstance.addListener(source -> markConfigDirty()); + globalConfigInstance = loadGlobalConfig(); + globalConfigInstance.addListener(source -> markGlobalConfigDirty()); + Settings.init(); if (newlyCreated) { @@ -145,7 +158,7 @@ public final class ConfigHolder { return new Config(); } - private static InvocationDispatcher configWriter = InvocationDispatcher.runOn(Lang::thread, content -> { + private static final InvocationDispatcher configWriter = InvocationDispatcher.runOn(Lang::thread, content -> { try { writeToConfig(content); } catch (IOException e) { @@ -167,4 +180,48 @@ public final class ConfigHolder { private static void saveConfigSync() throws IOException { writeToConfig(configInstance.toJson()); } + + // Global Config + + private static GlobalConfig loadGlobalConfig() throws IOException { + if (Files.exists(GLOBAL_CONFIG_PATH)) { + try { + String content = FileUtils.readText(GLOBAL_CONFIG_PATH); + GlobalConfig deserialized = GlobalConfig.fromJson(content); + if (deserialized == null) { + LOG.info("Config is empty"); + } else { + return deserialized; + } + } catch (JsonParseException e) { + LOG.log(Level.WARNING, "Malformed config.", e); + } + } + + LOG.info("Creating an empty global config"); + return new GlobalConfig(); + } + + private static final InvocationDispatcher globalConfigWriter = InvocationDispatcher.runOn(Lang::thread, content -> { + try { + writeToGlobalConfig(content); + } catch (IOException e) { + LOG.log(Level.SEVERE, "Failed to save config", e); + } + }); + + private static void writeToGlobalConfig(String content) throws IOException { + LOG.info("Saving global config"); + synchronized (GLOBAL_CONFIG_PATH) { + Files.write(GLOBAL_CONFIG_PATH, content.getBytes(UTF_8)); + } + } + + static void markGlobalConfigDirty() { + globalConfigWriter.accept(globalConfigInstance.toJson()); + } + + private static void saveGlobalConfigSync() throws IOException { + writeToConfig(globalConfigInstance.toJson()); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GlobalConfig.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GlobalConfig.java new file mode 100644 index 000000000..e3a099aa7 --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GlobalConfig.java @@ -0,0 +1,107 @@ +/* + * 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.setting; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.collections.ObservableList; +import javafx.collections.ObservableMap; +import javafx.collections.ObservableSet; +import org.hildan.fxgson.creators.ObservableListCreator; +import org.hildan.fxgson.creators.ObservableMapCreator; +import org.hildan.fxgson.creators.ObservableSetCreator; +import org.hildan.fxgson.factories.JavaFxPropertyTypeAdapterFactory; +import org.jackhuang.hmcl.util.gson.EnumOrdinalDeserializer; +import org.jackhuang.hmcl.util.gson.FileTypeAdapter; +import org.jackhuang.hmcl.util.javafx.ObservableHelper; +import org.jackhuang.hmcl.util.javafx.PropertyUtils; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.net.Proxy; + +public class GlobalConfig implements Cloneable, Observable { + + private static final Gson CONFIG_GSON = new GsonBuilder() + .registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE) + .registerTypeAdapter(ObservableList.class, new ObservableListCreator()) + .registerTypeAdapter(ObservableSet.class, new ObservableSetCreator()) + .registerTypeAdapter(ObservableMap.class, new ObservableMapCreator()) + .registerTypeAdapterFactory(new JavaFxPropertyTypeAdapterFactory(true, true)) + .registerTypeAdapter(EnumBackgroundImage.class, new EnumOrdinalDeserializer<>(EnumBackgroundImage.class)) // backward compatibility for backgroundType + .registerTypeAdapter(Proxy.Type.class, new EnumOrdinalDeserializer<>(Proxy.Type.class)) // backward compatibility for hasProxy + .setPrettyPrinting() + .create(); + + @Nullable + public static GlobalConfig fromJson(String json) throws JsonParseException { + GlobalConfig loaded = CONFIG_GSON.fromJson(json, GlobalConfig.class); + if (loaded == null) { + return null; + } + GlobalConfig instance = new GlobalConfig(); + PropertyUtils.copyProperties(loaded, instance); + return instance; + } + + @SerializedName("agreementVersion") + private IntegerProperty agreementVersion = new SimpleIntegerProperty(); + + private transient ObservableHelper helper = new ObservableHelper(this); + + public GlobalConfig() { + PropertyUtils.attachListener(this, helper); + } + + @Override + public void addListener(InvalidationListener listener) { + helper.addListener(listener); + } + + @Override + public void removeListener(InvalidationListener listener) { + helper.removeListener(listener); + } + + public String toJson() { + return CONFIG_GSON.toJson(this); + } + + @Override + public GlobalConfig clone() { + return fromJson(this.toJson()); + } + + public int getAgreementVersion() { + return agreementVersion.get(); + } + + public IntegerProperty agreementVersionProperty() { + return agreementVersion; + } + + public void setAgreementVersion(int agreementVersion) { + this.agreementVersion.set(agreementVersion); + } +} 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 60b8793a5..225dc76bd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -17,11 +17,14 @@ */ package org.jackhuang.hmcl.ui; +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXDialogLayout; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.ButtonBase; +import javafx.scene.control.Label; import javafx.scene.layout.Region; import javafx.scene.paint.Color; import javafx.stage.Stage; @@ -37,11 +40,8 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.account.AccountListPage; import org.jackhuang.hmcl.ui.animation.ContainerAnimations; -import org.jackhuang.hmcl.ui.construct.InputDialogPane; -import org.jackhuang.hmcl.ui.construct.MessageDialogPane; +import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; -import org.jackhuang.hmcl.ui.construct.PromptDialogPane; -import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane; import org.jackhuang.hmcl.ui.decorator.DecoratorController; import org.jackhuang.hmcl.ui.download.DownloadPage; import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider; @@ -64,6 +64,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import static org.jackhuang.hmcl.setting.ConfigHolder.config; +import static org.jackhuang.hmcl.setting.ConfigHolder.globalConfig; import static org.jackhuang.hmcl.ui.FXUtils.newImage; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -201,6 +202,27 @@ public final class Controllers { stage.setTitle(Metadata.FULL_TITLE); stage.initStyle(StageStyle.TRANSPARENT); stage.setScene(scene); + + if (globalConfig().getAgreementVersion() < 1) { + JFXDialogLayout agreementPane = new JFXDialogLayout(); + agreementPane.setHeading(new Label(i18n("launcher.agreement"))); + agreementPane.setBody(new Label(i18n("launcher.agreement.hint"))); + JFXHyperlink agreementLink = new JFXHyperlink(i18n("launcher.agreement")); + agreementLink.setOnAction(e -> FXUtils.openLink("https://hmcl.huangyuhui.net/eula")); + JFXButton yesButton = new JFXButton(i18n("launcher.agreement.accept")); + yesButton.getStyleClass().add("dialog-accept"); + yesButton.setOnAction(e -> { + globalConfig().setAgreementVersion(1); + agreementPane.fireEvent(new DialogCloseEvent()); + }); + JFXButton noButton = new JFXButton(i18n("launcher.agreement.decline")); + noButton.getStyleClass().add("dialog-cancel"); + noButton.setOnAction(e -> { + System.exit(1); + }); + agreementPane.setActions(agreementLink, yesButton, noButton); + Controllers.dialog(agreementPane); + } } public static void dialog(Region content) { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 75e85d3e3..b179a2d45 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -420,6 +420,10 @@ launch.state.waiting_launching=Launching Game launch.wrong_javadir=Invalid Java directory, default Java path will be applied. launcher=Launcher +launcher.agreement=EULA +launcher.agreement.accept=Accept +launcher.agreement.decline=Decline +launcher.agreement.hint=You must agree the EULA to use this software. launcher.background=Background Image launcher.background.classic=Classic launcher.background.choose=Choose a background image file diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index c726141ae..b9ff8e14a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -420,6 +420,10 @@ launch.state.waiting_launching=等待遊戲啟動 launch.wrong_javadir=Java 路徑錯誤,將自動重設為預設 Java 路徑。 launcher=啟動器 +launcher.agreement=用戶協議與免責聲明 +launcher.agreement.accept=同意 +launcher.agreement.decline=拒絕 +launcher.agreement.hint=同意本軟體的用戶協議與免責聲明以使用本軟體。 launcher.background=背景位址 launcher.background.choose=選擇背景路徑 launcher.background.classic=經典 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 6385b8974..3c06f1571 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -420,6 +420,10 @@ launch.state.waiting_launching=等待游戏启动 launch.wrong_javadir=错误的 Java 路径,将自动重置为默认 Java 路径。 launcher=启动器 +launcher.agreement=用户协议与免责声明 +launcher.agreement.accept=同意 +launcher.agreement.decline=拒绝 +launcher.agreement.hint=同意本软件的用户协议与免责声明以使用本软件。 launcher.background=背景地址 launcher.background.choose=选择背景路径 launcher.background.classic=经典