1 using Microsoft.Xna.Framework;
3 using Newtonsoft.Json.Serialization;
6 using System.Collections.Generic;
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>>();
28 public static readonly JsonSerializerSettings serializerSettings =
new JsonSerializerSettings
30 Formatting = Formatting.Indented,
31 DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
32 ObjectCreationHandling = ObjectCreationHandling.Replace,
33 NullValueHandling = NullValueHandling.Ignore,
34 Converters = converters,
38 internal static readonly JsonSerializerSettings serializerSettingsCompact =
new JsonSerializerSettings
40 Formatting = Formatting.None,
41 DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
42 ObjectCreationHandling = ObjectCreationHandling.Replace,
43 NullValueHandling = NullValueHandling.Ignore,
44 Converters = converters,
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;
74 if (!LoadTimeConfigs.TryGetValue(config.
mod, out configList2))
75 LoadTimeConfigs.Add(config.
mod, configList2 =
new List<ModConfig>());
76 configList2.Add(GeneratePopulatedClone(config));
80 internal static void LoadAll()
84 foreach (var activeConfig
in activeConfigs.Value)
91 internal static void OnChangedAll() {
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";
102 string path = Path.Combine(ModConfigPath, filename);
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) :
"{}";
112 JsonConvert.PopulateObject(json, config, serializerSettings);
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.");
117 JsonConvert.PopulateObject(
"{}", config, serializerSettings);
121 internal static void Reset(
ModConfig pendingConfig)
124 JsonConvert.PopulateObject(json, pendingConfig, serializerSettings);
127 internal static void Save(
ModConfig config)
129 Directory.CreateDirectory(ModConfigPath);
130 string filename = config.
mod.
Name +
"_" + config.
Name +
".json";
131 string path = Path.Combine(ModConfigPath, filename);
132 string json = JsonConvert.SerializeObject(config, serializerSettings);
133 File.WriteAllText(path, json);
136 internal static void Unload()
139 serializerSettingsCompact.ContractResolver = serializerSettings.ContractResolver;
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);
148 LoadTimeConfigs.Clear();
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];
161 var loadTimeConfigs = LoadTimeConfigs[mod];
162 for (
int i = 0; i < configs.Count; i++)
164 if (loadTimeConfigs[i].NeedsReload(configs[i]))
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;
187 if (LoadTimeConfigs.TryGetValue(mod, out 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();
205 JsonConvert.PopulateObject(json, activeConfig, serializerSettingsCompact);
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();
237 ModConfig pendingConfig = GeneratePopulatedClone(config);
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.";
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);
309 public static T GetCustomAttribute<T>(PropertyFieldWrapper memberInfo,
object item,
object array) where T :
Attribute {
311 T attribute = (T)
Attribute.GetCustomAttribute(memberInfo.Type, typeof(T),
true);
318 attribute = (T)
Attribute.GetCustomAttribute(memberInfo.MemberInfo, typeof(T)) ?? attribute;
323 public static T GetCustomAttribute<T>(PropertyFieldWrapper memberInfo, Type type) where 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;
349 if (a is IEnumerable && b is IEnumerable && !(a is
string) && !(b is
string))
350 return EnumerableEquals((IEnumerable)a, (IEnumerable)b);
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;
381 if (baseProvider == null)
382 throw new ArgumentNullException();
383 this.baseProvider = baseProvider;
386 public virtual object GetValue(
object target) {
return baseProvider.GetValue(target); }
388 public virtual void SetValue(
object target,
object value) { baseProvider.SetValue(target, value); }
400 this.defaultValueGenerator = defaultValueGenerator;
403 public override void SetValue(
object target,
object value) {
404 base.SetValue(target, value ?? defaultValueGenerator.Invoke());
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 bool ObjectEquals(object a, object b)
override IList< JsonProperty > CreateProperties(Type type, MemberSerialization memberSerialization)
virtual void OnLoaded()
This method is called when the ModConfig has been loaded for the first time. This happens before regu...
virtual ModConfig Clone()
tModLoader will call Clone on ModConfig to facilitate proper implementation of the ModConfig user int...
virtual bool AcceptClientChanges(ModConfig pendingConfig, int whoAmI, ref string message)
Called on the Server for ServerSide configs to determine if the changes asked for by the Client will ...
readonly IValueProvider baseProvider
This serves as the central class which loads mods. It contains many static fields and methods related...
ModConfig provides a way for mods to be configurable. ModConfigs can either be Client specific or Ser...
ValueProviderDecorator(IValueProvider baseProvider)
This class inherits from BinaryWriter. This means that you can use all of its writing functions to se...
virtual object GetValue(object target)
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)
readonly Func< object > defaultValueGenerator
static bool EnumerableEquals(IEnumerable a, IEnumerable b)
override void SetValue(object target, object value)
static IEnumerable< PropertyFieldWrapper > GetFieldsAndProperties(object item)
abstract ConfigScope Mode
virtual void OnChanged()
This hook is called anytime new config values have been set and are ready to take effect...
static ModConfig GeneratePopulatedClone(ModConfig original)
Custom ContractResolver for facilitating refernce type defaults. The ShouldSerialize code enables unc...
virtual string Name
Stores the name of the mod. This name serves as the mod's identification, and also helps with saving ...
static void SetPendingChanges(bool changes=true)
Mod is an abstract class that you will override. It serves as a central place from which the mod's co...
static object AlternateCreateInstance(Type type)
static readonly JsonSerializerSettings serializerSettings
static Mod GetMod(string name)
Gets the instance of the Mod with the specified name.
ConfigScope
Each ModConfig class has a different scope. Failure to use the correct mode will lead to bugs...
virtual void SetValue(object target, object value)
NullToDefaultValueProvider(IValueProvider baseProvider, Func< object > defaultValueGenerator)