feat: prepare for JavaFX-property-based launcher settings (#665)

master
Tobias Nett 2021-11-12 20:14:29 +01:00 committed by GitHub
parent 6a41541d93
commit 634037c6ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 7 deletions

View File

@ -100,6 +100,11 @@ dependencies {
implementation group: 'com.vdurmont', name: 'semver4j', version: '3.1.0'
implementation group: 'com.vladsch.flexmark', name: 'flexmark-all', version: '0.62.2'
implementation('org.hildan.fxgson:fx-gson:4.0.0') {
because "de-/serialization of launcher properties to JSON"
}
// These dependencies are only needed for running tests
testImplementation 'org.hamcrest:hamcrest:2.2'

View File

@ -74,7 +74,7 @@ public class LauncherInitTask extends Task<LauncherConfiguration> {
final Path cacheDirectory = getDirectoryFor(LauncherManagedDirectory.CACHE, userDataDirectory);
// launcher settings
final Path settingsFile = userDataDirectory.resolve(Settings.DEFAULT_FILE_NAME);
final Path settingsFile = userDataDirectory.resolve(Settings.LEGACY_FILE_NAME);
final LauncherSettings launcherSettings = getLauncherSettings(settingsFile);
// validate the settings

View File

@ -3,29 +3,120 @@
package org.terasology.launcher.settings;
import com.google.gson.Gson;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import org.hildan.fxgson.FxGson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
import org.terasology.launcher.model.GameIdentifier;
import org.terasology.launcher.util.JavaHeapSize;
import org.terasology.launcher.util.Languages;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.Properties;
//TODO: should this be called `SettingsController` and also carry out some UI handling, e.g., displaying error messages
// to the user?
public final class Settings {
public static final String DEFAULT_FILE_NAME = "TerasologyLauncherSettings.properties";
public static final String LEGACY_FILE_NAME = "TerasologyLauncherSettings.properties";
public static final String JSON_FILE_NAME = "settings.json";
private static final Logger logger = LoggerFactory.getLogger(Settings.class);
private static final String COMMENT_SETTINGS = "Terasology Launcher - Settings";
private static Gson gson = FxGson.coreBuilder()
.registerTypeAdapter(Path.class, new PathConverter())
.setPrettyPrinting()
.create();
private Settings() {
public final ObjectProperty<Locale> locale;
public final ObjectProperty<JavaHeapSize> maxHeapSize;
public final ObjectProperty<JavaHeapSize> minHeapSize;
public final ObjectProperty<Level> logLevel;
public final ObjectProperty<Path> gameDirectory;
public final ObjectProperty<Path> gameDataDirectory;
public final BooleanProperty keepDownloadedFiles;
public final BooleanProperty showPreReleases;
public final BooleanProperty closeLauncherAfterGameStart;
public final ObjectProperty<GameIdentifier> lastPlayedGameVersion;
public final ListProperty<String> userJavaParameters;
public final ListProperty<String> userGameParameters;
Settings() {
locale = new SimpleObjectProperty<>(Languages.getCurrentLocale());
maxHeapSize = new SimpleObjectProperty<>(JavaHeapSize.NOT_USED);
minHeapSize = new SimpleObjectProperty<>(JavaHeapSize.NOT_USED);
logLevel = new SimpleObjectProperty<>(Level.INFO);
gameDirectory = new SimpleObjectProperty<>();
gameDataDirectory = new SimpleObjectProperty<>();
keepDownloadedFiles = new SimpleBooleanProperty(false);
showPreReleases = new SimpleBooleanProperty(false);
closeLauncherAfterGameStart = new SimpleBooleanProperty(true);
lastPlayedGameVersion = new SimpleObjectProperty<>();
userJavaParameters = new SimpleListProperty<>(FXCollections.observableArrayList());
userGameParameters = new SimpleListProperty<>(FXCollections.observableArrayList());
}
static Settings fromLegacy(LauncherSettings legacyLauncherSettings) {
Settings jsonSettings = new Settings();
jsonSettings.locale.setValue(legacyLauncherSettings.getLocale());
jsonSettings.maxHeapSize.setValue(legacyLauncherSettings.getMaxHeapSize());
jsonSettings.minHeapSize.setValue(legacyLauncherSettings.getInitialHeapSize());
jsonSettings.logLevel.setValue(legacyLauncherSettings.getLogLevel());
jsonSettings.gameDirectory.setValue(legacyLauncherSettings.getGameDirectory());
jsonSettings.gameDataDirectory.setValue(legacyLauncherSettings.getGameDataDirectory());
jsonSettings.keepDownloadedFiles.setValue(legacyLauncherSettings.isKeepDownloadedFiles());
jsonSettings.showPreReleases.setValue(legacyLauncherSettings.isShowPreReleases());
jsonSettings.closeLauncherAfterGameStart.setValue(legacyLauncherSettings.isCloseLauncherAfterGameStart());
jsonSettings.lastPlayedGameVersion.setValue(legacyLauncherSettings.getLastPlayedGameVersion().orElse(null));
jsonSettings.userJavaParameters.setAll(legacyLauncherSettings.getJavaParameterList());
jsonSettings.userGameParameters.setAll(legacyLauncherSettings.getUserGameParameterList());
return jsonSettings;
}
//TODO: change contract to load a file with fixed name from the path such that this method can decide on file format
public static LauncherSettings load(final Path path) {
// TODO: try to load from JSON, fall-back to Properties
Path json = path.getParent().resolve(JSON_FILE_NAME);
if (Files.exists(json)) {
logger.debug("Loading launcher settings from '{}'.", json);
try (FileReader reader = new FileReader(json.toFile())) {
Settings jsonSettings = gson.fromJson(reader, Settings.class);
logger.info(jsonSettings.toString());
} catch (IOException e) {
logger.error("Error while loading launcher settings from file.", e);
}
}
if (Files.exists(path)) {
logger.debug("Loading launcher settings from '{}'.", path);
@ -38,6 +129,7 @@ public final class Settings {
logger.error("Error while loading launcher settings from file.", e);
}
}
return null;
}
@ -47,11 +139,34 @@ public final class Settings {
Files.createDirectories(path.getParent());
}
try (OutputStream outputStream = Files.newOutputStream(path)) {
settings.getProperties().store(outputStream, COMMENT_SETTINGS);
settings.getProperties().store(outputStream, "Terasology Launcher - Settings");
}
//TODO: For the switch, only write JSON. For some failover safety we may write both formats for one or two
// releases before fully deprecating the Properties.
Settings jsonSettings = fromLegacy(settings);
Path jsonPath = path.getParent().resolve(JSON_FILE_NAME);
logger.debug("Writing launcher settings to '{}'.", jsonPath);
try (FileWriter writer = new FileWriter(jsonPath.toFile())) {
gson.toJson(jsonSettings, writer);
writer.flush();
}
}
public static LauncherSettings getDefault() {
return new LauncherSettings(new Properties());
};
static class PathConverter implements JsonDeserializer<Path>, JsonSerializer<Path> {
@Override
public Path deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return Paths.get(json.getAsString());
}
@Override
public JsonElement serialize(Path src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src.toString());
}
}
}

View File

@ -460,7 +460,7 @@ public class ApplicationController {
*/
private void close() {
logger.debug("Dispose launcher frame...");
final Path settingsFile = launcherDirectory.resolve(Settings.DEFAULT_FILE_NAME);
final Path settingsFile = launcherDirectory.resolve(Settings.LEGACY_FILE_NAME);
try {
Settings.store(launcherSettings, settingsFile);
} catch (IOException e) {

View File

@ -149,7 +149,7 @@ public class SettingsController {
}
// store changed settings
final Path settingsFile = launcherDirectory.resolve(Settings.DEFAULT_FILE_NAME);
final Path settingsFile = launcherDirectory.resolve(Settings.LEGACY_FILE_NAME);
try {
Settings.store(launcherSettings, settingsFile);
} catch (IOException e) {