1
0
forked from Clones/Controlify
Files
Controlify/src/main/java/dev/isxander/controlify/config/ControlifyConfig.java

227 lines
8.0 KiB
Java

package dev.isxander.controlify.config;
import com.google.gson.*;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.ControllerManager;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.joystick.CompoundJoystickInfo;
import dev.isxander.controlify.utils.DebugLog;
import dev.isxander.controlify.utils.Log;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.Version;
import net.fabricmc.loader.api.VersionParsingException;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class ControlifyConfig {
public static final Path CONFIG_PATH = FabricLoader.getInstance().getConfigDir().resolve("controlify.json");
private static final Gson GSON = new GsonBuilder()
.serializeNulls()
.setPrettyPrinting()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeHierarchyAdapter(Class.class, new TypeAdapters.ClassTypeAdapter())
.registerTypeHierarchyAdapter(Version.class, new TypeAdapters.VersionTypeAdapter())
.create();
private final Controlify controlify;
private String currentControllerUid;
private JsonObject controllerData = new JsonObject();
private Map<String, CompoundJoystickInfo> compoundJoysticks = Map.of();
private GlobalSettings globalSettings = new GlobalSettings();
private boolean firstLaunch;
private Version lastSeenVersion = null;
private boolean dirty;
public ControlifyConfig(Controlify controlify) {
this.controlify = controlify;
}
public void save() {
Log.LOGGER.info("Saving Controlify config...");
try {
Files.deleteIfExists(CONFIG_PATH);
Files.writeString(CONFIG_PATH, GSON.toJson(generateConfig()), StandardOpenOption.CREATE_NEW, StandardOpenOption.TRUNCATE_EXISTING);
dirty = false;
} catch (IOException e) {
throw new IllegalStateException("Failed to save config!", e);
}
}
public void load() {
Log.LOGGER.info("Loading Controlify config...");
if (!Files.exists(CONFIG_PATH)) {
firstLaunch = true;
if (lastSeenVersion == null) {
try {
lastSeenVersion = Version.parse("0.0.0");
} catch (VersionParsingException e) {
throw new RuntimeException(e);
}
setDirty();
}
save();
return;
}
try {
applyConfig(GSON.fromJson(Files.readString(CONFIG_PATH), JsonObject.class));
} catch (Exception e) {
Log.LOGGER.error("Failed to load Controlify config!", e);
}
if (dirty) {
DebugLog.log("Config was dirty after load, saving...");
save();
}
}
private JsonObject generateConfig() {
JsonObject config = new JsonObject();
config.addProperty("last_seen_version", Log.VERSION.getFriendlyString());
JsonObject newControllerData = controllerData.deepCopy(); // we use the old config, so we don't lose disconnected controller data
for (var controller : ControllerManager.getConnectedControllers()) {
// `add` replaces if already existing
newControllerData.add(controller.uid(), generateControllerConfig(controller));
}
controllerData = newControllerData;
config.addProperty("current_controller", currentControllerUid = controlify.getCurrentController().map(Controller::uid).orElse(null));
config.add("controllers", controllerData);
config.add("compound_joysticks", GSON.toJsonTree(compoundJoysticks.values().toArray(new CompoundJoystickInfo[0])));
config.add("global", GSON.toJsonTree(globalSettings));
return config;
}
private JsonObject generateControllerConfig(Controller<?, ?> controller) {
JsonObject object = new JsonObject();
object.add("config", GSON.toJsonTree(controller.config()));
object.add("bindings", controller.bindings().toJson());
return object;
}
private void applyConfig(JsonObject object) throws VersionParsingException {
if (lastSeenVersion == null) {
boolean hasLastSeenVersion = object.has("last_seen_version");
lastSeenVersion = hasLastSeenVersion ? Version.parse(object.get("last_seen_version").getAsString()) : Version.parse("0.0.0");
if (!hasLastSeenVersion || lastSeenVersion.compareTo(Log.VERSION) < 0) {
setDirty();
}
}
globalSettings = GSON.fromJson(object.getAsJsonObject("global"), GlobalSettings.class);
if (globalSettings == null) {
globalSettings = new GlobalSettings();
setDirty();
}
JsonObject controllers = object.getAsJsonObject("controllers");
if (controllers != null) {
this.controllerData = controllers;
for (var controller : ControllerManager.getConnectedControllers()) {
loadOrCreateControllerData(controller);
}
} else {
setDirty();
}
if (object.has("compound_joysticks")) {
this.compoundJoysticks = object
.getAsJsonArray("compound_joysticks")
.asList()
.stream()
.map(element -> GSON.fromJson(element, CompoundJoystickInfo.class))
.collect(Collectors.toMap(info -> info.type().mappingId(), Function.identity()));
} else {
this.compoundJoysticks = Map.of();
}
if (object.has("current_controller")) {
JsonElement element = object.get("current_controller");
currentControllerUid = element.isJsonNull() ? null : element.getAsString();
} else {
currentControllerUid = controlify.getCurrentController().map(Controller::uid).orElse(null);
setDirty();
}
}
public void loadOrCreateControllerData(Controller<?, ?> controller) {
var uid = controller.uid();
if (controllerData.has(uid)) {
DebugLog.log("Loading controller data for " + uid);
applyControllerConfig(controller, controllerData.getAsJsonObject(uid));
} else {
DebugLog.log("New controller found, setting config dirty ({})", uid);
setDirty();
}
}
private void applyControllerConfig(Controller<?, ?> controller, JsonObject object) {
try {
controller.setConfig(GSON, object.getAsJsonObject("config"));
dirty |= !controller.bindings().fromJson(object.getAsJsonObject("bindings"));
} catch (Exception e) {
Log.LOGGER.error("Failed to load controller data for " + controller.uid() + ". Resetting to default!", e);
controller.resetConfig();
save();
}
}
public void setDirty() {
dirty = true;
}
public void saveIfDirty() {
if (dirty) {
DebugLog.log("Config is dirty. Saving...");
save();
}
}
public Map<String, CompoundJoystickInfo> getCompoundJoysticks() {
return compoundJoysticks;
}
public GlobalSettings globalSettings() {
return globalSettings;
}
public boolean isFirstLaunch() {
return firstLaunch;
}
public boolean isLastSeenVersionLessThan(Version version) {
return lastSeenVersion.compareTo(version) < 0;
}
public boolean isLastSeenVersionLessThan(String version) {
try {
return isLastSeenVersionLessThan(Version.parse(version));
} catch (VersionParsingException e) {
throw new RuntimeException(e);
}
}
@Nullable
public String currentControllerUid() {
return currentControllerUid;
}
}