ConfigEntry.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using BepInEx.Logging;
  5. namespace BepInEx.Configuration
  6. {
  7. public sealed class ConfigEntry
  8. {
  9. internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition, Type settingType, object defaultValue) : this(configFile, definition)
  10. {
  11. SetTypeAndDefaultValue(settingType, defaultValue, true);
  12. }
  13. internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition)
  14. {
  15. ConfigFile = configFile ?? throw new ArgumentNullException(nameof(configFile));
  16. Definition = definition ?? throw new ArgumentNullException(nameof(definition));
  17. }
  18. internal void SetTypeAndDefaultValue(Type settingType, object defaultValue, bool uniqueDefaultValue)
  19. {
  20. if (settingType == null) throw new ArgumentNullException(nameof(settingType));
  21. if (settingType == SettingType)
  22. {
  23. if (uniqueDefaultValue)
  24. DefaultValue = defaultValue;
  25. return;
  26. }
  27. if (SettingType != null)
  28. {
  29. throw new ArgumentException($"Tried to define setting \"{Definition}\" as type {settingType.Name} " +
  30. $"while it was already defined as type {SettingType.Name}. Use the same " +
  31. $"Type for all Wrappers of a single setting.");
  32. }
  33. if (defaultValue == null && settingType.IsValueType)
  34. throw new ArgumentException("defaultValue is null while settingType is a value type");
  35. if (defaultValue != null && !settingType.IsInstanceOfType(defaultValue))
  36. throw new ArgumentException("defaultValue can not be assigned to type " + settingType.Name);
  37. SettingType = settingType;
  38. DefaultValue = defaultValue;
  39. }
  40. private object _convertedValue;
  41. private string _serializedValue;
  42. public ConfigFile ConfigFile { get; }
  43. public ConfigDefinition Definition { get; }
  44. public ConfigDescription Description { get; internal set; }
  45. public Type SettingType { get; private set; }
  46. public object DefaultValue { get; private set; }
  47. /// <summary>
  48. /// Is the type of this setting defined, and by extension can <see cref="Value"/> of this setting be accessed.
  49. /// Setting is defined when any <see cref="ConfigWrapper{T}"/> objects reference it.
  50. /// </summary>
  51. public bool IsDefined => SettingType != null;
  52. /// <summary>
  53. /// Can't be used when <see cref="IsDefined"/> is false.
  54. /// </summary>
  55. public object Value
  56. {
  57. get
  58. {
  59. ProcessSerializedValue();
  60. return _convertedValue;
  61. }
  62. set => SetValue(value, true, this);
  63. }
  64. internal void SetValue(object newValue, bool fireEvent, object sender)
  65. {
  66. bool wasChanged = ProcessSerializedValue();
  67. wasChanged = wasChanged || !Equals(newValue, _convertedValue);
  68. if (wasChanged)
  69. {
  70. _convertedValue = newValue;
  71. if (fireEvent)
  72. OnSettingChanged(sender);
  73. }
  74. }
  75. public string GetSerializedValue()
  76. {
  77. if (_serializedValue != null)
  78. return _serializedValue;
  79. if (!IsDefined)
  80. return null;
  81. return TomlTypeConverter.ConvertToString(Value, SettingType);
  82. }
  83. public void SetSerializedValue(string newValue, bool fireEvent, object sender)
  84. {
  85. string current = GetSerializedValue();
  86. if (string.Equals(current, newValue)) return;
  87. _serializedValue = newValue;
  88. if (!IsDefined) return;
  89. if (ProcessSerializedValue())
  90. {
  91. if (fireEvent)
  92. OnSettingChanged(sender);
  93. }
  94. }
  95. private bool ProcessSerializedValue()
  96. {
  97. if (!IsDefined)
  98. throw new InvalidOperationException("Can't get the value before the SettingType is specified");
  99. if (_serializedValue != null)
  100. {
  101. string value = _serializedValue;
  102. _serializedValue = null;
  103. if (value != "")
  104. {
  105. try
  106. {
  107. var newValue = TomlTypeConverter.ConvertToValue(value, SettingType);
  108. if (!Equals(newValue, _convertedValue))
  109. {
  110. _convertedValue = newValue;
  111. return true;
  112. }
  113. return false;
  114. }
  115. catch (Exception e)
  116. {
  117. Logger.Log(LogLevel.Warning, $"Config value of setting \"{Definition}\" could not be " +
  118. $"parsed and will be ignored. Reason: {e.Message}; Value: {value}");
  119. }
  120. }
  121. }
  122. if (_convertedValue == null && DefaultValue != null)
  123. {
  124. _convertedValue = DefaultValue;
  125. return true;
  126. }
  127. return false;
  128. }
  129. private void OnSettingChanged(object sender)
  130. {
  131. ConfigFile.OnSettingChanged(sender, this);
  132. }
  133. public void WriteDescription(StreamWriter writer)
  134. {
  135. if (Description != null)
  136. writer.WriteLine(Description.ToSerializedString());
  137. if (SettingType != null)
  138. {
  139. writer.WriteLine("# Setting type: " + SettingType.Name);
  140. writer.WriteLine("# Default value: " + DefaultValue);
  141. // todo acceptable values
  142. if (SettingType.IsEnum && SettingType.GetCustomAttributes(typeof(FlagsAttribute), true).Any())
  143. writer.WriteLine("# Multiple values can be set at the same time by separating them with , (e.g. Debug, Warning)");
  144. }
  145. }
  146. }
  147. }