From f8fbd4a47605eaddc18c110b27065b024f64fc66 Mon Sep 17 00:00:00 2001 From: Zeffuro Date: Sun, 21 Dec 2025 11:01:12 +0100 Subject: [PATCH] First config implementation --- AetherBags/Configuration/CurrencySettings.cs | 11 +++ .../Configuration/SystemConfiguration.cs | 11 +++ AetherBags/Helpers/FileHelpers.cs | 70 ++++++++++++++ AetherBags/Helpers/ImportExportResetHelper.cs | 65 +++++++++++++ AetherBags/Helpers/Util.cs | 92 +++++++++++++++++++ AetherBags/Nodes/CurrencyNode.cs | 7 +- AetherBags/Plugin.cs | 18 +++- AetherBags/Services.cs | 3 + AetherBags/System.cs | 2 + 9 files changed, 275 insertions(+), 4 deletions(-) create mode 100644 AetherBags/Configuration/CurrencySettings.cs create mode 100644 AetherBags/Configuration/SystemConfiguration.cs create mode 100644 AetherBags/Helpers/FileHelpers.cs create mode 100644 AetherBags/Helpers/ImportExportResetHelper.cs create mode 100644 AetherBags/Helpers/Util.cs diff --git a/AetherBags/Configuration/CurrencySettings.cs b/AetherBags/Configuration/CurrencySettings.cs new file mode 100644 index 0000000..e5d2091 --- /dev/null +++ b/AetherBags/Configuration/CurrencySettings.cs @@ -0,0 +1,11 @@ +using System.Numerics; +using KamiToolKit.Classes; + +namespace AetherBags.Configuration; + +public class CurrencySettings +{ + public Vector4 DefaultColor { get; set; } = ColorHelper.GetColor(8); + public Vector4 CappedColor { get; set; } = ColorHelper.GetColor(43); + public Vector4 LimitColor { get; set; } = ColorHelper.GetColor(17); +} \ No newline at end of file diff --git a/AetherBags/Configuration/SystemConfiguration.cs b/AetherBags/Configuration/SystemConfiguration.cs new file mode 100644 index 0000000..c930b73 --- /dev/null +++ b/AetherBags/Configuration/SystemConfiguration.cs @@ -0,0 +1,11 @@ +using System.Numerics; +using KamiToolKit.Classes; + +namespace AetherBags.Configuration; + +public class SystemConfiguration +{ + public const string FileName = "AetherBags.json"; + + public CurrencySettings Currency { get; set; } = new(); +} \ No newline at end of file diff --git a/AetherBags/Helpers/FileHelpers.cs b/AetherBags/Helpers/FileHelpers.cs new file mode 100644 index 0000000..bc27d01 --- /dev/null +++ b/AetherBags/Helpers/FileHelpers.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; +using System.Text.Json; +using Dalamud.Utility; + +namespace AetherBags.Helpers; + +public static class FileHelpers { + private static readonly JsonSerializerOptions SerializerOptions = new() { + WriteIndented = true, + IncludeFields = true, + }; + + public static T LoadFile(string filePath) where T : new() { + var fileInfo = new FileInfo(filePath); + if (fileInfo is { Exists: true }) { + try { + var fileText = File.ReadAllText(fileInfo.FullName); + var dataObject = JsonSerializer.Deserialize(fileText, SerializerOptions); + + // If deserialize result is null, create a new instance instead and save it. + if (dataObject is null) { + dataObject = new T(); + SaveFile(dataObject, filePath); + } + + return dataObject; + } + catch (Exception e) { + // If there is any kind of error loading the file, generate a new one instead and save it. + Services.Logger.Error(e, $"Error trying to load file {filePath}, creating a new one instead."); + + SaveFile(new T(), filePath); + } + } + + var newFile = new T(); + SaveFile(newFile, filePath); + + return newFile; + } + + public static void SaveFile(T? file, string filePath) { + try { + if (file is null) { + Services.Logger.Error("Null file provided."); + return; + } + + var fileText = JsonSerializer.Serialize(file, file.GetType(), SerializerOptions); + FilesystemUtil.WriteAllTextSafe(filePath, fileText); + } + catch (Exception e) { + Services.Logger.Error(e, $"Error trying to save file {filePath}"); + } + } + + public static FileInfo GetFileInfo(params string[] path) { + var directory = Services.PluginInterface.ConfigDirectory; + + for (var index = 0; index < path.Length - 1; index++) { + directory = new DirectoryInfo(Path.Combine(directory.FullName, path[index])); + if (!directory.Exists) { + directory.Create(); + } + } + + return new FileInfo(Path.Combine(directory.FullName, path[^1])); + } +} diff --git a/AetherBags/Helpers/ImportExportResetHelper.cs b/AetherBags/Helpers/ImportExportResetHelper.cs new file mode 100644 index 0000000..85d2e24 --- /dev/null +++ b/AetherBags/Helpers/ImportExportResetHelper.cs @@ -0,0 +1,65 @@ +using System.Linq; +using AetherBags.Configuration; +using Dalamud.Bindings.ImGui; +using Dalamud.Game.ClientState.Keys; +using Dalamud.Interface.ImGuiNotification; + +namespace AetherBags.Helpers; + +public abstract class ImportExportResetHelper { + public static void TryImportConfigFromClipboard(SystemConfiguration currentOverlayConfig) + { + if (!Services.KeyState[VirtualKey.SHIFT]) + return; + + var clipboard = ImGui.GetClipboardText(); + var notification = new Notification { Content = "Configuration imported from clipboard.", Type = NotificationType.Success }; + + if (!string.IsNullOrWhiteSpace(clipboard)) + { + var imported = Util.DeserializeConfig(clipboard); + if (imported != null) + { + System.Config = imported; + Util.SaveConfig(System.Config); + Services.Logger.Info("Configuration imported from clipboard."); + } + else + { + notification.Content = "Clipboard data was invalid or could not be imported."; + notification.Type = NotificationType.Error; + Services.Logger.Warning("Clipboard data was invalid or could not be imported."); + } + } + else + { + notification.Content = "Clipboard is empty or invalid for import."; + notification.Type = NotificationType.Warning; + Services.Logger.Warning("Clipboard is empty or invalid for import."); + } + + Services.NotificationManager.AddNotification(notification); + } + + public static void TryExportConfigToClipboard( + SystemConfiguration config) + { + var exportString = Util.SerializeConfig(config); + ImGui.SetClipboardText(exportString); + Services.NotificationManager.AddNotification( + new Notification { Content = "Configuration exported to clipboard.", Type = NotificationType.Success } + ); + Services.Logger.Info("Configuration exported to clipboard."); + } + + public static void TryResetConfig() + { + System.Config = Util.ResetConfig(); + Util.SaveConfig(System.Config); + + Services.NotificationManager.AddNotification( + new Notification { Content = "Configuration reset to default.", Type = NotificationType.Success } + ); + Services.Logger.Info("Configuration reset to default."); + } +} diff --git a/AetherBags/Helpers/Util.cs b/AetherBags/Helpers/Util.cs new file mode 100644 index 0000000..3fac973 --- /dev/null +++ b/AetherBags/Helpers/Util.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using AetherBags.Configuration; +using JsonSerializer = System.Text.Json.JsonSerializer; + +namespace AetherBags.Helpers; + +public static class Util +{ + private static readonly JsonSerializerOptions ConfigJsonOptions = new() + { + WriteIndented = true, + IncludeFields = true, + PropertyNameCaseInsensitive = true, + ReadCommentHandling = JsonCommentHandling.Skip, + AllowTrailingCommas = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + + public static string SerializeUIntSet(HashSet set) + => string.Join(",", set.OrderBy(x => x)); + + public static HashSet DeserializeUIntSet(string data) + => data + .Split([','], StringSplitOptions.RemoveEmptyEntries) + .Select(s => uint.TryParse(s, out var val) ? val : (uint?)null) + .Where(v => v.HasValue) + .Select(v => v!.Value) + .ToHashSet(); + + private static string CompressToBase64(string str) + => Convert.ToBase64String(Dalamud.Utility.Util.CompressString(str)); + + private static string DecompressFromBase64(string base64) + => Dalamud.Utility.Util.DecompressString(Convert.FromBase64String(base64)); + + public static string SerializeHashSet(HashSet hashSet) + => CompressToBase64(SerializeUIntSet(hashSet)); + + public static HashSet DeserializeHashSet(string input) + { + try + { + return DeserializeUIntSet(DecompressFromBase64(input)); + } + catch + { + return new HashSet(); + } + } + + public static string SerializeConfig(SystemConfiguration config) + { + var json = JsonSerializer.Serialize(config, ConfigJsonOptions); + return CompressToBase64(json); + } + + public static SystemConfiguration? DeserializeConfig(string input) + { + try + { + var json = DecompressFromBase64(input); + return JsonSerializer.Deserialize(json, ConfigJsonOptions); + } + catch + { + return null; + } + } + + public static void SaveConfig(SystemConfiguration config) + { + FileInfo file = FileHelpers.GetFileInfo(SystemConfiguration.FileName); + FileHelpers.SaveFile(config, file.FullName); + } + + private static SystemConfiguration LoadConfig() + { + FileInfo file = FileHelpers.GetFileInfo(SystemConfiguration.FileName); + return FileHelpers.LoadFile(file.FullName); + } + + public static SystemConfiguration LoadConfigOrDefault() + => LoadConfig() ?? new SystemConfiguration(); + + public static SystemConfiguration ResetConfig() + => new SystemConfiguration(); +} diff --git a/AetherBags/Nodes/CurrencyNode.cs b/AetherBags/Nodes/CurrencyNode.cs index dbaa6d0..c39d522 100644 --- a/AetherBags/Nodes/CurrencyNode.cs +++ b/AetherBags/Nodes/CurrencyNode.cs @@ -44,10 +44,11 @@ public class CurrencyNode : SimpleComponentNode _countNode.Position = new Vector2(_iconImageNode.Bounds.Right + 2f, 0f); // Limit > Capped > Normal + var config = System.Config.Currency; _countNode.TextColor = - value.LimitReached ? ColorHelper.GetColor(17) : - value.IsCapped ? ColorHelper.GetColor(43) : - ColorHelper.GetColor(8); + value.LimitReached ? config.LimitColor : + value.IsCapped ? config.CappedColor : + config.DefaultColor; } } } \ No newline at end of file diff --git a/AetherBags/Plugin.cs b/AetherBags/Plugin.cs index 62f4a06..d48b1eb 100644 --- a/AetherBags/Plugin.cs +++ b/AetherBags/Plugin.cs @@ -1,5 +1,6 @@ using System.Numerics; using AetherBags.Addons; +using AetherBags.Configuration; using AetherBags.Helpers; using Dalamud.Plugin; using Dalamud.Game.Command; @@ -18,6 +19,8 @@ public class Plugin : IDalamudPlugin KamiToolKitLibrary.Initialize(pluginInterface); + System.Config = Util.LoadConfigOrDefault(); + System.AddonInventoryWindow = new AddonInventoryWindow { InternalName = "AetherBags", @@ -38,6 +41,7 @@ public class Plugin : IDalamudPlugin HelpMessage = HelpDescription }); Services.ClientState.Login += OnLogin; + Services.ClientState.Logout += OnLogout; if (Services.ClientState.IsLoggedIn) { Services.Framework.RunOnFrameworkThread(OnLogin); @@ -46,7 +50,10 @@ public class Plugin : IDalamudPlugin public void Dispose() { + Util.SaveConfig(System.Config); + Services.ClientState.Login -= OnLogin; + Services.ClientState.Logout -= OnLogout; Services.CommandManager.RemoveHandler("/aetherbags"); Services.CommandManager.RemoveHandler("/ab"); @@ -70,9 +77,18 @@ public class Plugin : IDalamudPlugin } } - private void OnLogin() { + private void OnLogin() + { + System.Config = Util.LoadConfigOrDefault(); + #if DEBUG System.AddonInventoryWindow.Toggle(); #endif } + + private void OnLogout(int type, int code) + { + Util.SaveConfig(System.Config); + System.AddonInventoryWindow.Close(); + } } \ No newline at end of file diff --git a/AetherBags/Services.cs b/AetherBags/Services.cs index e31584c..77fe4c8 100644 --- a/AetherBags/Services.cs +++ b/AetherBags/Services.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Plugin.Services; @@ -12,5 +13,7 @@ public class Services [PluginService] public static IDataManager DataManager { get; set; } = null!; [PluginService] public static IDalamudPluginInterface PluginInterface { get; private set; } = null!; [PluginService] public static IFramework Framework { get; private set; } = null!; + [PluginService] public static IKeyState KeyState { get; private set; } = null!; [PluginService] public static IPluginLog Logger { get; private set; } = null!; + [PluginService] public static INotificationManager NotificationManager { get; private set; } = null!; } \ No newline at end of file diff --git a/AetherBags/System.cs b/AetherBags/System.cs index 2a267dd..28ff178 100644 --- a/AetherBags/System.cs +++ b/AetherBags/System.cs @@ -1,8 +1,10 @@ using AetherBags.Addons; +using AetherBags.Configuration; namespace AetherBags; public static class System { public static AddonInventoryWindow AddonInventoryWindow { get; set; } = null!; + public static SystemConfiguration Config { get; set; } = null!; } \ No newline at end of file