Skip to content

Commit b902afe

Browse files
authored
Improved config validation if properties are invalid, falling back to default value (SubnauticaNitrox#1307)
* Added warn if property value is invalid * Added warn message for missing properties in properties file
1 parent 12ae79a commit b902afe

File tree

1 file changed

+73
-11
lines changed

1 file changed

+73
-11
lines changed

NitroxModel/Serialization/PropertiesWriter.cs

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public static class PropertiesWriter
2727
char[] lineSeparator = { '=' };
2828
int lineNum = 0;
2929
string readLine;
30+
HashSet<MemberInfo> unserializedMembers = typeCachedDict.Values.ToHashSet();
3031
while ((readLine = reader.ReadLine()) != null)
3132
{
3233
lineNum++;
@@ -38,22 +39,23 @@ public static class PropertiesWriter
3839
if (readLine.Contains('='))
3940
{
4041
string[] keyValuePair = readLine.Split(lineSeparator, 2);
41-
keyValuePair[0] = keyValuePair[0].ToLowerInvariant(); // Ignore case for property names in file.
42-
if (!typeCachedDict.TryGetValue(keyValuePair[0], out MemberInfo member))
42+
// Ignore case for property names in file.
43+
if (!typeCachedDict.TryGetValue(keyValuePair[0].ToLowerInvariant(), out MemberInfo member))
4344
{
44-
Log.Warn($"Property {keyValuePair[0]} does not exist on type {typeof(T).FullName}!");
45+
Log.Warn($"Property or field {keyValuePair[0]} does not exist on type {typeof(T).FullName}!");
4546
continue;
4647
}
48+
unserializedMembers.Remove(member); // This member was serialized in the file
4749

48-
FieldInfo field = member as FieldInfo;
49-
if (field != null)
50+
if (!SetMemberValue(props, member, keyValuePair[1]))
5051
{
51-
field.SetValue(props, TypeDescriptor.GetConverter(field.FieldType).ConvertFrom(keyValuePair[1]));
52-
}
53-
PropertyInfo prop = member as PropertyInfo;
54-
if (prop != null)
55-
{
56-
prop.SetValue(props, TypeDescriptor.GetConverter(prop.PropertyType).ConvertFrom(keyValuePair[1]));
52+
(Type type, object value) data = member switch
53+
{
54+
FieldInfo field => (field.FieldType, field.GetValue(props)),
55+
PropertyInfo prop => (prop.PropertyType, prop.GetValue(props)),
56+
_ => (typeof(string), "")
57+
};
58+
Log.Warn($@"Property ""({data.type.Name}) {member.Name}"" has an invalid value {StringifyValue(keyValuePair[1])} on line {lineNum}. Using default value: {StringifyValue(data.value)}");
5759
}
5860
}
5961
else
@@ -62,6 +64,25 @@ public static class PropertiesWriter
6264
}
6365
}
6466

67+
if (unserializedMembers.Any())
68+
{
69+
IEnumerable<string> unserializedProps = unserializedMembers.Select(m =>
70+
{
71+
object value = null;
72+
if (m is FieldInfo field)
73+
{
74+
value = field.GetValue(props);
75+
}
76+
else if (m is PropertyInfo prop)
77+
{
78+
value = prop.GetValue(props);
79+
}
80+
return new { m.Name, Value = value };
81+
})
82+
.Select(m => $" - {m.Name}: {m.Value}");
83+
Log.Warn($@"{props.FileName} is using default values for the missing properties:{Environment.NewLine}{string.Join(Environment.NewLine, unserializedProps)}");
84+
}
85+
6586
return props;
6687
}
6788

@@ -121,6 +142,47 @@ private static Dictionary<string, MemberInfo> GetTypeCacheDictionary<T>()
121142
return typeCachedDict;
122143
}
123144

145+
private static string StringifyValue(object value)
146+
{
147+
return value switch
148+
{
149+
string _ => $@"""{value}""",
150+
null => @"""""",
151+
_ => value.ToString()
152+
};
153+
}
154+
155+
private static bool SetMemberValue<T>(T instance, MemberInfo member, string valueFromFile)
156+
{
157+
object ConvertFromStringOrDefault(Type typeOfValue, out bool isDefault, object defaultValue = default)
158+
{
159+
try
160+
{
161+
object newValue = TypeDescriptor.GetConverter(typeOfValue).ConvertFrom(valueFromFile);
162+
isDefault = false;
163+
return newValue;
164+
}
165+
catch (Exception)
166+
{
167+
isDefault = true;
168+
return defaultValue;
169+
}
170+
}
171+
172+
bool usedDefault;
173+
switch (member)
174+
{
175+
case FieldInfo field:
176+
field.SetValue(instance, ConvertFromStringOrDefault(field.FieldType, out usedDefault, field.GetValue(instance)));
177+
return !usedDefault;
178+
case PropertyInfo prop:
179+
prop.SetValue(instance, ConvertFromStringOrDefault(prop.PropertyType, out usedDefault, prop.GetValue(instance)));
180+
return !usedDefault;
181+
default:
182+
throw new Exception($"Serialized member must be field or property: {member}.");
183+
}
184+
}
185+
124186
private static void WriteProperty<T>(T member, object value, StreamWriter stream) where T : MemberInfo
125187
{
126188
stream.Write(member.Name);

0 commit comments

Comments
 (0)