diff --git a/BSIPA-ModList/UI/ViewControllers/SettingsViewController.cs b/BSIPA-ModList/UI/ViewControllers/SettingsViewController.cs index 89c20d1e..ed45bfa6 100644 --- a/BSIPA-ModList/UI/ViewControllers/SettingsViewController.cs +++ b/BSIPA-ModList/UI/ViewControllers/SettingsViewController.cs @@ -20,19 +20,19 @@ namespace BSIPA_ModList.UI showEnableDisable = menu.AddBool("Show Enable/Disable Button", "If enabled, BSIPA mods will have a button to enable or disable them."); autoCheck.applyImmediately = true; - autoCheck.GetValue += () => IPA.Config.SelfConfig.SelfConfigRef.Value.Updates.AutoCheckUpdates; + autoCheck.GetValue += () => IPA.Config.SelfConfig.Instance.Value.Updates.AutoCheckUpdates; autoCheck.SetValue += val => { - IPA.Config.SelfConfig.SelfConfigRef.Value.Updates.AutoCheckUpdates = val; - IPA.Config.SelfConfig.LoaderConfig.Store(IPA.Config.SelfConfig.SelfConfigRef.Value); + IPA.Config.SelfConfig.Instance.Value.Updates.AutoCheckUpdates = val; + IPA.Config.SelfConfig.LoaderConfig.Store(IPA.Config.SelfConfig.Instance.Value); }; autoUpdate.applyImmediately = true; - autoUpdate.GetValue += () => IPA.Config.SelfConfig.SelfConfigRef.Value.Updates.AutoUpdate; + autoUpdate.GetValue += () => IPA.Config.SelfConfig.Instance.Value.Updates.AutoUpdate; autoUpdate.SetValue += val => { - IPA.Config.SelfConfig.SelfConfigRef.Value.Updates.AutoUpdate = val; - IPA.Config.SelfConfig.LoaderConfig.Store(IPA.Config.SelfConfig.SelfConfigRef.Value); + IPA.Config.SelfConfig.Instance.Value.Updates.AutoUpdate = val; + IPA.Config.SelfConfig.LoaderConfig.Store(IPA.Config.SelfConfig.Instance.Value); }; showEnableDisable.applyImmediately = true; diff --git a/IPA.Loader/Config/Config.cs b/IPA.Loader/Config/Config.cs index fa83b504..fef8394e 100644 --- a/IPA.Loader/Config/Config.cs +++ b/IPA.Loader/Config/Config.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using IPA.Config.Providers; using IPA.Utilities; #if NET3 @@ -116,7 +117,7 @@ namespace IPA.Config return config; } - internal static Config GetProviderFor(string modName, ParameterInfo info) + internal static Config GetConfigFor(string modName, ParameterInfo info) { var prefs = Array.Empty(); if (info.GetCustomAttribute() is PreferAttribute prefer) @@ -151,6 +152,16 @@ namespace IPA.Config Store = store; } + /// + /// Forces a synchronous load from disk. + /// + public void LoadSync() => LoadAsync().Wait(); + + /// + /// Forces an asynchronous load from disk. + /// + public Task LoadAsync() => ConfigRuntime.TriggerFileLoad(this); + private Config(string name, IConfigProvider provider, FileInfo file) { Name = name; Provider = provider; File = file; diff --git a/IPA.Loader/Config/ConfigRuntime.cs b/IPA.Loader/Config/ConfigRuntime.cs index 9e9a8647..a7b8c1d3 100644 --- a/IPA.Loader/Config/ConfigRuntime.cs +++ b/IPA.Loader/Config/ConfigRuntime.cs @@ -114,6 +114,42 @@ namespace IPA.Config public static Task TriggerFileLoad(Config config) => loadFactory.StartNew(() => LoadTask(config)); + public static Task TriggerLoadAll() + => Task.WhenAll(configs.Select(TriggerFileLoad)); + + /// + /// this is synchronous, unlike + /// + /// + public static void Save(Config config) + { + var store = config.Store; + + try + { + using var readLock = Synchronization.LockRead(store.WriteSyncObject); + lock (config.Provider) + { + config.Provider.File = config.File; + store.WriteTo(config.Provider); + } + } + catch (Exception e) + { + Logger.config.Error($"{nameof(IConfigStore)} for {config.File} errored while writing to disk"); + Logger.config.Error(e); + } + } + + /// + /// this is synchronous, unlike + /// + public static void SaveAll() + { + foreach (var config in configs) + Save(config); + } + private static void LoadTask(Config config) { // these tasks will always be running in the same thread as each other try @@ -149,23 +185,7 @@ namespace IPA.Config } // otherwise, we have a thing that changed in a store - var config = configArr[index - 1]; - var store = config.Store; - - try - { - using var readLock = Synchronization.LockRead(store.WriteSyncObject); - lock (config.Provider) - { - config.Provider.File = config.File; - store.WriteTo(config.Provider); - } - } - catch (Exception e) - { - Logger.config.Error($"{nameof(IConfigStore)} for {config.File} errored while writing to disk"); - Logger.config.Error(e); - } + Save(configArr[index - 1]); } } } diff --git a/IPA.Loader/Config/SelfConfig.cs b/IPA.Loader/Config/SelfConfig.cs index ff79205a..5e9a7cf1 100644 --- a/IPA.Loader/Config/SelfConfig.cs +++ b/IPA.Loader/Config/SelfConfig.cs @@ -1,6 +1,7 @@ // BEGIN: section ignore using IPA.Logging; using IPA.Utilities; +using IPA.Config.Stores; // END: section ignore using Newtonsoft.Json; @@ -11,31 +12,14 @@ namespace IPA.Config // This is to allow the doc generation to parse this file and use Newtonsoft to generate a JSON schema // BEGIN: section ignore - private static IConfigProvider _loaderConfig; + public static Config LoaderConfig { get; set; } - public static IConfigProvider LoaderConfig - { - get => _loaderConfig; - set - { - _loaderConfig?.RemoveLinks(); - value.Load(); - SelfConfigRef = value.MakeLink((c, v) => - { - if (v.Value.Regenerate) - c.Store(v.Value = new SelfConfig { Regenerate = false }); - - StandardLogger.Configure(v.Value); - }); - _loaderConfig = value; - } - } - - public static Ref SelfConfigRef = new SelfConfig(); + public static SelfConfig Instance = new SelfConfig(); public static void Load() { LoaderConfig = Config.GetConfigFor(IPAName, "json"); + Instance = LoaderConfig.Generated(); } public static void ReadCommandLine(string[] args) @@ -81,12 +65,12 @@ namespace IPA.Config { public bool AutoUpdate = true; // LINE: ignore 2 - public static bool AutoUpdate_ => SelfConfigRef.Value.Updates.AutoUpdate + public static bool AutoUpdate_ => Instance.Updates.AutoUpdate && CommandLineValues.Updates.AutoUpdate; public bool AutoCheckUpdates = true; // LINE: ignore 2 - public static bool AutoCheckUpdates_ => SelfConfigRef.Value.Updates.AutoCheckUpdates + public static bool AutoCheckUpdates_ => Instance.Updates.AutoCheckUpdates && CommandLineValues.Updates.AutoCheckUpdates; } @@ -96,35 +80,35 @@ namespace IPA.Config { public bool ShowCallSource = false; // LINE: ignore 2 - public static bool ShowCallSource_ => SelfConfigRef.Value.Debug.ShowCallSource + public static bool ShowCallSource_ => Instance.Debug.ShowCallSource || CommandLineValues.Debug.ShowCallSource; public bool ShowDebug = false; // LINE: ignore 2 - public static bool ShowDebug_ => SelfConfigRef.Value.Debug.ShowDebug + public static bool ShowDebug_ => Instance.Debug.ShowDebug || CommandLineValues.Debug.ShowDebug; // This option only takes effect after a full game restart, unless new logs are created again public bool CondenseModLogs = false; // LINE: ignore 2 - public static bool CondenseModLogs_ => SelfConfigRef?.Value.Debug.CondenseModLogs ?? false + public static bool CondenseModLogs_ => Instance?.Debug.CondenseModLogs ?? false || CommandLineValues.Debug.CondenseModLogs; public bool ShowHandledErrorStackTraces = false; // LINE: ignore - public static bool ShowHandledErrorStackTraces_ => SelfConfigRef.Value.Debug.ShowHandledErrorStackTraces; + public static bool ShowHandledErrorStackTraces_ => Instance.Debug.ShowHandledErrorStackTraces; public bool HideMessagesForPerformance = true; // LINE: ignore - public static bool HideMessagesForPerformance_ => SelfConfigRef.Value.Debug.HideMessagesForPerformance; + public static bool HideMessagesForPerformance_ => Instance.Debug.HideMessagesForPerformance; public int HideLogThreshold = 512; // LINE: ignore - public static int HideLogThreshold_ => SelfConfigRef.Value.Debug.HideLogThreshold; + public static int HideLogThreshold_ => Instance.Debug.HideLogThreshold; public bool ShowTrace = false; // LINE: ignore 2 - public static bool ShowTrace_ => SelfConfigRef.Value.Debug.ShowTrace + public static bool ShowTrace_ => Instance.Debug.ShowTrace || CommandLineValues.Debug.ShowTrace; } @@ -132,12 +116,12 @@ namespace IPA.Config public bool YeetMods = true; // LINE: ignore 2 - public static bool YeetMods_ => SelfConfigRef.Value.YeetMods + public static bool YeetMods_ => Instance.YeetMods && CommandLineValues.YeetMods; [JsonProperty(Required = Required.Default)] public string LastGameVersion = null; // LINE: ignore - public static string LastGameVersion_ => SelfConfigRef.Value.LastGameVersion; + public static string LastGameVersion_ => Instance.LastGameVersion; } } \ No newline at end of file diff --git a/IPA.Loader/Config/Stores/GeneratedStore.cs b/IPA.Loader/Config/Stores/GeneratedStore.cs index 58091c26..8487dbb6 100644 --- a/IPA.Loader/Config/Stores/GeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStore.cs @@ -24,7 +24,8 @@ namespace IPA.Config.Stores { /// /// Creates a generated of type , registers it to - /// the object, and returns it. + /// the object, and returns it. This also forces a synchronous config load via + /// if is . /// /// /// @@ -36,11 +37,17 @@ namespace IPA.Config.Stores /// /// the type to wrap /// the to register to + /// whether to synchronously load the content, or trigger an async load /// a generated instance of as a special - public static T Generated(this Config cfg) where T : class + public static T Generated(this Config cfg, bool loadSync = true) where T : class { var ret = GeneratedStore.Create(); cfg.SetStore(ret as IConfigStore); + if (loadSync) + cfg.LoadSync(); + else + cfg.LoadAsync(); + return ret; } } diff --git a/IPA.Loader/Loader/DisabledConfig.cs b/IPA.Loader/Loader/DisabledConfig.cs index 8be87292..dff0e16c 100644 --- a/IPA.Loader/Loader/DisabledConfig.cs +++ b/IPA.Loader/Loader/DisabledConfig.cs @@ -1,4 +1,5 @@ using IPA.Config; +using IPA.Config.Stores; using IPA.Utilities; using System; using System.Collections.Generic; @@ -10,29 +11,14 @@ namespace IPA.Loader { internal class DisabledConfig { - private static IConfigProvider _provider; + public static Config.Config Disabled { get; set; } - public static IConfigProvider Provider - { - get => _provider; - set - { - _provider?.RemoveLinks(); - value.Load(); - Ref = value.MakeLink((c, v) => - { - if (v.Value.Reset) - c.Store(v.Value = new DisabledConfig { Reset = false }); - }); - _provider = value; - } - } - - public static Ref Ref; + public static DisabledConfig Instance; public static void Load() { - Provider = Config.Config.GetConfigFor("Disabled Mods", "json"); + Disabled = Config.Config.GetConfigFor("Disabled Mods", "json"); + Instance = Disabled.Generated(); } public bool Reset = true; diff --git a/IPA.Loader/Loader/PluginComponent.cs b/IPA.Loader/Loader/PluginComponent.cs index 4663468a..92bfe8b9 100644 --- a/IPA.Loader/Loader/PluginComponent.cs +++ b/IPA.Loader/Loader/PluginComponent.cs @@ -39,24 +39,18 @@ namespace IPA.Loader SceneManager.activeSceneChanged += OnActiveSceneChanged; SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; - - Config.Config.Save(); } void Update() { bsPlugins.OnUpdate(); ipaPlugins.OnUpdate(); - - Config.Config.Update(); } void LateUpdate() { bsPlugins.OnLateUpdate(); ipaPlugins.OnLateUpdate(); - - //Config.Config.Update(); } void FixedUpdate() @@ -82,7 +76,7 @@ namespace IPA.Loader bsPlugins.OnApplicationQuit(); ipaPlugins.OnApplicationQuit(); - Config.Config.Save(); + ConfigRuntime.SaveAll(); quitting = true; } diff --git a/IPA.Loader/Loader/PluginInitInjector.cs b/IPA.Loader/Loader/PluginInitInjector.cs index c3dda102..33ed4162 100644 --- a/IPA.Loader/Loader/PluginInitInjector.cs +++ b/IPA.Loader/Loader/PluginInitInjector.cs @@ -68,12 +68,10 @@ namespace IPA.Loader new TypedInjector(typeof(IModPrefs), (prev, param, meta) => prev ?? new ModPrefs(meta)), #pragma warning restore CS0618 // Type or member is obsolete new TypedInjector(typeof(PluginLoader.PluginMetadata), (prev, param, meta) => prev ?? meta), - new TypedInjector(typeof(IConfigProvider), (prev, param, meta) => + new TypedInjector(typeof(Config.Config), (prev, param, meta) => { if (prev != null) return prev; - var cfgProvider = Config.Config.GetProviderFor(meta.Name, param); - cfgProvider.Load(); - return cfgProvider; + return Config.Config.GetConfigFor(meta.Name, param); }) }; diff --git a/IPA.Loader/Loader/PluginLoader.cs b/IPA.Loader/Loader/PluginLoader.cs index 884133e1..3db18798 100644 --- a/IPA.Loader/Loader/PluginLoader.cs +++ b/IPA.Loader/Loader/PluginLoader.cs @@ -154,8 +154,7 @@ namespace IPA.Loader Directory.CreateDirectory(pluginDir); } - SelfConfig.SelfConfigRef.Value.LastGameVersion = gameVer.ToString(); - SelfConfig.LoaderConfig.Store(SelfConfig.SelfConfigRef.Value); + SelfConfig.Instance.LastGameVersion = gameVer.ToString(); } internal static List PluginsMetadata = new List(); @@ -409,7 +408,7 @@ namespace IPA.Loader { var enabled = new List(PluginsMetadata.Count); - var disabled = DisabledConfig.Ref.Value.DisabledModIds; + var disabled = DisabledConfig.Instance.DisabledModIds; foreach (var meta in PluginsMetadata) { if (disabled.Contains(meta.Id ?? meta.Name)) @@ -523,7 +522,7 @@ namespace IPA.Loader else if (disable) { DisabledPlugins.Add(meta); - DisabledConfig.Ref.Value.DisabledModIds.Add(meta.Id ?? meta.Name); + DisabledConfig.Instance.DisabledModIds.Add(meta.Id ?? meta.Name); } else ignoredPlugins.Add(meta); diff --git a/IPA.Loader/Loader/PluginManager.cs b/IPA.Loader/Loader/PluginManager.cs index 13cd23fa..f186e863 100644 --- a/IPA.Loader/Loader/PluginManager.cs +++ b/IPA.Loader/Loader/PluginManager.cs @@ -105,8 +105,7 @@ namespace IPA.Loader var dependents = BSMetas.Where(m => m.Metadata.Dependencies.Contains(plugin.Metadata)).ToList(); needsRestart = dependents.Aggregate(needsRestart, (b, p) => DisablePlugin(p) || b); - DisabledConfig.Ref.Value.DisabledModIds.Add(plugin.Metadata.Id ?? plugin.Metadata.Name); - DisabledConfig.Provider.Store(DisabledConfig.Ref.Value); + DisabledConfig.Instance.DisabledModIds.Add(plugin.Metadata.Id ?? plugin.Metadata.Name); if (!needsRestart && plugin.Plugin is IDisablablePlugin disable) { @@ -167,8 +166,7 @@ namespace IPA.Loader Logger.loader.Info($"Enabling {plugin.Name}"); - DisabledConfig.Ref.Value.DisabledModIds.Remove(plugin.Id ?? plugin.Name); - DisabledConfig.Provider.Store(DisabledConfig.Ref.Value); + DisabledConfig.Instance.DisabledModIds.Remove(plugin.Id ?? plugin.Name); var needsRestart = true; diff --git a/IPA.Loader/Logging/StandardLogger.cs b/IPA.Loader/Logging/StandardLogger.cs index e6ec19bc..3ff25955 100644 --- a/IPA.Loader/Logging/StandardLogger.cs +++ b/IPA.Loader/Logging/StandardLogger.cs @@ -305,7 +305,7 @@ namespace IPA.Logging } } - var debugConfig = SelfConfig.SelfConfigRef?.Value?.Debug; + var debugConfig = SelfConfig.Instance?.Debug; if (debugConfig != null && debugConfig.HideMessagesForPerformance && logQueue.Count > debugConfig.HideLogThreshold)