Update config upgrades to be a bit more robust

This commit is contained in:
Chris 2021-01-28 19:31:09 +10:30
parent d5594c5630
commit f87b6156f4
2 changed files with 26 additions and 14 deletions

View file

@ -600,7 +600,7 @@ namespace TShockAPI.Configuration
/// <returns></returns> /// <returns></returns>
public override TShockSettings ConvertJson(string json, out bool incompleteSettings) public override TShockSettings ConvertJson(string json, out bool incompleteSettings)
{ {
var settings = FileTools.LoadConfigAndCheckForMissingFields<TShockSettings>(FileTools.AttemptConfigUpgrade(json), out incompleteSettings); var settings = FileTools.LoadConfigAndCheckForMissingFields<TShockSettings>(json, out incompleteSettings);
Settings = settings; Settings = settings;
OnConfigRead?.Invoke(this); OnConfigRead?.Invoke(this);

View file

@ -177,11 +177,12 @@ namespace TShockAPI
/// Looks for a 'Settings' token in the json object. If one is not found, returns a new json object with all tokens of the previous object added /// Looks for a 'Settings' token in the json object. If one is not found, returns a new json object with all tokens of the previous object added
/// as children to a root 'Settings' token /// as children to a root 'Settings' token
/// </summary> /// </summary>
/// <param name="json"></param> /// <param name="cfg"></param>
/// <param name="requiredUpgrade"></param>
/// <returns></returns> /// <returns></returns>
internal static JObject AttemptConfigUpgrade(string json) internal static JObject AttemptConfigUpgrade(JObject cfg, out bool requiredUpgrade)
{ {
JObject cfg = JObject.Parse(json); requiredUpgrade = false;
if (cfg.SelectToken("Settings") == null) if (cfg.SelectToken("Settings") == null)
{ {
@ -190,40 +191,51 @@ namespace TShockAPI
{ "Settings", cfg } { "Settings", cfg }
}; };
cfg = newCfg; cfg = newCfg;
requiredUpgrade = true;
} }
return cfg; return cfg;
} }
internal static TSettings LoadConfigAndCheckForMissingFields<TSettings>(string json, out bool anyMissingFields) where TSettings : new() internal static TSettings LoadConfigAndCheckForMissingFields<TSettings>(string json, out bool writeConfig) where TSettings : new()
{ {
return LoadConfigAndCheckForMissingFields<TSettings>(JObject.Parse(json), out anyMissingFields); //If an empty file is attempting to be loaded as a config, instead use an empty json object. Otherwise Newtonsoft throws an exception here
if (string.IsNullOrWhiteSpace(json))
{
json = "{}";
}
return LoadConfigAndCheckForMissingFields<TSettings>(JObject.Parse(json), out writeConfig);
} }
/// <summary> /// <summary>
/// Parses a JObject into a TSettings object, also emitting a bool indicating if any of the TSetting's fields were missing from the JObject /// Parses a JObject into a TSettings object, also emitting a bool indicating if the JObject was incomplete
/// </summary> /// </summary>
/// <typeparam name="TSettings">The type of the config file object</typeparam> /// <typeparam name="TSettings">The type of the config file object</typeparam>
/// <param name="jObject">The json object to parse</param> /// <param name="jObject">The json object to parse</param>
/// <param name="anyMissingFields">Whether any fields are missing from the config</param> /// <param name="writeConfig">Whether the config needs to be written to disk again</param>
/// <returns>The config object</returns> /// <returns>The config object</returns>
internal static TSettings LoadConfigAndCheckForMissingFields<TSettings>(JObject jObject, out bool anyMissingFields) where TSettings : new() internal static TSettings LoadConfigAndCheckForMissingFields<TSettings>(JObject jObject, out bool writeConfig) where TSettings : new()
{ {
anyMissingFields = false; JObject cfg = AttemptConfigUpgrade(jObject, out bool requiredUpgrade);
var configFields = new HashSet<string>(typeof(TSettings).GetFields() var configFields = new HashSet<string>(typeof(TSettings).GetFields()
.Where(field => !field.IsStatic) .Where(field => !field.IsStatic)
.Select(field => field.Name)); .Select(field => field.Name));
var jsonFields = new HashSet<string>(jObject.SelectToken("Settings") var jsonFields = new HashSet<string>(cfg.SelectToken("Settings")
.Children() .Children()
.Select(field => field as JProperty) .Select(field => field as JProperty)
.Where(field => field != null) .Where(field => field != null)
.Select(field => field.Name)); .Select(field => field.Name));
anyMissingFields = !configFields.SetEquals(jsonFields); bool missingFields = !configFields.SetEquals(jsonFields);
return jObject.SelectToken("Settings").ToObject<TSettings>();
//If the config file had to be upgraded or the fields in the given TSettings don't match the config, we'll want the config to be rewritten with the correct data
writeConfig = requiredUpgrade || missingFields;
return cfg.SelectToken("Settings").ToObject<TSettings>();
} }
} }
} }