feat: eula.

This commit is contained in:
huanghongxun 2021-09-21 18:41:50 +08:00
parent fb7ff7b891
commit 3f9d16ab8c
7 changed files with 204 additions and 6 deletions

View File

@ -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

View File

@ -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());
}
} }

View 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);
}
}

View File

@ -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) {

View File

@ -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

View 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=經典

View 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=经典