예제 #1
0
        /// <summary>Writes field into a config node.</summary>
        /// <param name="node">A node to write state to.</param>
        /// <param name="instance">An owner of the field. Can be <c>null</c> for static fields.</param>
        public void WriteToConfig(ConfigNode node, object instance)
        {
            var value = fieldInfo.GetValue(instance);

            if (value == null)
            {
                return;
            }
            if (collectionFieldHandler != null)
            {
                collectionFieldHandler.SerializeValues(node, value);
            }
            else
            {
                var cfgData = ordinaryFieldHandler.SerializeValue(value);
                if (cfgData != null)
                {
                    if (ordinaryFieldHandler.IsCompound())
                    {
                        ConfigAccessor.SetNodeByPath(node, cfgPath, (ConfigNode)cfgData);
                    }
                    else
                    {
                        ConfigAccessor.SetValueByPath(node, cfgPath, (string)cfgData);
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Restores an ordinary value collection from the config node. The type can be compound.
        /// </summary>
        /// <remarks>This method never throws.</remarks>
        /// <param name="node">The node to read the state from.</param>
        /// <param name="instance">
        /// The owner of the field. It can be <c>null</c> for the static fields.
        /// </param>
        void ReadOrdinaryFromConfig(ConfigNode node, object instance)
        {
            var value = fieldInfo.GetValue(instance);

            if (isCompound)
            {
                var cfgNode = ConfigAccessor.GetNodeByPath(node, cfgPath);
                if (cfgNode != null)
                {
                    if (value == null)
                    {
                        // Try creating the instance using its default constructor.
                        if (fieldInfo.IsInitOnly)
                        {
                            DebugEx.Warning(
                                "Cannot assign to a NULL readonly compound field! Field is ignored: {0}.{1}",
                                fieldInfo.DeclaringType.FullName, fieldInfo.Name);
                            return;
                        }
                        try {
                            value = Activator.CreateInstance(fieldInfo.FieldType);
                            fieldInfo.SetValue(instance, value);
                        } catch (Exception ex) {
                            DebugEx.Error("Cannot restore field of type {0}: {1}", fieldInfo.FieldType, ex.Message);
                        }
                    }
                    DeserializeCompoundFieldsFromNode(cfgNode, value);
                }
            }
            else
            {
                if (fieldInfo.IsInitOnly)
                {
                    DebugEx.Warning("Cannot assign to a readonly field! Field is ignored: {0}.{1}",
                                    fieldInfo.DeclaringType.FullName, fieldInfo.Name);
                    return;
                }
                var cfgValue = ConfigAccessor.GetValueByPath(node, cfgPath);
                if (cfgValue != null)
                {
                    try {
                        object fieldValue;
                        if (isCustomSimpleType)
                        {
                            // Prefer the existing instance of the field value when available.
                            fieldValue = value ?? Activator.CreateInstance(fieldInfo.FieldType);
                            ((IPersistentField)fieldValue).ParseFromString(cfgValue);
                        }
                        else
                        {
                            fieldValue = simpleTypeProto.ParseFromString(cfgValue, fieldInfo.FieldType);
                        }
                        fieldInfo.SetValue(instance, fieldValue);
                    } catch (Exception ex) {
                        DebugEx.Error("Cannot parse value \"{0}\" as {1}: {2}",
                                      cfgValue, fieldInfo.FieldType.FullName, ex.Message);
                    }
                }
            }
        }
예제 #3
0
 /// <summary>Reads custom type fields from the part's config.</summary>
 /// <remarks>
 /// <para>
 /// The consumer code must call this method from the <c>OnLoad</c> method to capture the
 /// PartLoader initalization. This method automatically detects the game loading phase, so it's
 /// safe to call it in every <c>OnLoad</c> inovacation.
 /// </para>
 /// </remarks>
 /// <param name="module">The module to load the data for.</param>
 /// <param name="cfgNode">The config node, passed by the game to the module.</param>
 /// <seealso cref="PersistentFieldAttribute"/>
 /// <seealso cref="StdPersistentGroups.PartConfigLoadGroup"/>
 /// <seealso cref="CopyPartConfigFromPrefab"/>
 /// <example>
 /// <code source="Examples/ConfigUtils/ConfigAccessor-Examples.cs" region="ReadPartConfigExample"/>
 /// </example>
 public static void ReadPartConfig(PartModule module, ConfigNode cfgNode)
 {
     if (!PartLoader.Instance.IsReady())
     {
         ConfigAccessor.ReadFieldsFromNode(
             cfgNode, module.GetType(), module, group: StdPersistentGroups.PartConfigLoadGroup);
     }
 }
예제 #4
0
        /// <summary>Writes field into a config node.</summary>
        /// <remarks>
        /// This method is not expected to fail since converting any type into string is expected to
        /// succeeed on any value.
        /// </remarks>
        /// <param name="node">A node to write state to.</param>
        /// <param name="instance">An owner of the field. Can be <c>null</c> for static fields.</param>
        public void WriteToConfig(ConfigNode node, object instance)
        {
            if (isDisabled)
            {
                return; // Field is not supported.
            }
            var value = fieldInfo.GetValue(instance);

            if (value == null)
            {
                Debug.LogWarningFormat("Skip writing field {0}.{1} due to its value is NULL",
                                       fieldInfo.DeclaringType.FullName, fieldInfo.Name);
                return;
            }
            if (collectionProto != null)
            {
                // For collections iterative via proto class and serialize item values.
                foreach (var itemValue in collectionProto.GetEnumerator(value))
                {
                    if (itemValue != null)
                    {
                        if (isCompound)
                        {
                            ConfigAccessor.AddNodeByPath(node, cfgPath, SerializeCompoundFieldsToNode(itemValue));
                        }
                        else if (isCustomSimpleType)
                        {
                            ConfigAccessor.AddValueByPath(
                                node, cfgPath, ((IPersistentField)itemValue).SerializeToString());
                        }
                        else
                        {
                            ConfigAccessor.AddValueByPath(
                                node, cfgPath, simpleTypeProto.SerializeToString(itemValue));
                        }
                    }
                }
            }
            else
            {
                // For ordinal values just serialize the value.
                if (isCompound)
                {
                    ConfigAccessor.SetNodeByPath(node, cfgPath, SerializeCompoundFieldsToNode(value));
                }
                else if (isCustomSimpleType)
                {
                    ConfigAccessor.SetValueByPath(node, cfgPath, ((IPersistentField)value).SerializeToString());
                }
                else
                {
                    ConfigAccessor.SetValueByPath(node, cfgPath, simpleTypeProto.SerializeToString(value));
                }
            }
        }
예제 #5
0
        /// <summary>Restores a collection from the config node.</summary>
        /// <remarks>This method never throws.</remarks>
        /// <param name="node">The node to read the state from.</param>
        /// <param name="instance">
        /// The owner of the field. It can be <c>null</c> for the static fields.
        /// </param>
        void ReadCollectionFromConfig(ConfigNode node, object instance)
        {
            var value = fieldInfo.GetValue(instance);

            if (value == null)
            {
                // Collections are the complex objects, they must exist in order to be restored.
                DebugEx.Warning("Skip reading collection field {0}.{1} due to it's not initalized",
                                fieldInfo.DeclaringType.FullName, fieldInfo.Name);
                return;
            }
            collectionProto.ClearItems(value);
            if (isCompound)
            {
                // For compound items read nodes and have them parsed.
                var itemCfgs = ConfigAccessor.GetNodesByPath(node, cfgPath);
                if (itemCfgs != null)
                {
                    foreach (var itemCfg in itemCfgs)
                    {
                        var itemValue = Activator.CreateInstance(collectionProto.GetItemType());
                        DeserializeCompoundFieldsFromNode(itemCfg, itemValue);
                        collectionProto.AddItem(value, itemValue);
                    }
                }
            }
            else
            {
                // For ordinary items read strings and have them parsed.
                var itemCfgs = ConfigAccessor.GetValuesByPath(node, cfgPath);
                if (itemCfgs != null)
                {
                    foreach (var itemCfg in itemCfgs)
                    {
                        try {
                            object itemValue;
                            if (isCustomSimpleType)
                            {
                                itemValue = Activator.CreateInstance(collectionProto.GetItemType());
                                ((IPersistentField)itemValue).ParseFromString(itemCfg);
                            }
                            else
                            {
                                itemValue = simpleTypeProto.ParseFromString(itemCfg, collectionProto.GetItemType());
                            }
                            collectionProto.AddItem(value, itemValue);
                        } catch (Exception ex) {
                            DebugEx.Error("Cannot parse value \"{0}\" as {1}: {2}",
                                          itemCfgs, collectionProto.GetItemType().FullName, ex.Message);
                        }
                    }
                }
            }
        }
예제 #6
0
        /// <summary>Makes pacth from a config node.</summary>
        /// <remarks>
        /// The critical settings will be checked for the sane values. If a bad value found, then the
        /// menthod will throw.
        /// </remarks>
        /// <param name="node">The node to create from.</param>
        /// <param name="context">
        /// The context of node loading. It can be any object, e.g. <c>UrlDir.UrlConfig</c> or
        /// <c>ConfigNode</c>. It's only used for logging errors to give a context for debugging.
        /// </param>
        /// <param name="url">The URL to the node in the game's DB.</param>
        /// <returns>The patch. It's never <c>null.</c></returns>
        static ConfigNodePatch MakeFromNodeInternal(ConfigNode node, object context, string url = null)
        {
            var patchNode = new ConfigNodePatch();

            if (url != null)
            {
                patchNode.sourceConfigUrl = url;
            }
            ConfigAccessor.ReadFieldsFromNode(node, patchNode.GetType(), patchNode);

            // Sanity check of the test rules.
            Preconditions.ConfValueExists(patchNode.name, "node/name", context: context);
            Preconditions.ConfValueExists(patchNode.testSection, "node/TEST", context: context);
            Preconditions.ConfValueExists(
                patchNode.testSection.partTests, "node/TEST/PART", context: context);
            Preconditions.ConfValueExists(
                patchNode.testSection.partTests.GetValue("name"), "node/TEST/PART/name", context: context);
            foreach (var moduleNode in patchNode.testSection.moduleTests)
            {
                Preconditions.ConfValueExists(
                    moduleNode.GetValue("name"), "node/TEST/MODULE/name", context: context);
            }

            // Sanity check of the upgrade rules
            Preconditions.ConfValueExists(patchNode.upgradeSection, "node/UPGRADE", context: context);
            Preconditions.ConfValueExists(
                patchNode.upgradeSection.partRules, "node/UPGRADE/PART", context: context);
            Preconditions.OneOf(patchNode.upgradeSection.partRules.action,
                                new[] { PatchAction.Drop, PatchAction.Fix },
                                "node/UPGRADE/PART/action", context: context);
            foreach (var moduleRule in patchNode.upgradeSection.moduleRules)
            {
                Preconditions.ConfValueExists(moduleRule.name, "node/TEST/MODULE/name", context: context);
            }

            // Check the patch age.
            var patchAgeDays = (DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalDays
                               - patchNode.patchCreationTimestamp / (24 * 60 * 60);

            if (patchAgeDays > 180)
            {
                DebugEx.Warning("Patch is too old: patch={0}, age={1} days", patchNode, patchAgeDays);
            }

            return(patchNode);
        }
예제 #7
0
        /// <summary>
        /// Reads a value of an arbitrary type <typeparamref name="T"/> from the config node.
        /// </summary>
        /// <param name="node">The node to read data from.</param>
        /// <param name="pathKeys">
        /// The array of values that makes the full path. The first node in the array is the top most
        /// component of the path.
        /// </param>
        /// <param name="typeProto">
        /// A proto that can parse values of type <typeparamref name="T"/>. If not set, then
        /// <see cref="StandardOrdinaryTypesProto"/> is used.
        /// </param>
        /// <returns>The parsed value or <c>null</c> if not found.</returns>
        /// <typeparam name="T">
        /// The value type to write. The <paramref name="typeProto"/> instance must be able to handle it.
        /// </typeparam>
        /// <exception cref="ArgumentException">If type cannot be handled by the proto.</exception>
        /// <seealso cref="SetValueByPath&lt;T&gt;(ConfigNode, string[], T, KSPDev.ConfigUtils.AbstractOrdinaryValueTypeProto)"/>
        public static T?GetValueByPath <T>(
            ConfigNode node, string[] pathKeys, AbstractOrdinaryValueTypeProto typeProto = null)
            where T : struct
        {
            if (typeProto == null)
            {
                typeProto = standardTypesProto;
            }
            if (!typeProto.CanHandle(typeof(T)))
            {
                throw new ArgumentException(string.Format(
                                                "Proto {0} cannot handle type {1}", typeProto.GetType(), typeof(T)));
            }
            var strValue = ConfigAccessor.GetValueByPath(node, pathKeys);

            return(strValue == null
        ? null
        : (T?)typeProto.ParseFromString(strValue, typeof(T)));
        }
예제 #8
0
        /// <summary>Creates a collection from the config node.</summary>
        /// <param name="node">A node to read data from.</param>
        /// <returns>Сollection instance.</returns>
        internal object DeserializeValues(ConfigNode node)
        {
            object instance = null;
            var    values   = persistentField.ordinaryFieldHandler.IsCompound()
        ? ConfigAccessor.GetNodesByPath(node, persistentField.cfgPath) as object[]
        : ConfigAccessor.GetValuesByPath(node, persistentField.cfgPath) as object[];

            if (values != null)
            {
                instance = Activator.CreateInstance(collectionType);
                foreach (var value in values)
                {
                    var item = persistentField.ordinaryFieldHandler.DeserializeValue(value);
                    if (item != null)
                    {
                        collectionProto.AddItem(instance, item);
                    }
                }
            }
            return(instance);
        }
예제 #9
0
        /// <summary>Reads field from a config node.</summary>
        /// <param name="node">A node to read state from.</param>
        /// <param name="instance">An owner of the field. Can be <c>null</c> for static fields.</param>
        public void ReadFromConfig(ConfigNode node, object instance)
        {
            object value = null;

            if (collectionFieldHandler != null)
            {
                value = collectionFieldHandler.DeserializeValues(node);
            }
            else
            {
                var cfgData = ordinaryFieldHandler.IsCompound()
          ? ConfigAccessor.GetNodeByPath(node, cfgPath) as object
          : ConfigAccessor.GetValueByPath(node, cfgPath) as object;
                if (cfgData != null)
                {
                    value = ordinaryFieldHandler.DeserializeValue(cfgData);
                }
            }
            if (value != null)
            {
                fieldInfo.SetValue(instance, value);
            }
        }
예제 #10
0
        /// <summary>Stores collection values into a config node.</summary>
        /// <param name="node">A node to add values into.</param>
        /// <param name="value">A collection instance to get values from.</param>
        internal void SerializeValues(ConfigNode node, object value)
        {
            var proto = collectionProto as GenericCollectionTypeProto;

            foreach (var itemValue in proto.GetEnumerator(value))
            {
                if (itemValue == null)
                {
                    continue;
                }
                var cfgData = persistentField.ordinaryFieldHandler.SerializeValue(itemValue);
                if (cfgData != null)
                {
                    if (cfgData is ConfigNode)
                    {
                        ConfigAccessor.AddNodeByPath(node, persistentField.cfgPath, (ConfigNode)cfgData);
                    }
                    else
                    {
                        ConfigAccessor.AddValueByPath(node, persistentField.cfgPath, (string)cfgData);
                    }
                }
            }
        }
예제 #11
0
 /// <param name="cfgPath">
 /// A path to the fields's value in the config. Components must be separated by symbol <c>/</c>.
 /// The path is relative, the absolute path is determined when doing actual (de)serialization.
 /// The path is case-insensitive.
 /// </param>
 protected BasePersistentFieldAttribute(string cfgPath)
 {
     this.path = ConfigAccessor.StrToPath(cfgPath);
 }
예제 #12
0
 /// <summary>
 /// Reads a value of an arbitrary type <typeparamref name="T"/> from the config node.
 /// </summary>
 /// <param name="node">The node to read data from.</param>
 /// <param name="path">
 /// The path to the node. The path components should be separated by '/' symbol.
 /// </param>
 /// <param name="typeProto">
 /// A proto that can parse values of type <typeparamref name="T"/>. If not set, then
 /// <see cref="StandardOrdinaryTypesProto"/> is used.
 /// </param>
 /// <returns>The parsed value or <c>null</c> if not found.</returns>
 /// <typeparam name="T">
 /// The value type to write. The <paramref name="typeProto"/> instance must be able to handle it.
 /// </typeparam>
 /// <exception cref="ArgumentException">If type cannot be handled by the proto.</exception>
 /// <seealso cref="SetValueByPath&lt;T&gt;(ConfigNode, string, T, KSPDev.ConfigUtils.AbstractOrdinaryValueTypeProto)"/>
 public static T?GetValueByPath <T>(
     ConfigNode node, string path, AbstractOrdinaryValueTypeProto typeProto = null)
     where T : struct
 {
     return(GetValueByPath <T>(node, ConfigAccessor.StrToPath(path), typeProto));
 }
예제 #13
0
        /// <summary>Reads field from a config node.</summary>
        /// <param name="node">A node to read state from.</param>
        /// <param name="instance">An owner of the field. Can be <c>null</c> for static fields.</param>
        public void ReadFromConfig(ConfigNode node, object instance)
        {
            var value = fieldInfo.GetValue(instance);

            if (collectionProto != null)
            {
                // For collection field use existing object and restore its items.
                if (value == null)
                {
                    Debug.LogWarningFormat("Skip reading collection field {0}.{1} due to it's not initalized",
                                           fieldInfo.DeclaringType.FullName, fieldInfo.Name);
                    return;
                }
                collectionProto.ClearItems(value);
                if (isCompound)
                {
                    // For compound items read nodes and have them parsed.
                    var itemCfgs = ConfigAccessor.GetNodesByPath(node, cfgPath);
                    if (itemCfgs != null)
                    {
                        foreach (var itemCfg in itemCfgs)
                        {
                            var itemValue = Activator.CreateInstance(collectionProto.GetItemType());
                            DeserializeCompoundFieldsFromNode(itemCfg, itemValue);
                            collectionProto.AddItem(value, itemValue);
                        }
                    }
                }
                else
                {
                    // For ordinary items read strings and have them parsed.
                    var itemCfgs = ConfigAccessor.GetValuesByPath(node, cfgPath);
                    if (itemCfgs != null)
                    {
                        foreach (var itemCfg in itemCfgs)
                        {
                            try {
                                object itemValue;
                                if (isCustomSimpleType)
                                {
                                    itemValue = Activator.CreateInstance(collectionProto.GetItemType());
                                    ((IPersistentField)itemValue).ParseFromString(itemCfg);
                                }
                                else
                                {
                                    itemValue = simpleTypeProto.ParseFromString(itemCfg, collectionProto.GetItemType());
                                }
                                collectionProto.AddItem(value, itemValue);
                            } catch (Exception ex) {
                                Debug.LogErrorFormat("Cannot parse value \"{0}\" as {1}: {2}",
                                                     itemCfgs, collectionProto.GetItemType().FullName, ex.Message);
                            }
                        }
                    }
                }
            }
            else
            {
                // For ordinary field just restore value and assign it to the field.
                if (isCompound)
                {
                    if (value != null)
                    {
                        DeserializeCompoundFieldsFromNode(ConfigAccessor.GetNodeByPath(node, cfgPath), value);
                    }
                    else
                    {
                        Debug.LogWarningFormat("Skip reading compound field {0}.{1} due to it's not initalized",
                                               fieldInfo.DeclaringType.FullName, fieldInfo.Name);
                    }
                }
                else
                {
                    var cfgValue = ConfigAccessor.GetValueByPath(node, cfgPath);
                    if (cfgValue != null)
                    {
                        try {
                            object fieldValue;
                            if (isCustomSimpleType)
                            {
                                // Prefer the existing instance of the field value when available.
                                fieldValue = value ?? Activator.CreateInstance(fieldInfo.FieldType);
                                ((IPersistentField)fieldValue).ParseFromString(cfgValue);
                            }
                            else
                            {
                                fieldValue = simpleTypeProto.ParseFromString(cfgValue, fieldInfo.FieldType);
                            }
                            fieldInfo.SetValue(instance, fieldValue);
                        } catch (Exception ex) {
                            Debug.LogErrorFormat("Cannot parse value \"{0}\" as {1}: {2}",
                                                 cfgValue, fieldInfo.FieldType.FullName, ex.Message);
                        }
                    }
                }
            }
        }