-
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
331 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Specialized; | ||
using System.ComponentModel; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Windows.Media; | ||
using System.Windows.Threading; | ||
using ff14bot.Helpers; | ||
using LlamaLibrary.Logging; | ||
using LlamaLibrary.Memory; | ||
using Newtonsoft.Json; | ||
using LogLevel = LlamaLibrary.Logging.LogLevel; | ||
|
||
namespace LlamaLibrary.Settings.Base; | ||
|
||
using LogLevel = LogLevel; | ||
|
||
public abstract class BaseSettings : INotifyPropertyChanged | ||
{ | ||
private readonly LLogger _logger; | ||
private readonly DebounceDispatcher _saveDebounceDispatcher; | ||
private Dictionary<string, DebounceDispatcher>? _debounceDispatchers; | ||
private bool _loaded; | ||
|
||
public BaseSettings(string path) | ||
{ | ||
Dispatcher = Dispatcher.CurrentDispatcher; | ||
_saveDebounceDispatcher = new DebounceDispatcher(SaveLocal); | ||
_logger = new LLogger($"{GetType().Name}", Colors.Peru, LogLevel.Debug); | ||
FilePath = path; | ||
LoadFrom(FilePath); | ||
} | ||
|
||
public static string AssemblyDirectory => JsonSettings.AssemblyPath ?? throw new InvalidOperationException(); | ||
|
||
public static string AssemblyPath => AssemblyDirectory; | ||
|
||
public static string SettingsPath => Path.Combine(AssemblyPath, "Settings"); | ||
|
||
private Dispatcher Dispatcher { get; } | ||
|
||
[JsonIgnore] | ||
private string FilePath { get; } | ||
|
||
public static string GetSettingsFilePath(params string[] subPathParts) | ||
{ | ||
var list = new List<string> { SettingsPath }; | ||
list.AddRange(subPathParts); | ||
return Path.Combine(list.ToArray()); | ||
} | ||
|
||
protected void LoadFrom(string file) | ||
{ | ||
var properties = GetType().GetProperties(); | ||
|
||
foreach (var propertyInfo in properties) | ||
{ | ||
_debounceDispatchers ??= new Dictionary<string, DebounceDispatcher>(); | ||
_debounceDispatchers[propertyInfo.Name] = new DebounceDispatcher(OnPropertyChangedDebounce); | ||
|
||
var customAttributes = propertyInfo.GetCustomAttributes<DefaultValueAttribute>(true).ToList(); | ||
|
||
if (customAttributes.Count == 0) | ||
{ | ||
continue; | ||
} | ||
|
||
foreach (var custom in customAttributes) | ||
{ | ||
if (propertyInfo.GetSetMethod() != null) | ||
{ | ||
propertyInfo.SetValue(this, custom.Value, null); | ||
} | ||
} | ||
} | ||
|
||
if (File.Exists(file)) | ||
{ | ||
try | ||
{ | ||
Dispatcher.Invoke(() => | ||
{ | ||
try | ||
{ | ||
JsonConvert.PopulateObject(File.ReadAllText(file), this); | ||
} | ||
catch (Exception e) | ||
{ | ||
_logger.Exception(e); | ||
} | ||
}); | ||
|
||
foreach (var propertyInfo in properties) | ||
{ | ||
//Check if property is a observable collection | ||
if (typeof(INotifyCollectionChanged).IsAssignableFrom(propertyInfo.PropertyType)) | ||
{ | ||
//Set list changed event to trigger on property change | ||
var collection = propertyInfo.GetValue(this) as INotifyCollectionChanged; | ||
if (collection != null) | ||
{ | ||
if (propertyInfo.PropertyType.IsGenericType && typeof(INotifyPropertyChanged).IsAssignableFrom(propertyInfo.PropertyType.GenericTypeArguments[0])) | ||
{ | ||
collection.CollectionChanged += (_, args) => | ||
{ | ||
OnPropertyChanged(propertyInfo.Name); | ||
|
||
if (args is { Action: NotifyCollectionChangedAction.Add, NewItems: not null }) | ||
{ | ||
foreach (var item in args.NewItems) | ||
{ | ||
if (item is INotifyPropertyChanged notifyPropertyChanged) | ||
{ | ||
notifyPropertyChanged.PropertyChanged += OnNotifyPropertyChangedOnPropertyChanged; | ||
} | ||
} | ||
} | ||
|
||
if (args is { Action: NotifyCollectionChangedAction.Remove, OldItems: not null }) | ||
{ | ||
foreach (var item in args.OldItems) | ||
{ | ||
if (item is INotifyPropertyChanged notifyPropertyChanged) | ||
{ | ||
notifyPropertyChanged.PropertyChanged -= OnNotifyPropertyChangedOnPropertyChanged; | ||
} | ||
} | ||
} | ||
|
||
return; | ||
|
||
void OnNotifyPropertyChangedOnPropertyChanged(object? o, PropertyChangedEventArgs eventArgs) | ||
{ | ||
OnPropertyChanged(propertyInfo.Name); | ||
} | ||
}; | ||
} | ||
else | ||
{ | ||
collection.CollectionChanged += (_, _) => { OnPropertyChanged(propertyInfo.Name); }; | ||
} | ||
} | ||
else | ||
{ | ||
_logger.Error($"Property {propertyInfo.Name} is not an INotifyCollectionChanged it is {propertyInfo.PropertyType}"); | ||
} | ||
} | ||
|
||
if (typeof(IBindingList).IsAssignableFrom(propertyInfo.PropertyType)) | ||
{ | ||
//Set list changed event to trigger on property change | ||
var collection = propertyInfo.GetValue(this) as IBindingList; | ||
if (collection != null) | ||
{ | ||
collection.ListChanged += (_, _) => { OnPropertyChanged(propertyInfo.Name); }; | ||
} | ||
else | ||
{ | ||
_logger.Error($"Property {propertyInfo.Name} is not an IBindingList it is {propertyInfo.PropertyType}"); | ||
} | ||
} | ||
|
||
if (typeof(INotifyPropertyChanged).IsAssignableFrom(propertyInfo.PropertyType)) | ||
{ | ||
var notifyPropertyChanged = propertyInfo.GetValue(this) as INotifyPropertyChanged; | ||
if (notifyPropertyChanged != null) | ||
{ | ||
notifyPropertyChanged.PropertyChanged += (_, _) => { OnPropertyChanged(propertyInfo.Name); }; | ||
} | ||
else | ||
{ | ||
_logger.Error($"Property {propertyInfo.Name} is not an INotifyPropertyChanged it is {propertyInfo.PropertyType}"); | ||
} | ||
} | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
_logger.Exception(e); | ||
} | ||
} | ||
|
||
_loaded = true; | ||
if (file != FilePath || !File.Exists(file)) | ||
{ | ||
Save(); | ||
} | ||
} | ||
|
||
public virtual void Save() | ||
{ | ||
_saveDebounceDispatcher.Debounce(500, null!, DispatcherPriority.ApplicationIdle, Dispatcher); | ||
} | ||
|
||
private void SaveLocal(object? state = null) | ||
{ | ||
SaveAs(FilePath); | ||
} | ||
|
||
public void SaveAs(string file) | ||
{ | ||
try | ||
{ | ||
if (!_loaded) | ||
{ | ||
return; | ||
} | ||
|
||
if (!File.Exists(file)) | ||
{ | ||
var directoryName = Path.GetDirectoryName(file); | ||
if (directoryName != null && !Directory.Exists(directoryName)) | ||
{ | ||
Directory.CreateDirectory(directoryName); | ||
} | ||
} | ||
|
||
File.WriteAllText(file, JsonConvert.SerializeObject(this, Formatting.Indented)); | ||
} | ||
catch (Exception e) | ||
{ | ||
_logger.Exception(e); | ||
} | ||
} | ||
|
||
private void OnPropertyChangedDebounce(object propertyName) | ||
{ | ||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs((string)propertyName)); | ||
Save(); | ||
} | ||
|
||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) | ||
{ | ||
if (propertyName == null) | ||
{ | ||
return; | ||
} | ||
|
||
_debounceDispatchers![propertyName].Debounce(50, propertyName, DispatcherPriority.ApplicationIdle, Dispatcher); | ||
} | ||
|
||
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null) | ||
{ | ||
if (EqualityComparer<T>.Default.Equals(field, value)) | ||
{ | ||
return false; | ||
} | ||
|
||
field = value; | ||
OnPropertyChanged(propertyName); | ||
return true; | ||
} | ||
|
||
public event PropertyChangedEventHandler? PropertyChanged; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using ff14bot; | ||
using LlamaLibrary.Extensions; | ||
|
||
namespace LlamaLibrary.Settings.Base; | ||
|
||
public class BaseSettings<T> : BaseSettings | ||
where T : BaseSettings<T>, new() | ||
{ | ||
private static T? _instance; | ||
|
||
public BaseSettings() : base(GetSettingsFilePath($"{typeof(T).Name}.json")) | ||
{ | ||
} | ||
|
||
public BaseSettings(string settingsFilePath) : base(settingsFilePath) | ||
{ | ||
} | ||
|
||
public static T Instance | ||
{ | ||
get => _instance ??= new T(); | ||
set => _instance = value; | ||
} | ||
|
||
public static void SetInstance(T instance) | ||
{ | ||
_instance = instance; | ||
} | ||
} | ||
|
||
public class CharacterBaseSettings<T> : BaseSettings<T> | ||
where T : BaseSettings<T>, new() | ||
{ | ||
public CharacterBaseSettings() : base(GetSettingsFilePath($"{Core.Me.Name}_{Core.Me.PlayerId()}", $"{typeof(T).Name}.json")) | ||
{ | ||
} | ||
|
||
public CharacterBaseSettings(string fileName) : base(GetSettingsFilePath($"{Core.Me.Name}_{Core.Me.PlayerId()}", fileName)) | ||
{ | ||
} | ||
} | ||
|
||
public class AccountBaseSettings<T> : BaseSettings<T> | ||
where T : BaseSettings<T>, new() | ||
{ | ||
public AccountBaseSettings() : base(GetSettingsFilePath($"Account_{Core.Me.AccountId()}", $"{typeof(T).Name}.json")) | ||
{ | ||
} | ||
|
||
public AccountBaseSettings(string fileName) : base(GetSettingsFilePath($"Account_{Core.Me.AccountId()}", fileName)) | ||
{ | ||
} | ||
|
||
public AccountBaseSettings(int accountId, string fileName) : base(GetSettingsFilePath($"Account_{accountId}", fileName)) | ||
{ | ||
} | ||
|
||
public AccountBaseSettings(int accountId) : base(GetSettingsFilePath($"Account_{accountId}", $"{typeof(T).Name}.json")) | ||
{ | ||
} | ||
} | ||
|
||
public class HomeWorldBaseSettings<T> : BaseSettings<T> | ||
where T : BaseSettings<T>, new() | ||
{ | ||
public HomeWorldBaseSettings() : base(GetSettingsFilePath($"HomeWorld_{Core.Me.HomeWorld()}", $"{typeof(T).Name}.json")) | ||
{ | ||
} | ||
|
||
public HomeWorldBaseSettings(string fileName) : base(GetSettingsFilePath($"HomeWorld_{Core.Me.HomeWorld()}", fileName)) | ||
{ | ||
} | ||
} |