From 9d2461b2eb53ec0ed9b2dc085a88cd36bac5a779 Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Tue, 4 Sep 2018 22:33:06 -0500 Subject: [PATCH] Added IConfigProvider and one implimentation --- .../ConfigProviders/JsonConfigProvider.cs | 100 +++++++++++++++++ IPA.Loader/Config/IConfigProvider.cs | 51 +++++++++ IPA.Loader/{ => Config}/IniFile.cs | 2 +- IPA.Loader/{ => Config}/ModPrefs.cs | 2 +- IPA.Loader/IConfigProvider.cs | 103 ------------------ IPA.Loader/IPA.Loader.csproj | 7 +- IPA.Loader/Loader/PluginManager.cs | 16 ++- IPA.Loader/Logging/Logger.cs | 1 + 8 files changed, 170 insertions(+), 112 deletions(-) create mode 100644 IPA.Loader/Config/ConfigProviders/JsonConfigProvider.cs create mode 100644 IPA.Loader/Config/IConfigProvider.cs rename IPA.Loader/{ => Config}/IniFile.cs (99%) rename IPA.Loader/{ => Config}/ModPrefs.cs (99%) delete mode 100644 IPA.Loader/IConfigProvider.cs diff --git a/IPA.Loader/Config/ConfigProviders/JsonConfigProvider.cs b/IPA.Loader/Config/ConfigProviders/JsonConfigProvider.cs new file mode 100644 index 00000000..7a1ef5ad --- /dev/null +++ b/IPA.Loader/Config/ConfigProviders/JsonConfigProvider.cs @@ -0,0 +1,100 @@ +using IPA.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IPA.Config.ConfigProviders +{ + internal class JsonConfigProvider : IConfigProvider + { + private JObject jsonObj; + + public dynamic Dynamic => jsonObj; + + public bool HasChanged { get; private set; } = false; + + private string _filename = null; + public string Filename + { + get => _filename; + set + { + if (_filename != null) + throw new InvalidOperationException("Can only assign to Filename once"); + _filename = value; + } + } + + public void Load() + { + var finfo = new FileInfo(Filename + ".json"); + if (finfo.Exists) + { + string json = finfo.OpenText().ReadToEnd(); + try + { + jsonObj = JObject.Parse(json); + } + catch (Exception e) + { + Logger.config.Error($"Error parsing JSON in file {Filename}.json; resetting to empty JSON"); + Logger.config.Error(e); + jsonObj = new JObject(); + File.WriteAllText(finfo.FullName, JsonConvert.SerializeObject(jsonObj)); + } + } + else + { + jsonObj = new JObject(); + } + + SetupListeners(); + } + + private void SetupListeners() + { + jsonObj.PropertyChanged += JsonObj_PropertyChanged; + jsonObj.ListChanged += JsonObj_ListChanged; + jsonObj.CollectionChanged += JsonObj_CollectionChanged; + } + + private void JsonObj_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + HasChanged = true; + } + + private void JsonObj_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e) + { + HasChanged = true; + } + + private void JsonObj_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + HasChanged = true; + } + + public T Parse() + { + return jsonObj.ToObject(); + } + + public void Save() + { + var finfo = new FileInfo(Filename + ".json"); + + File.WriteAllText(finfo.FullName, JsonConvert.SerializeObject(jsonObj)); + } + + public void Store(T obj) + { + jsonObj = JObject.FromObject(obj); + SetupListeners(); + HasChanged = true; + } + } +} diff --git a/IPA.Loader/Config/IConfigProvider.cs b/IPA.Loader/Config/IConfigProvider.cs new file mode 100644 index 00000000..5bd773d3 --- /dev/null +++ b/IPA.Loader/Config/IConfigProvider.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IPA.Config +{ + /// + /// An interface for configuration providers. + /// + public interface IConfigProvider + { + /// + /// Loads the data provided by this into an object of type . + /// + /// the type of the object to parse into + /// the values from the config provider parsed into the object + T Parse(); + /// + /// Stores the data from into the . + /// + /// the type of + /// the object containing the data to save + void Store(T obj); + + /// + /// Gets a dynamic object providing access to the configuration. + /// + dynamic Dynamic { get; } + + #region State getters + /// + /// Returns if object has changed since the last save + /// + bool HasChanged { get; } + /// + /// Will be set with the filename (no extension) to save to. When saving, the implimentation should add the appropriate extension. Should error if set multiple times. + /// + string Filename { set; } + /// + /// Saves configuration to file. Should error if not a root object. + /// + void Save(); + /// + /// Loads the state of the file on disk. + /// + void Load(); + #endregion + } +} diff --git a/IPA.Loader/IniFile.cs b/IPA.Loader/Config/IniFile.cs similarity index 99% rename from IPA.Loader/IniFile.cs rename to IPA.Loader/Config/IniFile.cs index 94ff4f3f..c7926f05 100644 --- a/IPA.Loader/IniFile.cs +++ b/IPA.Loader/Config/IniFile.cs @@ -4,7 +4,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Text; -namespace IPA +namespace IPA.Config { /// /// Create a New INI file to store or load data diff --git a/IPA.Loader/ModPrefs.cs b/IPA.Loader/Config/ModPrefs.cs similarity index 99% rename from IPA.Loader/ModPrefs.cs rename to IPA.Loader/Config/ModPrefs.cs index b5a033d4..cdae6aac 100644 --- a/IPA.Loader/ModPrefs.cs +++ b/IPA.Loader/Config/ModPrefs.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Reflection; using System.Text; -namespace IPA +namespace IPA.Config { /// /// Allows to get and set preferences for your mod. diff --git a/IPA.Loader/IConfigProvider.cs b/IPA.Loader/IConfigProvider.cs deleted file mode 100644 index 9681c262..00000000 --- a/IPA.Loader/IConfigProvider.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IPA -{ - /// - /// An interface for configuration providers. - /// - public interface IConfigProvider - { - /// - /// Loads the data provided by this into an object of type . - /// - /// the type of the object to parse into - /// the values from the config provider parsed into the object - T Parse(); - /// - /// Stores the data from into the . - /// - /// - /// NOTE TO IMPLEMENTERS: - /// Since is bundled with this library, try to include support for its attributes. - /// - /// the type of - /// the object containing the data to save - void Store(T obj); - - #region Getters - /// - /// Gets the acting as a sub-object for a given key. - /// - /// the name of the field with the - /// an accessor for the selected sub-object - IConfigProvider GetSubObject(string name); - /// - /// Gets the value of type for key . - /// - /// - /// If is , behavior should be identical to . - /// - /// the type of the value to get - /// the name of the field to get the value of - /// the value of the field - T Get(string name); // can be IConfigProvider - /// - /// The non-generic version of . - /// If key corresponds to a sub-object, will return an . - /// - /// the name of the field to get the value of - /// the value of the field - object Get(string name); // can return IConfigProvider - /// - /// Gets the value of type of the element at . - /// - /// - /// If is , behavior should be identical to if it were executed on the immediate parent of the target element. - /// - /// the type of the value to get - /// an ordered array specifying keys starting from the root (this) - /// the value at path - T GetPath(params string[] path); - /// - /// The non-generic version of . - /// If key corresponds to a sub-object, will return an . - /// - /// an ordered array specifying keys starting from the root (this) - /// the value at path - object GetPath(params string[] path); - #endregion - - #region Setters - /// - /// Sets the object for key '' to . - /// - /// the key to set it as - /// the provider value - void SetSubObject(string name, IConfigProvider provider); // argument should be same provider type - /// - /// Sets the value for key to . - /// - /// - /// If is , behavior should be identical to . - /// - /// the type of the value to set - /// the key - /// the value to set - void Set(string name, T value); - /// - /// Sets the value for path to . - /// - /// - /// If is , behavior should be identical to if it were executed on the immediate parent of the target element. - /// - /// the type of the value to set - /// the path to the new value location - /// the value to set - void SetPath(string[] path, T value); - #endregion - } -} diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj index b8a4621d..07306013 100644 --- a/IPA.Loader/IPA.Loader.csproj +++ b/IPA.Loader/IPA.Loader.csproj @@ -55,19 +55,20 @@ - + + - + - + diff --git a/IPA.Loader/Loader/PluginManager.cs b/IPA.Loader/Loader/PluginManager.cs index a08bbf56..42dfed49 100644 --- a/IPA.Loader/Loader/PluginManager.cs +++ b/IPA.Loader/Loader/PluginManager.cs @@ -1,4 +1,6 @@ using IPA; +using IPA.Config; +using IPA.Config.ConfigProviders; using IPA.Logging; using IPA.Old; using IPA.Updating; @@ -75,7 +77,7 @@ namespace IPA.Loader } private static List _ipaPlugins = null; - + internal static List configProviders = new List(); private static void LoadPlugins() { @@ -205,9 +207,9 @@ namespace IPA.Loader if (@ref.FullName == "IllusionPlugin.IBeatSaberPlugin") @ref.Namespace = "IPA"; //@ref.Name = ""; if (@ref.FullName == "IllusionPlugin.IEnhancedBeatSaberPlugin") @ref.Namespace = "IPA"; //@ref.Name = ""; if (@ref.FullName == "IllusionPlugin.BeatSaber.ModsaberModInfo") @ref.Namespace = "IPA"; //@ref.Name = ""; - if (@ref.FullName == "IllusionPlugin.IniFile") @ref.Namespace = "IPA"; //@ref.Name = ""; - if (@ref.FullName == "IllusionPlugin.IModPrefs") @ref.Namespace = "IPA"; //@ref.Name = ""; - if (@ref.FullName == "IllusionPlugin.ModPrefs") @ref.Namespace = "IPA"; //@ref.Name = ""; + if (@ref.FullName == "IllusionPlugin.IniFile") @ref.Namespace = "IPA.Config"; //@ref.Name = ""; + if (@ref.FullName == "IllusionPlugin.IModPrefs") @ref.Namespace = "IPA.Config"; //@ref.Name = ""; + if (@ref.FullName == "IllusionPlugin.ModPrefs") @ref.Namespace = "IPA.Config"; //@ref.Name = ""; if (@ref.FullName == "IllusionPlugin.Utils.ReflectionUtil") @ref.Namespace = "IPA.Utilities"; //@ref.Name = ""; if (@ref.FullName == "IllusionPlugin.Logging.Logger") @ref.Namespace = "IPA.Logging"; //@ref.Name = ""; if (@ref.FullName == "IllusionPlugin.Logging.LogPrinter") @ref.Namespace = "IPA.Logging"; //@ref.Name = ""; @@ -258,6 +260,12 @@ namespace IPA.Loader if (modPrefs == null) modPrefs = new ModPrefs(bsPlugin); initArgs.Add(modPrefs); } + else if (ptype.IsAssignableFrom(typeof(IConfigProvider))) + { + var configProvider = new JsonConfigProvider() { Filename = Path.Combine("UserData", $"{bsPlugin.Name}.{param.Name}") }; + configProviders.Add(configProvider); + initArgs.Add(configProvider); + } else initArgs.Add(ptype.GetDefault()); } diff --git a/IPA.Loader/Logging/Logger.cs b/IPA.Loader/Logging/Logger.cs index 70bf4016..180b9f5a 100644 --- a/IPA.Loader/Logging/Logger.cs +++ b/IPA.Loader/Logging/Logger.cs @@ -24,6 +24,7 @@ namespace IPA.Logging internal static Logger updater => log.GetChildLogger("Updater"); internal static Logger libLoader => log.GetChildLogger("LibraryLoader"); internal static Logger loader => log.GetChildLogger("Loader"); + internal static Logger config => log.GetChildLogger("Config"); internal static bool LogCreated => _log != null || UnityLogInterceptor._logger != null; ///