mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-16 15:26:27 -04:00
feat: eula.
This commit is contained in:
parent
fb7ff7b891
commit
3f9d16ab8c
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Hello Minecraft! Launcher
|
* Hello Minecraft! Launcher
|
||||||
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,6 +19,7 @@ package org.jackhuang.hmcl.setting;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
import org.jackhuang.hmcl.Metadata;
|
||||||
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
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 = "hmcl.json";
|
||||||
public static final String CONFIG_FILENAME_LINUX = ".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 Path configLocation;
|
||||||
private static Config configInstance;
|
private static Config configInstance;
|
||||||
|
private static GlobalConfig globalConfigInstance;
|
||||||
private static boolean newlyCreated;
|
private static boolean newlyCreated;
|
||||||
|
|
||||||
public static Config config() {
|
public static Config config() {
|
||||||
@ -52,6 +55,13 @@ public final class ConfigHolder {
|
|||||||
return configInstance;
|
return configInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GlobalConfig globalConfig() {
|
||||||
|
if (globalConfigInstance == null) {
|
||||||
|
throw new IllegalStateException("Configuration hasn't been loaded");
|
||||||
|
}
|
||||||
|
return globalConfigInstance;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isNewlyCreated() {
|
public static boolean isNewlyCreated() {
|
||||||
return newlyCreated;
|
return newlyCreated;
|
||||||
}
|
}
|
||||||
@ -68,6 +78,9 @@ public final class ConfigHolder {
|
|||||||
configInstance = loadConfig();
|
configInstance = loadConfig();
|
||||||
configInstance.addListener(source -> markConfigDirty());
|
configInstance.addListener(source -> markConfigDirty());
|
||||||
|
|
||||||
|
globalConfigInstance = loadGlobalConfig();
|
||||||
|
globalConfigInstance.addListener(source -> markGlobalConfigDirty());
|
||||||
|
|
||||||
Settings.init();
|
Settings.init();
|
||||||
|
|
||||||
if (newlyCreated) {
|
if (newlyCreated) {
|
||||||
@ -145,7 +158,7 @@ public final class ConfigHolder {
|
|||||||
return new Config();
|
return new Config();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InvocationDispatcher<String> configWriter = InvocationDispatcher.runOn(Lang::thread, content -> {
|
private static final InvocationDispatcher<String> configWriter = InvocationDispatcher.runOn(Lang::thread, content -> {
|
||||||
try {
|
try {
|
||||||
writeToConfig(content);
|
writeToConfig(content);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -167,4 +180,48 @@ public final class ConfigHolder {
|
|||||||
private static void saveConfigSync() throws IOException {
|
private static void saveConfigSync() throws IOException {
|
||||||
writeToConfig(configInstance.toJson());
|
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<String> 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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
107
HMCL/src/main/java/org/jackhuang/hmcl/setting/GlobalConfig.java
Normal file
107
HMCL/src/main/java/org/jackhuang/hmcl/setting/GlobalConfig.java
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.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);
|
||||||
|
}
|
||||||
|
}
|
@ -17,11 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui;
|
package org.jackhuang.hmcl.ui;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXDialogLayout;
|
||||||
import javafx.beans.property.DoubleProperty;
|
import javafx.beans.property.DoubleProperty;
|
||||||
import javafx.beans.property.SimpleDoubleProperty;
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.ButtonBase;
|
import javafx.scene.control.ButtonBase;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
@ -37,11 +40,8 @@ import org.jackhuang.hmcl.task.Task;
|
|||||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||||
import org.jackhuang.hmcl.ui.account.AccountListPage;
|
import org.jackhuang.hmcl.ui.account.AccountListPage;
|
||||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||||
import org.jackhuang.hmcl.ui.construct.InputDialogPane;
|
import org.jackhuang.hmcl.ui.construct.*;
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
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.decorator.DecoratorController;
|
||||||
import org.jackhuang.hmcl.ui.download.DownloadPage;
|
import org.jackhuang.hmcl.ui.download.DownloadPage;
|
||||||
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
|
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
|
||||||
@ -64,6 +64,7 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
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.ui.FXUtils.newImage;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
@ -201,6 +202,27 @@ public final class Controllers {
|
|||||||
stage.setTitle(Metadata.FULL_TITLE);
|
stage.setTitle(Metadata.FULL_TITLE);
|
||||||
stage.initStyle(StageStyle.TRANSPARENT);
|
stage.initStyle(StageStyle.TRANSPARENT);
|
||||||
stage.setScene(scene);
|
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) {
|
public static void dialog(Region content) {
|
||||||
|
@ -420,6 +420,10 @@ launch.state.waiting_launching=Launching Game
|
|||||||
launch.wrong_javadir=Invalid Java directory, default Java path will be applied.
|
launch.wrong_javadir=Invalid Java directory, default Java path will be applied.
|
||||||
|
|
||||||
launcher=Launcher
|
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=Background Image
|
||||||
launcher.background.classic=Classic
|
launcher.background.classic=Classic
|
||||||
launcher.background.choose=Choose a background image file
|
launcher.background.choose=Choose a background image file
|
||||||
|
@ -420,6 +420,10 @@ launch.state.waiting_launching=等待遊戲啟動
|
|||||||
launch.wrong_javadir=Java 路徑錯誤,將自動重設為預設 Java 路徑。
|
launch.wrong_javadir=Java 路徑錯誤,將自動重設為預設 Java 路徑。
|
||||||
|
|
||||||
launcher=啟動器
|
launcher=啟動器
|
||||||
|
launcher.agreement=用戶協議與免責聲明
|
||||||
|
launcher.agreement.accept=同意
|
||||||
|
launcher.agreement.decline=拒絕
|
||||||
|
launcher.agreement.hint=同意本軟體的用戶協議與免責聲明以使用本軟體。
|
||||||
launcher.background=背景位址
|
launcher.background=背景位址
|
||||||
launcher.background.choose=選擇背景路徑
|
launcher.background.choose=選擇背景路徑
|
||||||
launcher.background.classic=經典
|
launcher.background.classic=經典
|
||||||
|
@ -420,6 +420,10 @@ launch.state.waiting_launching=等待游戏启动
|
|||||||
launch.wrong_javadir=错误的 Java 路径,将自动重置为默认 Java 路径。
|
launch.wrong_javadir=错误的 Java 路径,将自动重置为默认 Java 路径。
|
||||||
|
|
||||||
launcher=启动器
|
launcher=启动器
|
||||||
|
launcher.agreement=用户协议与免责声明
|
||||||
|
launcher.agreement.accept=同意
|
||||||
|
launcher.agreement.decline=拒绝
|
||||||
|
launcher.agreement.hint=同意本软件的用户协议与免责声明以使用本软件。
|
||||||
launcher.background=背景地址
|
launcher.background=背景地址
|
||||||
launcher.background.choose=选择背景路径
|
launcher.background.choose=选择背景路径
|
||||||
launcher.background.classic=经典
|
launcher.background.classic=经典
|
||||||
|
Loading…
x
Reference in New Issue
Block a user