diff --git a/JotunnLib/Extensions/ConfigFileExtensions.cs b/JotunnLib/Extensions/ConfigFileExtensions.cs new file mode 100644 index 000000000..885688332 --- /dev/null +++ b/JotunnLib/Extensions/ConfigFileExtensions.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using BepInEx.Configuration; + +namespace Jotunn.Extensions +{ + /// + /// Extends ConfigFile with a convenience method to bind config entries with less boilerplate code + /// and explicitly expose commonly used configuration manager attributes. + /// + public static class ConfigFileExtensions + { + internal class ConfigFileOrderingMaps + { + public Dictionary sectionToSectionNumber = new Dictionary(); + public Dictionary sectionToSettingOrder = new Dictionary(); + } + private static readonly Dictionary _configFileOrderingMaps = new Dictionary(); + + /// + /// Formats section name as "{sectionNumber} - {section}" based on how + /// many sections have been bound to this config. + /// + /// + /// + private static string GetOrderedSectionName(this ConfigFile configFile, string section) + { + if (!_configFileOrderingMaps.TryGetValue(configFile.ConfigFilePath, out ConfigFileOrderingMaps orderingMaps)) + { + orderingMaps = new ConfigFileOrderingMaps(); + _configFileOrderingMaps.Add(configFile.ConfigFilePath, orderingMaps); + } + + if (!orderingMaps.sectionToSectionNumber.TryGetValue(section, out int number)) + { + number = orderingMaps.sectionToSectionNumber.Count + 1; + orderingMaps.sectionToSectionNumber[section] = number; + } + + return $"{number} - {section}"; + } + + /// + /// Orders settings within a section. + /// + /// + /// + private static int GetSettingOrder(this ConfigFile configFile, string section) + { + if (!_configFileOrderingMaps.TryGetValue(configFile.ConfigFilePath, out ConfigFileOrderingMaps orderingMaps)) + { + orderingMaps = new ConfigFileOrderingMaps(); + _configFileOrderingMaps.Add(configFile.ConfigFilePath, orderingMaps); + } + + if (!orderingMaps.sectionToSettingOrder.TryGetValue(section, out int order)) + { + order = 0; + } + + orderingMaps.sectionToSettingOrder[section] = order - 1; + return order; + } + + internal static string GetExtendedDescription(string description, bool synchronizedSetting) + { + return description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"); + } + + /// + /// Bind a new config entry to the config file and modify description to state whether the config entry is synced or not. + /// + /// Type of the value the config entry holds. + /// Configuration file to bind the config entry to. + /// Configuration file section to list the config entry in. Settings are grouped by this. + /// Name of the setting. + /// Default value of the config entry. + /// Plain text description of the config entry to display as hover text in configuration manager. + /// Whether the config entry IsAdminOnly and should be synced with server. + /// Whether to number the section names using a prefix based on the order they are bound to the config file. + /// Whether to order the settings in each section based on the order they are bound to the config file. + /// Acceptable values for config entry as an AcceptableValueRange, AcceptableValueList, or custom subclass. + /// Custom setting editor (OnGUI code that replaces the default editor provided by ConfigurationManager). + /// Config manager attributes for additional user specified functionality. Any fields of BindConfig will overwrite properties in configAttributes. + /// ConfigEntry bound to the config file. + public static ConfigEntry BindConfigInOrder( + this ConfigFile configFile, + string section, + string key, + T defaultValue, + string description, + bool synced = true, + bool sectionOrder = true, + bool settingOrder = true, + AcceptableValueBase acceptableValues = null, + Action customDrawer = null, + ConfigurationManagerAttributes configAttributes = null + ) + { + section = sectionOrder ? configFile.GetOrderedSectionName(section) : section; + int order = settingOrder ? configFile.GetSettingOrder(section) : 0; + return configFile.BindConfig(section, key, defaultValue, description, synced, order, acceptableValues, customDrawer, configAttributes); + } + + /// + /// Bind a new config entry to the config file and modify description to state whether the config entry is synced or not. + /// + /// Type of the value the config entry holds. + /// Configuration file to bind the config entry to. + /// Configuration file section to list the config entry in. Settings are grouped by this. + /// Name of the setting. + /// Default value of the config entry. + /// Plain text description of the config entry to display as hover text in configuration manager. + /// Whether the config entry IsAdminOnly and should be synced with server. + /// Order of the setting on the settings list relative to other settings in a category. 0 by default, higher number is higher on the list. + /// Acceptable values for config entry as an AcceptableValueRange, AcceptableValueList, or custom subclass. + /// Custom setting editor (OnGUI code that replaces the default editor provided by ConfigurationManager). + /// Config manager attributes for additional user specified functionality. Any fields of BindConfig will overwrite properties in configAttributes. + /// ConfigEntry bound to the config file. + public static ConfigEntry BindConfig( + this ConfigFile configFile, + string section, + string key, + T defaultValue, + string description, + bool synced = true, + int? order = null, + AcceptableValueBase acceptableValues = null, + Action customDrawer = null, + ConfigurationManagerAttributes configAttributes = null + ) + { + string extendedDescription = GetExtendedDescription(description, synced); + + configAttributes ??= new ConfigurationManagerAttributes(); + configAttributes.IsAdminOnly = synced; + configAttributes.Order = order; + configAttributes.CustomDrawer = customDrawer; + + ConfigEntry configEntry = configFile.Bind( + section, + key, + defaultValue, + new ConfigDescription(extendedDescription, acceptableValues, configAttributes) + ); + + return configEntry; + } + } +}