Skip to content

Commit ed62615

Browse files
committed
Fix issue #297 : Our replacement for ConfigNode.ReadObject() now always return true to align with the stock API behavior, preventing issues for mods (uselessly) checking the ConfigNode.LoadObjectFromConfig() return value. Also changed the verbosity level from error to warning when loading objects that don't have any persistent fields, as this is a valid (albeit useless and wasteful) pattern.
1 parent 6f93e36 commit ed62615

File tree

2 files changed

+54
-50
lines changed

2 files changed

+54
-50
lines changed

KSPCommunityFixes/Library/Extensions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ public static string AssemblyQualifiedName(this object obj)
1717
return $"{type.Assembly.GetName().Name}:{type.Name}";
1818
}
1919

20+
/// <summary>
21+
/// Get an assembly qualified type name in the "assemblyName:typeName" format
22+
/// </summary>
23+
public static string AssemblyQualifiedName(this Type type)
24+
{
25+
return $"{type.Assembly.GetName().Name}:{type.Name}";
26+
}
27+
2028
public static bool IsPAWOpen(this Part part)
2129
{
2230
return part.PartActionWindow.IsNotNullOrDestroyed() && part.PartActionWindow.isActiveAndEnabled;

KSPCommunityFixes/Modding/PersistentIConfigNode.cs

Lines changed: 46 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
// - Add support for IConfigNode serialization
22
// - Add support for Guid serialization
33
// - Rewrite Object reading and writing for perf, clarity, and reuse
4-
//#define DEBUG_PERSISTENTICONFIGNODE
5-
using HarmonyLib;
4+
// #define DEBUG_PERSISTENTICONFIGNODE
5+
66
using System;
77
using System.Collections;
88
using System.Collections.Generic;
99
using System.Reflection;
1010
using UnityEngine;
1111
using static ConfigNode;
12-
using Random = System.Random;
1312

1413
namespace KSPCommunityFixes.Modding
1514
{
@@ -30,9 +29,9 @@ public LoadState(bool r, ReadLinkList l)
3029

3130
protected override void ApplyPatches()
3231
{
33-
AddPatch(PatchType.Prefix, typeof(ConfigNode), nameof(ConfigNode.ReadObject), new Type[] { typeof(object), typeof(ConfigNode) });
32+
AddPatch(PatchType.Override, typeof(ConfigNode), nameof(ReadObject), new Type[] { typeof(object), typeof(ConfigNode) });
3433

35-
AddPatch(PatchType.Prefix, typeof(ConfigNode), nameof(ConfigNode.WriteObject), new Type[] { typeof(object), typeof(ConfigNode), typeof(int) });
34+
AddPatch(PatchType.Override, typeof(ConfigNode), nameof(WriteObject), new Type[] { typeof(object), typeof(ConfigNode), typeof(int) });
3635

3736
AddPatch(PatchType.Prefix, typeof(ConfigNode), nameof(CreateConfigFromObject), new Type[] { typeof(object), typeof(int), typeof(ConfigNode) });
3837

@@ -68,52 +67,21 @@ private static void ConfigNode_LoadObjectFromConfig_Postfix(LoadState __state)
6867
readLinks = __state.links;
6968
}
7069

71-
// used by TypeCache since we can't make the below method return a value.
72-
public static bool WriteSuccess;
73-
74-
public static bool ConfigNode_WriteObject_Prefix(object obj, ConfigNode node, int pass)
70+
private static void ConfigNode_WriteObject_Override(object obj, ConfigNode node, int pass)
7571
{
76-
if (obj is IPersistenceSave persistenceSave)
77-
persistenceSave.PersistenceSave();
78-
79-
Type type = obj.GetType();
80-
var typeCache = TypeCache.GetOrCreate(type);
81-
if (typeCache == null)
82-
{
83-
WriteSuccess = false;
84-
return false;
85-
}
86-
87-
WriteSuccess = typeCache.Write(obj, node, pass);
88-
89-
return false;
72+
TypeCache.TryWriteObject(obj, node, pass);
9073
}
9174

9275

93-
public static bool ConfigNode_ReadObject_Prefix(object obj, ConfigNode node, out bool __result)
76+
private static bool ConfigNode_ReadObject_Override(object obj, ConfigNode node)
9477
{
95-
Type type = obj.GetType();
96-
var typeCache = TypeCache.GetOrCreate(type);
97-
if (typeCache == null)
98-
{
99-
__result = false;
100-
return false;
101-
}
102-
103-
bool readResult = typeCache.Read(obj, node);
104-
105-
if (obj is IPersistenceLoad persistenceLoad)
106-
{
107-
iPersistentLoaders.Add(persistenceLoad);
108-
}
109-
110-
__result = readResult;
111-
return false;
78+
TypeCache.TryReadObject(obj, node);
79+
return true;
11280
}
11381
}
11482

11583
// This is an expanded version of System.TypeCode
116-
public enum DataType : uint
84+
internal enum DataType : uint
11785
{
11886
INVALID = 0,
11987
IConfigNode,
@@ -151,7 +119,7 @@ public enum DataType : uint
151119
LastValueType = ValueEnum,
152120
}
153121

154-
public class FieldData
122+
internal class FieldData
155123
{
156124
private static readonly System.Globalization.CultureInfo _Invariant = System.Globalization.CultureInfo.InvariantCulture;
157125

@@ -272,7 +240,7 @@ public bool Read(ConfigNode node, object host, int attribIndex)
272240
}
273241
else
274242
{
275-
PersistentIConfigNode.ConfigNode_ReadObject_Prefix(existing, node, out bool success);
243+
bool success = TypeCache.TryReadObject(existing, node);
276244
if (removeAfterUse)
277245
node.name = string.Empty;
278246
return success;
@@ -413,10 +381,10 @@ public bool Write(object value, ConfigNode node, int attribIndex, int pass)
413381

414382
case DataType.Object:
415383
case DataType.Component:
416-
PersistentIConfigNode.ConfigNode_WriteObject_Prefix(value, subNode, pass);
384+
bool success = TypeCache.TryWriteObject(value, subNode, pass);
417385
if (subNode.HasData)
418386
node._nodes.nodes.Add(subNode);
419-
return PersistentIConfigNode.WriteSuccess;
387+
return success;
420388
}
421389
return false;
422390
}
@@ -446,7 +414,7 @@ public static object ReadObject(ConfigNode node, object existing, DataType dataT
446414
existing = TypeCache.GetNewInstance(fieldType);
447415
if (existing == null)
448416
return null;
449-
PersistentIConfigNode.ConfigNode_ReadObject_Prefix(existing, node, out success);
417+
success = TypeCache.TryReadObject(existing, node);
450418
return existing;
451419
}
452420
return null;
@@ -491,7 +459,7 @@ public static MonoBehaviour CreateComponent(object host, Type fieldType, Persist
491459

492460
// We're not reading directly here because the class might have
493461
// other interfaces/links/etc.
494-
PersistentIConfigNode.ConfigNode_ReadObject_Prefix(child, node, out success);
462+
success = TypeCache.TryReadObject(child, node);
495463
return child;
496464
}
497465

@@ -825,7 +793,7 @@ private bool FindSetArrayType(Type seqType)
825793
}
826794
}
827795

828-
public class TypeCache
796+
internal class TypeCache
829797
{
830798
private static readonly Dictionary<Type, TypeCache> cache = new Dictionary<Type, TypeCache>();
831799
//private static readonly Dictionary<Type, ConstructorInfo> _typeToConstrutor = new Dictionary<Type, ConstructorInfo>();
@@ -868,6 +836,34 @@ private int SeenFieldIndex(string fieldName)
868836
return numSeen;
869837
}
870838

839+
public static bool TryWriteObject(object obj, ConfigNode node, int pass)
840+
{
841+
if (obj is IPersistenceSave persistenceSave)
842+
persistenceSave.PersistenceSave();
843+
844+
Type type = obj.GetType();
845+
TypeCache typeCache = GetOrCreate(type);
846+
if (typeCache == null)
847+
return false;
848+
849+
return typeCache.Write(obj, node, pass);
850+
}
851+
852+
public static bool TryReadObject(object obj, ConfigNode node)
853+
{
854+
Type type = obj.GetType();
855+
TypeCache typeCache = GetOrCreate(type);
856+
if (typeCache == null)
857+
return false;
858+
859+
bool readResult = typeCache.Read(obj, node);
860+
861+
if (obj is IPersistenceLoad persistenceLoad)
862+
iPersistentLoaders.Add(persistenceLoad);
863+
864+
return readResult;
865+
}
866+
871867
public bool Read(object host, ConfigNode node)
872868
{
873869
if (_isPersistentLinkable)
@@ -952,7 +948,7 @@ public static TypeCache CreateAndAdd(Type t)
952948
var tc = new TypeCache(t);
953949
if (tc._fields.Count == 0)
954950
{
955-
Debug.LogError($"[KSPCommunityFixes]: No Persistent fields on object of type {t.Name} that is referenced in persistent field, adding as null to TypeCache.");
951+
Debug.LogWarning($"[KSPCommunityFixes]: No Persistent fields on object of type {t.AssemblyQualifiedName()}, adding as null to TypeCache. That object likely shouldn't implement the IConfigNode interface.");
956952
tc = null;
957953
}
958954

0 commit comments

Comments
 (0)