1using Microsoft.Xna.Framework;
3using Newtonsoft.Json.Serialization;
5using System.Collections;
6using System.Collections.Generic;
9using System.Reflection;
11using Terraria.ModLoader.Config.UI;
13using Terraria.ModLoader.UI;
21 internal static readonly IDictionary<Mod, List<ModConfig>> Configs =
new Dictionary<Mod, List<ModConfig>>();
26 private static readonly IDictionary<Mod, List<ModConfig>>
LoadTimeConfigs =
new Dictionary<Mod, List<ModConfig>>();
30 Formatting = Formatting.Indented,
31 DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
32 ObjectCreationHandling = ObjectCreationHandling.Replace,
33 NullValueHandling = NullValueHandling.Ignore,
38 internal static readonly JsonSerializerSettings serializerSettingsCompact =
new JsonSerializerSettings
40 Formatting = Formatting.None,
41 DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
42 ObjectCreationHandling = ObjectCreationHandling.Replace,
43 NullValueHandling = NullValueHandling.Ignore,
45 ContractResolver = serializerSettings.ContractResolver
48 private static readonly IList<JsonConverter>
converters =
new List<JsonConverter>() {
49 new Newtonsoft.Json.Converters.VersionConverter(),
53 public static readonly
string ModConfigPath = Path.Combine(Main.SavePath,
"Mod Configs");
54 public static readonly
string ServerModConfigPath = Path.Combine(Main.SavePath,
"Mod Configs",
"Server");
56 internal static void Add(
ModConfig config)
60 List<ModConfig> configList;
61 if (!Configs.TryGetValue(config.
mod, out configList))
62 Configs.Add(config.
mod, configList =
new List<ModConfig>());
63 configList.Add(config);
65 FieldInfo instance = config.GetType().GetField(
"Instance", BindingFlags.Static | BindingFlags.Public);
66 if (instance !=
null) {
67 instance.SetValue(
null, config);
73 List<ModConfig> configList2;
80 internal static void LoadAll()
84 foreach (var activeConfig
in activeConfigs.Value)
91 internal static void OnChangedAll() {
92 foreach (var activeConfigs
in ConfigManager.Configs) {
93 foreach (var activeConfig
in activeConfigs.Value) {
94 activeConfig.OnChanged();
99 internal static void Load(ModConfig config)
101 string filename = config.mod.Name +
"_" + config.Name +
".json";
105 string netJson =
ModNet.pendingConfigs.Single(x => x.modname == config.mod.Name && x.configname == config.Name).json;
106 JsonConvert.PopulateObject(netJson, config, serializerSettingsCompact);
109 bool jsonFileExists = File.Exists(path);
110 string json = jsonFileExists ? File.ReadAllText(path) :
"{}";
114 catch (
Exception e) when (jsonFileExists && (e is JsonReaderException || e is JsonSerializationException)) {
115 Logging.tML.Warn($
"Then config file {config.Name} from the mod {config.mod.Name} located at {path} failed to load. The file was likely corrupted somehow, so the defaults will be loaded and the file deleted.");
121 internal static void Reset(ModConfig pendingConfig)
127 internal static void Save(ModConfig config)
130 string filename = config.mod.Name +
"_" + config.Name +
".json";
133 File.WriteAllText(path, json);
136 internal static void Unload()
138 serializerSettings.ContractResolver =
new ReferenceDefaultsPreservingResolver();
141 Configs.SelectMany(configList => configList.Value).ToList().ForEach(config => {
142 FieldInfo instance = config.GetType().GetField(
"Instance", BindingFlags.Static | BindingFlags.Public);
143 if (instance !=
null) {
144 instance.SetValue(
null,
null);
150 Interface.modConfig.Unload();
151 Interface.modConfigList.Unload();
154 internal static bool AnyModNeedsReload() =>
ModLoader.
Mods.Any(ModNeedsReload);
156 internal static bool ModNeedsReload(
Mod mod)
158 if (Configs.ContainsKey(mod))
160 var configs = Configs[mod];
162 for (
int i = 0; i < configs.Count; i++)
164 if (loadTimeConfigs[i].NeedsReload(configs[i]))
174 internal static ModConfig GetConfig(
ModNet.
NetConfig netConfig) => ConfigManager.GetConfig(
ModLoader.
GetMod(netConfig.modname), netConfig.configname);
175 internal static ModConfig GetConfig(
Mod mod,
string config)
177 List<ModConfig> configs;
178 if (Configs.TryGetValue(mod, out configs))
180 return configs.Single(x => x.Name == config);
185 internal static ModConfig GetLoadTimeConfig(
Mod mod,
string config) {
186 List<ModConfig> configs;
188 return configs.Single(x => x.Name == config);
193 internal static void HandleInGameChangeConfigPacket(
BinaryReader reader,
int whoAmI)
195 if (Main.netMode == NetmodeID.MultiplayerClient)
197 bool success = reader.ReadBoolean();
198 string message = reader.ReadString();
201 string modname = reader.ReadString();
202 string configname = reader.ReadString();
203 string json = reader.ReadString();
204 ModConfig activeConfig = GetConfig(
ModLoader.
GetMod(modname), configname);
205 JsonConvert.PopulateObject(json, activeConfig, serializerSettingsCompact);
206 activeConfig.OnChanged();
208 Main.NewText($
"Shared config changed: Message: {message}, Mod: {modname}, Config: {configname}");
209 if (Main.InGameUI.CurrentState == Interface.modConfig)
211 Main.InGameUI.SetState(Interface.modConfig);
212 Interface.modConfig.SetMessage(
"Server response: " + message, Color.Green);
220 Main.NewText(
"Changes Rejected: " + message);
221 if (Main.InGameUI.CurrentState == Interface.modConfig)
223 Interface.modConfig.SetMessage(
"Server rejected changes: " + message, Color.Red);
232 string modname = reader.ReadString();
233 string configname = reader.ReadString();
234 string json = reader.ReadString();
236 ModConfig loadTimeConfig = GetLoadTimeConfig(
ModLoader.
GetMod(modname), configname);
238 JsonConvert.PopulateObject(json, pendingConfig, serializerSettingsCompact);
240 string message =
"Accepted";
241 if (loadTimeConfig.NeedsReload(pendingConfig))
244 message =
"Can't save because changes would require a reload.";
246 if (!config.AcceptClientChanges(pendingConfig, whoAmI, ref message))
253 ConfigManager.Save(pendingConfig);
254 JsonConvert.PopulateObject(json, config, ConfigManager.serializerSettingsCompact);
257 var p =
new ModPacket(MessageID.InGameChangeConfig);
268 var p =
new ModPacket(MessageID.InGameChangeConfig);
280 PropertyInfo[] properties = item.GetType().GetProperties(
282 BindingFlags.Public |
283 BindingFlags.Instance);
285 FieldInfo[] fields = item.GetType().GetFields(
287 BindingFlags.Public |
288 BindingFlags.Instance);
290 return fields.Select(x =>
new PropertyFieldWrapper(x)).Concat(properties.Select(x =>
new PropertyFieldWrapper(x)));
302 if (type == typeof(
string))
304 return Activator.CreateInstance(type);
311 T attribute = (T)
Attribute.GetCustomAttribute(memberInfo.Type, typeof(T),
true);
318 attribute = (T)
Attribute.GetCustomAttribute(memberInfo.MemberInfo, typeof(T)) ?? attribute;
325 T attribute = (T)
Attribute.GetCustomAttribute(memberInfo.Type, typeof(T),
true);
327 attribute = (T)
Attribute.GetCustomAttribute(type, typeof(T),
true) ?? attribute;
330 attribute = (T)
Attribute.GetCustomAttribute(memberInfo.MemberInfo, typeof(T)) ?? attribute;
334 public static Tuple<UIElement, UIElement>
WrapIt(UIElement parent, ref
int top, PropertyFieldWrapper memberInfo,
object item,
int order,
object list =
null, Type arrayType =
null,
int index = -1)
337 return UIModConfig.WrapIt(parent, ref top, memberInfo, item, order, list, arrayType, index);
342 Interface.modConfig.SetPendingChanges(changes);
347 if (ReferenceEquals(a, b))
return true;
348 if (a ==
null || b ==
null)
return false;
355 IEnumerator enumeratorA = a.GetEnumerator();
356 IEnumerator enumeratorB = b.GetEnumerator();
357 bool hasNextA = enumeratorA.MoveNext();
358 bool hasNextB = enumeratorB.MoveNext();
359 while (hasNextA && hasNextB) {
360 if (!
ObjectEquals(enumeratorA.Current, enumeratorB.Current))
return false;
361 hasNextA = enumeratorA.MoveNext();
362 hasNextB = enumeratorB.MoveNext();
364 return !hasNextA && !hasNextB;
382 throw new ArgumentNullException();
403 public override void SetValue(
object target,
object value) {
409 protected override IList<JsonProperty>
CreateProperties(Type type, MemberSerialization memberSerialization) {
410 IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
412 ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
414 object referenceInstance = ctor.Invoke(
null);
415 foreach (JsonProperty prop
in props.Where(p => p.Readable)) {
416 if (!prop.PropertyType.IsValueType) {
417 var a = type.GetMember(prop.PropertyName);
419 if (prop.PropertyType.GetConstructor(Type.EmptyTypes) !=
null) {
421 Func<object> defaultValueCreator = () => prop.ValueProvider.GetValue(ctor.Invoke(
null));
424 else if (prop.PropertyType.IsArray) {
425 Func<object> defaultValueCreator = () => (prop.ValueProvider.GetValue(referenceInstance) as Array).Clone();
429 if (prop.ShouldSerialize ==
null)
430 prop.ShouldSerialize = instance =>
432 object val = prop.ValueProvider.GetValue(instance);
433 object refVal = prop.ValueProvider.GetValue(referenceInstance);
static object AlternateCreateInstance(Type type)
static readonly string ServerModConfigPath
static ModConfig GeneratePopulatedClone(ModConfig original)
static void SetPendingChanges(bool changes=true)
static readonly IDictionary< Mod, List< ModConfig > > LoadTimeConfigs
static readonly string ModConfigPath
static bool ObjectEquals(object a, object b)
static bool EnumerableEquals(IEnumerable a, IEnumerable b)
static IEnumerable< PropertyFieldWrapper > GetFieldsAndProperties(object item)
static readonly JsonSerializerSettings serializerSettings
static readonly IList< JsonConverter > converters
static T GetCustomAttribute< T >(PropertyFieldWrapper memberInfo, object item, object array)
static Tuple< UIElement, UIElement > WrapIt(UIElement parent, ref int top, PropertyFieldWrapper memberInfo, object item, int order, object list=null, Type arrayType=null, int index=-1)
ModConfig provides a way for mods to be configurable. ModConfigs can either be Client specific or Ser...
virtual ModConfig Clone()
tModLoader will call Clone on ModConfig to facilitate proper implementation of the ModConfig user int...
virtual void OnChanged()
This hook is called anytime new config values have been set and are ready to take effect....
virtual void OnLoaded()
This method is called when the ModConfig has been loaded for the first time. This happens before regu...
override void SetValue(object target, object value)
NullToDefaultValueProvider(IValueProvider baseProvider, Func< object > defaultValueGenerator)
readonly Func< object > defaultValueGenerator
readonly IValueProvider baseProvider
virtual object GetValue(object target)
ValueProviderDecorator(IValueProvider baseProvider)
virtual void SetValue(object target, object value)
Custom ContractResolver for facilitating refernce type defaults. The ShouldSerialize code enables unc...
override IList< JsonProperty > CreateProperties(Type type, MemberSerialization memberSerialization)
Mod is an abstract class that you will override. It serves as a central place from which the mod's co...
virtual string Name
Stores the name of the mod. This name serves as the mod's identification, and also helps with saving ...
This serves as the central class which loads mods. It contains many static fields and methods related...
static Mod GetMod(string name)
Gets the instance of the Mod with the specified name.
This class inherits from BinaryWriter. This means that you can use all of its writing functions to se...
ConfigScope
Each ModConfig class has a different scope. Failure to use the correct mode will lead to bugs.