Click or drag to resize

PersistentFieldAttribute Class

A simple attribute for the fields that need (de)serialization.
Inheritance Hierarchy

Namespace:  KSPDev.ConfigUtils
Assembly:  KSPDev_Utils.1.2 (in KSPDev_Utils.1.2.dll) Version: 1.2 for KSP v1.6+
Syntax
C#
[AttributeUsageAttribute(AttributeTargets.Field)]
public sealed class PersistentFieldAttribute : BasePersistentFieldAttribute
Request Example View Source

The PersistentFieldAttribute type exposes the following members.

Constructors
  NameDescription
Public methodPersistentFieldAttribute
Creates attribute for a persistent field of standard KSP types.
Top
Properties
  NameDescription
Public propertyisCollection
Specifies if the annotated field is a collection of values.
Top
Remarks

The readonly fields cannot be restored from a persistent state. However, they can be written out.

By default the ordinal values are handled via StandardOrdinaryTypesProto and the collection fields via GenericCollectionTypeProto. These proto handlers can be changed in the annotation by assigning properties ordinaryTypeProto and/or collectionTypeProto.

Examples
Below is a simple usage of the attribute.
class ClassWithPersistentFields {
  [PersistentField("my/listField", isCollection = true)]
  private List<string> sampleList = new List<string>();

  internal struct ComplexType {
    [PersistentField("val1", group = "nevermind")]
    public bool boolVal;
    [PersistentField("val2")]
    public Color colorVal;
  }

  [PersistentField("my/setField", isCollection = true, group = "abc")]
  private HashSet<ComplexType> sampleSet = new HashSet<ComplexType>();

  void SaveConfigs() {
    // Save a default group of fields: only field "sampleList" qualifies.
    ConfigAccessor.WriteFieldsIntoFile("settings.cfg", instance: this);
    /* The following structure in the file will be created:
     * {
     *   my
     *   {
     *     listField: string1
     *     listField: string2
     *   }
     * }
     */

    // Save a specific group of fields: only field "sampleSet" belongs to group "abc".
    sampleSet.Add(new ComplexType() { boolVal = true, colorVal = Color.black });
    sampleSet.Add(new ComplexType() { boolVal = false, colorVal = Color.white });
    ConfigAccessor.WriteFieldsIntoFile("settings.cfg", instance: this, group: "abc");
    /* The following structure in the file will be created:
     * {
     *     setField
     *     {
     *       val1: True
     *       val2: 0,0,0,1
     *     }
     *     setField
     *     {
     *       val1: false
     *       val2: 1,1,1,1
     *     }
     *   }
     * }
     */
  }
}

Note that the group is ignored in the nested types. I.e. in ComplexType in this case. However, if ComplexType was an immediate target of the WriteFieldsIntoFile call then the group would be considered.

Visibility of the annotated field is also important. The persistent field attributes are only visible in the child class if they were public or protected in the parent. The private field annotations are not inherited and need to be handled at the level of the declaring class.

class Parent {
  [PersistentField("parent_private")]
  private int field1 = 1;

  [PersistentField("parent_protected")]
  protected int field2 = 2;

  [PersistentField("parent_public")]
  public int field3 = 3;
}

class Child : Parent {
  [PersistentField("child_private")]
  private int field1 = 10;

  void SaveConfig() {
    // Save all fields in the inherited type. 
    ConfigAccessor.WriteFieldsIntoFile("settings.cfg", instance: this);
    /* The following structure in the file will be created:
     * {
     *     parent_protected: 2
     *     parent_public: 3
     *     child_private: 10
     * }
     */

    // Save all fields in the base type. 
    ConfigAccessor.WriteFieldsIntoFile("settings.cfg", instance: (Parent) this);
    /* The following structure in the file will be created:
     * {
     *     parent_private: 1
     *     parent_protected: 2
     *     parent_public: 3
     * }
     */
  }
}

The code above implies that in common case the unsealed class should put the private fields in a group other than default to avoid settings collision.

Instead of creating nested classes with attributed fields, you can makea class that implements KSP interface IConfigNode. In this case the Save/load methid will be invoked when (de)serializing the field.

public class NodeCustomType : IConfigNode {
  public virtual void Save(ConfigNode node) {
  }
  public virtual void Load(ConfigNode node) {
  }

  [PersistentField("custom_class")]
  public NodeCustomType field1;
}

In case of your type is really simple, and you can serialize it into a plain string, you may choose to implement IPersistentField instead. It works in a similar way but the source/target of the persistense is a string instead of a config node.

See Also