/// <param name="fieldInfo">An annotated field metadata.</param> /// <param name="fieldAttr">An annotation of the field.</param> internal PersistentField(FieldInfo fieldInfo, PersistentFieldAttribute fieldAttr) { this.fieldInfo = fieldInfo; cfgPath = fieldAttr.path; var ordinaryType = fieldInfo.FieldType; if (fieldAttr.collectionTypeProto != null) { collectionFieldHandler = new CollectionFieldHandler(this, fieldInfo.FieldType, fieldAttr.collectionTypeProto); ordinaryType = collectionFieldHandler.GetItemType(); } ordinaryFieldHandler = new OrdinaryFieldHandler(this, ordinaryType, fieldAttr.ordinaryTypeProto); if (ordinaryFieldHandler.IsCompound()) { // Ignore static fields of the compound type since it can be used by multiple persistent // fields or as an item in a collection field. // Also, ignore groups in the compound types to reduce configuration complexity. compoundTypeFields = PersistentFieldsFactory.GetPersistentFields( ordinaryType, false /* needStatic */, true /* needInstance */, null /* group */) // Parent nodes have to be handled before children! .OrderBy(x => string.Join("/", x.cfgPath)) .ToArray(); } }
/// <param name="fieldInfo">An annotated field metadata.</param> /// <param name="fieldAttr">An annotation of the field.</param> public PersistentField(FieldInfo fieldInfo, PersistentFieldAttribute fieldAttr) { this.fieldInfo = fieldInfo; cfgPath = fieldAttr.path; var ordinaryType = fieldInfo.FieldType; if (fieldAttr.collectionTypeProto != null) { collectionProto = Activator.CreateInstance(fieldAttr.collectionTypeProto, new[] { fieldInfo.FieldType }) as AbstractCollectionTypeProto; ordinaryType = collectionProto.GetItemType(); } simpleTypeProto = Activator.CreateInstance(fieldAttr.ordinaryTypeProto) as AbstractOrdinaryValueTypeProto; isCustomSimpleType = typeof(IPersistentField).IsAssignableFrom(ordinaryType); // Determine if field's or collection item's type is a class (compound type). isCompound = !simpleTypeProto.CanHandle(ordinaryType) && !isCustomSimpleType && (ordinaryType.IsValueType && !ordinaryType.IsEnum || // IsStruct ordinaryType.IsClass && ordinaryType != typeof(string) && !ordinaryType.IsEnum); // IsClass if (!isCompound && !simpleTypeProto.CanHandle(ordinaryType) && !isCustomSimpleType) { Debug.LogErrorFormat( "Proto {0} cannot handle value in field {1}.{2}", ordinaryType.FullName, fieldInfo.DeclaringType.FullName, fieldInfo.Name); isDisabled = true; } // For a compound type retrieve all its persisted fields. if (isCompound && !isDisabled) { // Ignore static fields of the compound type since it can be used by multiple persistent // fields or as an item in a collection field. // Also, ignore groups in the compound types to reduce configuration complexity. compoundTypeFields = PersistentFieldsFactory.GetPersistentFields( ordinaryType, false /* needStatic */, true /* needInstance */, null /* group */) // Parent nodes have to be handled before children! .OrderBy(x => string.Join("/", x.cfgPath)) .ToArray(); // Disable if cannot deal with the field anyways. if (compoundTypeFields.Length == 0 && !typeof(IConfigNode).IsAssignableFrom(ordinaryType)) { Debug.LogErrorFormat( "Compound type in field {0}.{1} has no persistent fields and doesn't implement" + " IConfigNode", fieldInfo.DeclaringType.FullName, fieldInfo.Name); isDisabled = true; } } }
/// <summary>Reads the values of the annotated persistent fields from a config node.</summary> /// <param name="node">The config node to read data from.</param> /// <param name="type"> /// The type to load fields for. In case of <paramref name="instance"/> is set and all the /// fields, that need to be read, are not private, it's OK to provide just the type of the /// instance. However, when reading the private and static fields, it's important to set the right /// type. That is, the type which actually defines the fields. Otherwise, they won't be found. /// </param> /// <param name="instance"> /// The instance to look the fields in. If it's <c>null</c> then only the static fields will be /// loaded. /// </param> /// <param name="group">The group tag (see <see cref="BasePersistentFieldAttribute"/>).</param> /// <seealso cref="PersistentFieldAttribute"/> public static void ReadFieldsFromNode(ConfigNode node, Type type, object instance, string group = StdPersistentGroups.Default) { var fields = PersistentFieldsFactory.GetPersistentFields( type, true /* needStatic */, instance != null /* needInstance */, group).ToArray(); DebugEx.Fine("Loading {0} persistent fields: group=\"{1}\", node={2}", fields.Length, group ?? "<ALL>", node.name); foreach (var field in fields) { field.ReadFromConfig(node, instance); } }
/// <summary>Writes values of the annotated persistent fields into a config node.</summary> /// <remarks>All persitent values are <b>added</b> into the node provided. I.e. if node had /// already had a value being persited then it either overwritten (ordinary fields) or extended /// (collection fields).</remarks> /// <param name="node">A config node to write data into.</param> /// <param name="type">A type to write fields for.</param> /// <param name="instance">An instance of type <paramref name="type"/>. If it's <c>null</c> then /// only static fields will be written.</param> /// <param name="group">A group tag (see <see cref="AbstractPersistentFieldAttribute"/>).</param> /// <seealso cref="PersistentFieldAttribute"/> public static void WriteFieldsIntoNode(ConfigNode node, Type type, object instance, string group = StdPersistentGroups.Default) { var fields = PersistentFieldsFactory.GetPersistentFields( type, true /* needStatic */, instance != null /* needInstance */, group).ToArray(); Logger.logInfo("Writing {0} persistent fields: group=\"{1}\", node={2}", fields.Length, group ?? "<ALL>", node.name); foreach (var field in fields) { field.WriteToConfig(node, instance); } }
/// <summary>Copies the custom part fields from the prefab into the instance.</summary> /// <remarks> /// The consumer code must call this method from the <c>OnAwake</c> method to ensure the custom /// fields are properly initialized. /// </remarks> /// <param name="tgtModule">The module to copy the fields into.</param> /// <seealso cref="ReadPartConfig"/> /// <example> /// <code source="Examples/ConfigUtils/ConfigAccessor-Examples.cs" region="ReadPartConfigExample"/> /// </example> public static void CopyPartConfigFromPrefab(PartModule tgtModule) { var part = tgtModule.part; if (PartLoader.Instance.IsReady()) { var moduleIdx = part.Modules.IndexOf(tgtModule); if (moduleIdx == -1) { // Modules in the unloaded parts awake before being added into part. moduleIdx = part.Modules.Count; } if (moduleIdx >= part.partInfo.partPrefab.Modules.Count) { HostedDebugLog.Error( tgtModule, "The prefab part doesn't have the module at {0}", moduleIdx); return; } var srcModule = part.partInfo.partPrefab.Modules[moduleIdx]; if (srcModule.moduleName != tgtModule.moduleName) { HostedDebugLog.Error( tgtModule, "Mismatched module in prefab at {0}: expected={1}, found={2}", moduleIdx, tgtModule.moduleName, srcModule.moduleName); if (GameSettings.VERBOSE_DEBUG_LOG) { HostedDebugLog.Fine( tgtModule, "*** DUMP OF AVAILABLE MODULES: infoName={0}, prefabName{1}", part.partInfo.name, part.partInfo.partPrefab.name); for (var i = 0; i < part.partInfo.partPrefab.Modules.Count; i++) { DebugEx.Fine("* name={0}, id=#{1}", part.partInfo.partPrefab.Modules[i].moduleName, i); } } return; } var fields = PersistentFieldsFactory.GetPersistentFields( tgtModule.GetType(), false /* needStatic */, true /* needInstance */, StdPersistentGroups.PartConfigLoadGroup); foreach (var field in fields) { // We need a copy, so get it thru the persistence. var copyNode = new ConfigNode(); field.WriteToConfig(copyNode, srcModule); field.ReadFromConfig(copyNode, tgtModule); } } }