using Dalamud.Plugin; using MareSynchronos.MareConfiguration.Configurations; using Microsoft.Extensions.Logging; using Newtonsoft.Json; namespace MareSynchronos.MareConfiguration; public abstract class ConfigurationServiceBase : IDisposable where T : IMareConfiguration { protected abstract string ConfigurationName { get; } public string ConfigurationDirectory => _pluginInterface.ConfigDirectory.FullName; public T Current => _currentConfigInternal.Value; protected readonly DalamudPluginInterface _pluginInterface; private readonly CancellationTokenSource _periodicCheckCts = new(); private DateTime _configLastWriteTime; private bool _configIsDirty = false; private Lazy _currentConfigInternal; protected string ConfigurationPath => Path.Combine(ConfigurationDirectory, ConfigurationName); protected ConfigurationServiceBase(DalamudPluginInterface pluginInterface) { _pluginInterface = pluginInterface; Task.Run(CheckForConfigUpdatesInternal, _periodicCheckCts.Token); Task.Run(CheckForDirtyConfigInternal, _periodicCheckCts.Token); _currentConfigInternal = LazyConfig(); } private Lazy LazyConfig() { _configLastWriteTime = GetConfigLastWriteTime(); return new Lazy(() => LoadConfig()); } private DateTime GetConfigLastWriteTime() => new FileInfo(ConfigurationPath).LastWriteTimeUtc; private async Task CheckForConfigUpdatesInternal() { while (!_periodicCheckCts.IsCancellationRequested) { await Task.Delay(TimeSpan.FromSeconds(5), _periodicCheckCts.Token).ConfigureAwait(false); var lastWriteTime = GetConfigLastWriteTime(); if (lastWriteTime != _configLastWriteTime) { //_logger.LogDebug($"Config {ConfigurationName} changed, reloading config"); _currentConfigInternal = LazyConfig(); } } } private async Task CheckForDirtyConfigInternal() { while (!_periodicCheckCts.IsCancellationRequested) { if (_configIsDirty) { SaveDirtyConfig(); } await Task.Delay(TimeSpan.FromSeconds(1), _periodicCheckCts.Token).ConfigureAwait(false); } } protected T LoadConfig() { T? config; if (!File.Exists(ConfigurationPath)) { config = (T)Activator.CreateInstance(typeof(T))!; Save(); } else { config = JsonConvert.DeserializeObject(File.ReadAllText(ConfigurationPath)); if (config == null) { config = (T)Activator.CreateInstance(typeof(T))!; Save(); } } _configLastWriteTime = GetConfigLastWriteTime(); return config; } protected void SaveDirtyConfig() { _configIsDirty = false; var existingConfigs = Directory.EnumerateFiles(ConfigurationDirectory, ConfigurationName + ".bak.*").Select(c => new FileInfo(c)) .OrderByDescending(c => c.LastWriteTime).ToList(); if (existingConfigs.Skip(10).Any()) { foreach (var config in existingConfigs.Skip(10).ToList()) { config.Delete(); } } //_logger.LogDebug("Saving dirty config " + ConfigurationName); try { File.Copy(ConfigurationPath, ConfigurationPath + ".bak." + DateTime.Now.ToString("yyyyMMddHHmmss"), overwrite: true); } catch { } File.WriteAllText(ConfigurationPath, JsonConvert.SerializeObject(Current, Formatting.Indented)); _configLastWriteTime = new FileInfo(ConfigurationPath).LastWriteTimeUtc; } public void Save() { _configIsDirty = true; } public void Dispose() { //_logger.LogTrace($"Disposing {GetType()}"); _periodicCheckCts.Cancel(); } }