Exemplo n.º 1
0
        /// <summary>
        /// Ensures the given config node has at least one of the given values.
        /// </summary>
        /// <param name="configNode"></param>
        /// <param name="values"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool AtLeastOne(ConfigNode configNode, string[] values, IContractConfiguratorFactory obj)
        {
            string output = "";

            foreach (string value in values)
            {
                if (configNode.HasValue(value))
                {
                    return(true);
                }

                if (value == values.First())
                {
                    output = value;
                }
                else if (value == values.Last())
                {
                    output += " or " + value;
                }
                else
                {
                    output += ", " + value;
                }
            }

            if (values.Count() == 2)
            {
                LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": Either " + output + " is required.");
            }
            else
            {
                LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": One of " + output + " is required.");
            }
            return(false);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Performs validation to check if the given config node has values that were not expected.
        /// </summary>
        /// <param name="configNode">The ConfigNode to check.</param>
        /// <param name="obj">IContractConfiguratorFactory object for error reporting</param>
        /// <returns>Always true, but logs a warning if unexpected keys were found</returns>
        public static bool ValidateUnexpectedValues(ConfigNode configNode, IContractConfiguratorFactory obj)
        {
            bool valid = true;

            if (!keysFound.ContainsKey(configNode))
            {
                obj.hasWarnings = true;
                LoggingUtil.LogWarning(obj.GetType(), obj.ErrorPrefix() +
                                       ": did not attempt to load values for ConfigNode!");
                return(false);
            }

            Dictionary <string, int> found = keysFound[configNode];

            foreach (ConfigNode.Value pair in configNode.values)
            {
                if (!found.ContainsKey(pair.name))
                {
                    obj.hasWarnings = true;
                    LoggingUtil.LogWarning(obj.GetType(), obj.ErrorPrefix() +
                                           ": unexpected attribute '" + pair.name + "' found, ignored.");
                }
            }

            foreach (ConfigNode child in configNode.nodes)
            {
                // Exceptions
                if (child.name == "PARAMETER" && (obj is ContractType || obj is ParameterFactory) ||
                    child.name == "REQUIREMENT" && (obj is ContractType || obj is ParameterFactory || obj is ContractRequirement) ||
                    child.name == "BEHAVIOUR" && (obj is ContractType) ||
                    child.name == "ORBIT" && (obj is Behaviour.OrbitGeneratorFactory || obj is Behaviour.SpawnVesselFactory || obj is Behaviour.SpawnKerbalFactory))
                {
                    continue;
                }

                if (!found.ContainsKey(child.name))
                {
                    obj.hasWarnings = true;
                    LoggingUtil.LogWarning(obj.GetType(), obj.ErrorPrefix() +
                                           ": unexpected child node '" + child.name + "' found, ignored.");
                }
            }


            return(valid);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Validates that the given config node does NOT contain the given value.
        /// </summary>
        /// <param name="configNode">The ConfigNode to check.</param>
        /// <param name="field">The field to exclude</param>
        /// <param name="obj">IContractConfiguratorFactory object for error reporting</param>
        /// <returns>Always true, but logs a warning for an unexpected value.</returns>
        public static bool ValidateExcludedValue(ConfigNode configNode, string field, IContractConfiguratorFactory obj)
        {
            if (configNode.HasNode(field) || configNode.HasValue(field))
            {
                LoggingUtil.LogWarning(obj.GetType(), obj.ErrorPrefix() +
                                       ": unexpected entry '" + field + "' found, ignored.");
            }

            return(true);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Attempts to parse a value from the config node.  Validates return values using the
        /// given function.
        /// </summary>
        /// <typeparam name="T">The type of value to convert to.</typeparam>
        /// <param name="configNode">The ConfigNode to read from.</param>
        /// <param name="key">The key to examine.</param>
        /// <param name="setter">Function used to set the output value</param>
        /// <param name="obj">Factory object for error messages.</param>
        /// <param name="validation">Validation function to run against the returned value</param>
        /// <returns>The parsed value (or default value if not found)</returns>
        public static bool ParseValue <T>(ConfigNode configNode, string key, Action <T> setter, IContractConfiguratorFactory obj, Func <T, bool> validation)
        {
            // Check for required value
            if (!configNode.HasValue(key) && !configNode.HasNode(key))
            {
                LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": Missing required value '" + key + "'.");
                return(false);
            }

            return(ParseValue <T>(configNode, key, setter, obj, default(T), validation));
        }
        /// <summary>
        /// Attempts to parse a value from the config node.  Returns a default value if not found.
        /// </summary>
        /// <typeparam name="T">The type of value to convert to.</typeparam>
        /// <param name="configNode">The ConfigNode to read from.</param>
        /// <param name="key">The key to examine.</param>
        /// <param name="setter">Function used to set the output value</param>
        /// <param name="obj">Factory object for error messages.</param>
        /// <returns>The parsed value (or default value if not found)</returns>
        public static bool ParseValue <T>(ConfigNode configNode, string key, Action <T> setter, IContractConfiguratorFactory obj)
        {
            // Check for required value
            if (!configNode.HasValue(key) && !configNode.HasNode(key))
            {
                LoggingUtil.LogError(obj, "{0}: Missing required value '{1}'.", obj.ErrorPrefix(configNode), key);
                return(false);
            }

            return(ParseValue <T>(configNode, key, setter, obj, default(T), x => true));
        }
Exemplo n.º 6
0
        /// <summary>
        /// Checks whether the mandatory field exists, and if not logs and error.  Returns true
        /// only if the validation succeeded.
        /// </summary>
        /// <param name="configNode">The ConfigNode to check.</param>
        /// <param name="field">The child that is expected</param>
        /// <param name="obj">IContractConfiguratorFactory object for error reporting</param>
        /// <returns>Whether the validation succeeded, additionally logs an error on failure.</returns>
        public static bool ValidateMandatoryChild(ConfigNode configNode, string field, IContractConfiguratorFactory obj)
        {
            if (!configNode.HasNode(field))
            {
                LoggingUtil.LogError(obj.GetType(), obj.ErrorPrefix() +
                                     ": missing required child node '" + field + "'.");
                return(false);
            }

            return(true);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Performs validation to check if the given config node has values that were not expected.
        /// </summary>
        /// <param name="configNode">The ConfigNode to check.</param>
        /// <param name="obj">IContractConfiguratorFactory object for error reporting</param>
        /// <returns>Always true, but logs a warning if unexpected keys were found</returns>
        public static bool ValidateUnexpectedValues(ConfigNode configNode, IContractConfiguratorFactory obj)
        {
            if (!keysFound.ContainsKey(configNode))
            {
                LoggingUtil.LogWarning(obj.GetType(), obj.ErrorPrefix() +
                                       ": did not attempt to load values for ConfigNode!");
                return(false);
            }

            Dictionary <string, int> found = keysFound[configNode];

            foreach (ConfigNode.Value pair in configNode.values)
            {
                if (!found.ContainsKey(pair.name))
                {
                    LoggingUtil.LogWarning(obj.GetType(), obj.ErrorPrefix() +
                                           ": unexpected entry '" + pair.name + "' found, ignored.");
                }
            }

            return(true);
        }
Exemplo n.º 8
0
 /*
  * Attempts to parse a value from the config node.  Returns a default value if not found.
  * Validates return values using the given function.
  */
 public static bool ParseValue <T>(ConfigNode configNode, string key, ref T value, IContractConfiguratorFactory obj, T defaultValue, Func <T, bool> validation)
 {
     if (ParseValue <T>(configNode, key, ref value, obj, defaultValue))
     {
         try
         {
             if (!validation.Invoke(value))
             {
                 // In general, the validation function should throw an exception and give a much better message
                 LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": The value supplied for " + key + " (" + value + ") is invalid.");
                 return(false);
             }
         }
         catch (Exception e)
         {
             LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": The value supplied for " + key + " (" + value + ") is invalid: " + e.Message);
             LoggingUtil.LogDebug(obj, e.StackTrace);
             return(false);
         }
         return(true);
     }
     return(false);
 }
Exemplo n.º 9
0
        /// <summary>
        /// Ensures that the config node does not have items from the two mutually exclusive groups.
        /// </summary>
        /// <param name="configNode">The configNode to verify.</param>
        /// <param name="group1">The first group of keys.</param>
        /// <param name="group2">The second group of keys</param>
        /// <param name="obj">IContractConfiguratorFactory for logging</param>
        /// <returns>Whether the condition is satisfied</returns>
        public static bool MutuallyExclusive(ConfigNode configNode, string[] group1, string[] group2, IContractConfiguratorFactory obj)
        {
            string group1String = "";
            string group2String = "";
            bool   group1Value  = false;
            bool   group2Value  = false;

            foreach (string value in group1)
            {
                if (configNode.HasValue(value))
                {
                    group1Value = true;
                }

                if (value == group1.First())
                {
                    group1String = value;
                }
                else
                {
                    group1String += ", " + value;
                }
            }
            foreach (string value in group2)
            {
                if (configNode.HasValue(value))
                {
                    group2Value = true;
                }

                if (value == group2.First())
                {
                    group2String = value;
                }
                else
                {
                    group2String += ", " + value;
                }
            }

            if (group1Value && group2Value)
            {
                LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": The values " + group1String + " and " + group2String + " are mutually exclusive.");
                return(false);
            }

            return(true);
        }
Exemplo n.º 10
0
 /*
  * Attempts to parse a value from the config node.
  */
 public static bool ParseValue <T>(ConfigNode configNode, string key, ref T value, IContractConfiguratorFactory obj)
 {
     try
     {
         value = ParseValue <T>(configNode, key);
         return(value != null);
     }
     catch (Exception e)
     {
         LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": Error parsing " + key + ": " + configNode.id + e.Message);
         LoggingUtil.LogDebug(obj, e.StackTrace);
         return(false);
     }
     finally
     {
         AddFoundKey(configNode, key);
     }
 }
Exemplo n.º 11
0
        /// <summary>
        /// Parses the child DATA nodes out of the given config node, and returns the parsed values back in dataValues.
        /// </summary>
        /// <param name="configNode">The ConfigNode to load child DATA nodes from.</param>
        /// <param name="obj">The ContractConfigurator object to load from.</param>
        /// <param name="dataValues"></param>
        /// <param name="uniquenessChecks"></param>
        /// <returns></returns>
        public bool ParseDataNodes(ConfigNode configNode, IContractConfiguratorFactory obj,
                                   Dictionary <string, ContractType.DataValueInfo> dataValues, Dictionary <string, UniquenessCheck> uniquenessChecks)
        {
            bool valid = true;

            foreach (ConfigNode data in ConfigNodeUtil.GetChildNodes(configNode, "DATA"))
            {
                Type   type          = null;
                bool   requiredValue = true;
                bool   hidden        = true;
                bool   isLiteral     = false;
                string title         = "";

                ConfigNodeUtil.SetCurrentDataNode(null);
                valid &= ConfigNodeUtil.ParseValue <Type>(data, "type", x => type = x, obj);
                valid &= ConfigNodeUtil.ParseValue <bool>(data, "requiredValue", x => requiredValue = x, obj, true);
                valid &= ConfigNodeUtil.ParseValue <string>(data, "title", x => title = x, obj, "");
                valid &= ConfigNodeUtil.ParseValue <bool>(data, "hidden", x => hidden = x, obj, false);
                valid &= ConfigNodeUtil.ParseValue <bool>(data, "isLiteral", x => isLiteral = x, obj, false);

                bool doneTitleWarning = false;

                UniquenessCheck uniquenessCheck = UniquenessCheck.NONE;
                // Backwards compatibility for Contract Configurator 1.8.3
                if (data.HasValue("uniqueValue") || data.HasValue("activeUniqueValue"))
                {
                    LoggingUtil.LogWarning(this, "The use of uniqueValue and activeUniqueValue is obsolete since Contract Configurator 1.9.0, use uniquenessCheck instead.");

                    bool uniqueValue       = false;
                    bool activeUniqueValue = false;
                    valid &= ConfigNodeUtil.ParseValue <bool>(data, "uniqueValue", x => uniqueValue = x, obj, false);
                    valid &= ConfigNodeUtil.ParseValue <bool>(data, "activeUniqueValue", x => activeUniqueValue = x, obj, false);

                    uniquenessCheck = activeUniqueValue ? UniquenessCheck.CONTRACT_ACTIVE : uniqueValue ? UniquenessCheck.CONTRACT_ALL : UniquenessCheck.NONE;
                }
                else
                {
                    valid &= ConfigNodeUtil.ParseValue <UniquenessCheck>(data, "uniquenessCheck", x => uniquenessCheck = x, obj, UniquenessCheck.NONE);
                }

                ConfigNodeUtil.SetCurrentDataNode(this);

                if (type != null)
                {
                    foreach (ConfigNode.Value pair in data.values)
                    {
                        string name = pair.name;
                        if (name != "type" && name != "title" && name != "hidden" && name != "requiredValue" && name != "uniqueValue" && name != "activeUniqueValue" && name != "uniquenessCheck" && name != "isLiteral")
                        {
                            if (uniquenessCheck != UniquenessCheck.NONE)
                            {
                                uniquenessChecks[name] = uniquenessCheck;
                            }

                            object value = null;

                            // Create the setter function
                            Type     actionType = typeof(Action <>).MakeGenericType(type);
                            Delegate del        = Delegate.CreateDelegate(actionType, value, typeof(DataNode).GetMethod("NullAction"));

                            // Set the ParseValue method generic
                            MethodInfo method = (isLiteral ? methodParseValueLiteral : methodParseValue).MakeGenericMethod(new Type[] { type });

                            // Invoke the ParseValue method
                            if (isLiteral)
                            {
                                this[name] = method.Invoke(null, new object[] { data, name, false });
                            }
                            else
                            {
                                valid &= (bool)method.Invoke(null, new object[] { data, name, del, obj });
                            }

                            dataValues[name] = new ContractType.DataValueInfo(title, requiredValue, hidden, type);

                            // Recommend a title
                            if (!data.HasValue("title") && requiredValue && !IsDeterministic(name) && !hidden && !doneTitleWarning && !dataValues[name].IsIgnoredType())
                            {
                                doneTitleWarning = true;

                                LoggingUtil.Log(obj.minVersion >= ContractConfigurator.ENHANCED_UI_VERSION ? LoggingUtil.LogLevel.ERROR : LoggingUtil.LogLevel.WARNING, this,
                                                obj.ErrorPrefix() + ": " + name + ": The field 'title' is required in for data node values where 'requiredValue' is true.  Alternatively, the attribute 'hidden' can be set to true (but be careful - this can cause player confusion if all lines for the contract type show as 'Met' and the contract isn't generating).");

                                // Error on newer versions of contract packs
                                if (obj.minVersion >= ContractConfigurator.ENHANCED_UI_VERSION)
                                {
                                    valid = false;
                                }
                            }
                        }
                    }
                }
            }

            return(valid);
        }
Exemplo n.º 12
0
        /// <summary>
        /// Attempts to parse a value from the config node.  Returns a default value if not found.
        /// Validates return values using the given function.
        /// </summary>
        /// <typeparam name="T">The type of value to convert to.</typeparam>
        /// <param name="configNode">The ConfigNode to read from.</param>
        /// <param name="key">The key to examine.</param>
        /// <param name="setter">Function used to set the output value</param>
        /// <param name="obj">Factory object for error messages.</param>
        /// <param name="defaultValue">Default value to use if there is no key in the config node</param>
        /// <param name="validation">Validation function to run against the returned value</param>
        /// <returns>The parsed value (or default value if not found)</returns>
        public static bool ParseValue <T>(ConfigNode configNode, string key, Action <T> setter, IContractConfiguratorFactory obj, T defaultValue, Func <T, bool> validation)
        {
            // Initialize the data type of the expression
            if (currentDataNode != null && !currentDataNode.IsInitialized(key))
            {
                currentDataNode.BlankInit(key, typeof(T));
            }

            bool valid = true;
            T    value = defaultValue;

            if (configNode.HasValue(key) || configNode.HasNode(key))
            {
                try
                {
                    // Check whether there's a value
                    if (configNode.HasValue(key) && string.IsNullOrEmpty(configNode.GetValue(key)))
                    {
                        LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": Required value '" + key + "' is empty.");
                        valid = false;
                    }
                    else
                    {
                        // Load value
                        value = ParseValue <T>(configNode, key, true);
                    }

                    // If value was non-null, run validation
                    if (value != null && (typeof(T) != typeof(string) || ((string)(object)value) != ""))
                    {
                        try
                        {
                            valid = validation.Invoke(value);
                            if (!valid)
                            {
                                // In general, the validation function should throw an exception and give a much better message
                                LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": A validation error occured while loading the key '" + key + "' with value '" + value + "'.");
                            }
                        }
                        catch (Exception e)
                        {
                            if (e is DataNode.ValueNotInitialized)
                            {
                                throw;
                            }

                            LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": A validation error occured while loading the key '" + key + "' with value '" + value + "'.");
                            LoggingUtil.LogException(e);
                            valid = false;
                        }
                    }
                }
                catch (Exception e)
                {
                    if (e.GetType() == typeof(DataNode.ValueNotInitialized))
                    {
                        string dependency = ((DataNode.ValueNotInitialized)e).key;
                        string path       = currentDataNode.Path() + key;

                        LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), "Trying to load " + path + ", but " + dependency + " is uninitialized.");

                        // Defer loading this value
                        DeferredLoadObject <T> loadObj = null;
                        if (!deferredLoads.ContainsKey(path) || deferredLoads[path].GetType().GetGenericArguments().First() != typeof(T))
                        {
                            deferredLoads[path] = new DeferredLoadObject <T>(configNode, key, setter, obj, validation, currentDataNode);
                        }
                        loadObj = (DeferredLoadObject <T>)deferredLoads[path];

                        // New dependency - try again
                        if (!loadObj.dependencies.Contains(dependency))
                        {
                            LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), "    New dependency, will re-attempt to load later.");
                            loadObj.dependencies.Add(dependency);
                            return(true);
                        }
                    }

                    LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": Error parsing " + key);

                    // Return immediately on deferred load error
                    if (e.GetType() == typeof(DataNode.ValueNotInitialized))
                    {
                        DataNode.ValueNotInitialized vni = e as DataNode.ValueNotInitialized;
                        LoggingUtil.LogException(new Exception("Unknown identifier '@" + vni.key + "'."));
                        return(false);
                    }
                    LoggingUtil.LogException(e);

                    valid = false;
                }
                finally
                {
                    AddFoundKey(configNode, key);
                }
            }

            // Store the value
            if (currentDataNode != null)
            {
                currentDataNode[key] = value;

                if (!currentDataNode.IsDeterministic(key) && initialLoad)
                {
                    currentDataNode.DeferredLoads.Add(new DeferredLoadObject <T>(configNode, key, setter, obj, validation, currentDataNode));
                }
            }

            // Invoke the setter function
            if (valid)
            {
                setter.Invoke(value);
            }

            return(valid);
        }
        /// <summary>
        /// Parses the child DATA nodes out of the given config node, and returns the parsed values back in dataValues.
        /// </summary>
        /// <param name="configNode">The ConfigNode to load child DATA nodes from.</param>
        /// <param name="obj">The ContractConfigurator object to load from.</param>
        /// <param name="dataValues"></param>
        /// <param name="uniquenessChecks"></param>
        /// <returns></returns>
        public bool ParseDataNodes(ConfigNode configNode, IContractConfiguratorFactory obj,
            Dictionary<string, ContractType.DataValueInfo> dataValues, Dictionary<string, UniquenessCheck> uniquenessChecks)
        {
            bool valid = true;

            foreach (ConfigNode data in ConfigNodeUtil.GetChildNodes(configNode, "DATA"))
            {
                Type type = null;
                bool requiredValue = true;
                bool hidden = true;
                string title = "";

                ConfigNodeUtil.SetCurrentDataNode(null);
                valid &= ConfigNodeUtil.ParseValue<Type>(data, "type", x => type = x, obj);
                valid &= ConfigNodeUtil.ParseValue<bool>(data, "requiredValue", x => requiredValue = x, obj, true);
                valid &= ConfigNodeUtil.ParseValue<string>(data, "title", x => title = x, obj, "");
                valid &= ConfigNodeUtil.ParseValue<bool>(data, "hidden", x => hidden = x, obj, false);

                bool doneTitleWarning = false;

                UniquenessCheck uniquenessCheck = UniquenessCheck.NONE;
                // Backwards compatibility for Contract Configurator 1.8.3
                if (data.HasValue("uniqueValue") || data.HasValue("activeUniqueValue"))
                {
                    LoggingUtil.LogWarning(this, "The use of uniqueValue and activeUniqueValue is obsolete since Contract Configurator 1.9.0, use uniquenessCheck instead.");

                    bool uniqueValue = false;
                    bool activeUniqueValue = false;
                    valid &= ConfigNodeUtil.ParseValue<bool>(data, "uniqueValue", x => uniqueValue = x, obj, false);
                    valid &= ConfigNodeUtil.ParseValue<bool>(data, "activeUniqueValue", x => activeUniqueValue = x, obj, false);

                    uniquenessCheck = activeUniqueValue ? UniquenessCheck.CONTRACT_ACTIVE : uniqueValue ? UniquenessCheck.CONTRACT_ALL : UniquenessCheck.NONE;
                }
                else
                {
                    valid &= ConfigNodeUtil.ParseValue<UniquenessCheck>(data, "uniquenessCheck", x => uniquenessCheck = x, obj, UniquenessCheck.NONE);
                }

                ConfigNodeUtil.SetCurrentDataNode(this);

                if (type != null)
                {
                    foreach (ConfigNode.Value pair in data.values)
                    {
                        string name = pair.name;
                        if (name != "type" && name != "title" && name != "hidden" && name != "requiredValue" && name != "uniqueValue" && name != "activeUniqueValue" && name != "uniquenessCheck")
                        {
                            if (uniquenessCheck != UniquenessCheck.NONE)
                            {
                                uniquenessChecks[name] = uniquenessCheck;
                            }

                            object value = null;

                            // Create the setter function
                            Type actionType = typeof(Action<>).MakeGenericType(type);
                            Delegate del = Delegate.CreateDelegate(actionType, value, typeof(DataNode).GetMethod("NullAction"));

                            // Set the ParseValue method generic
                            MethodInfo method = methodParseValue.MakeGenericMethod(new Type[] { type });

                            // Invoke the ParseValue method
                            valid &= (bool)method.Invoke(null, new object[] { data, name, del, obj });

                            dataValues[name] = new ContractType.DataValueInfo(title, requiredValue, hidden, type);

                            // Recommend a title
                            if (!data.HasValue("title") && requiredValue && !IsDeterministic(name) && !hidden && !doneTitleWarning && !dataValues[name].IsIgnoredType())
                            {
                                doneTitleWarning = true;

                                LoggingUtil.Log(obj.minVersion >= ContractConfigurator.ENHANCED_UI_VERSION ? LoggingUtil.LogLevel.ERROR : LoggingUtil.LogLevel.WARNING, this,
                                    obj.ErrorPrefix() + ": " + name + ": The field 'title' is required in for data node values where 'requiredValue' is true.  Alternatively, the attribute 'hidden' can be set to true (but be careful - this can cause player confusion if all lines for the contract type show as 'Met' and the contract isn't generating).");

                                // Error on newer versions of contract packs
                                if (obj.minVersion >= ContractConfigurator.ENHANCED_UI_VERSION)
                                {
                                    valid = false;
                                }
                            }

                        }
                    }
                }
            }

            return valid;
        }
Exemplo n.º 14
0
        protected static T SelectUnique(List <T> input)
        {
            // Check if there's no values
            if (input == null || !input.Any())
            {
                return(default(T));
            }

            // Get details from the base parser
            ContractType contractType = BaseParser.currentParser.currentDataNode.Root.Factory as ContractType;
            string       key          = BaseParser.currentParser.currentKey;

            DataNode.UniquenessCheck uniquenessCheck = contractType.uniquenessChecks.ContainsKey(key) ? contractType.uniquenessChecks[key] : DataNode.UniquenessCheck.NONE;
            DataNode dataNode = BaseParser.currentParser.currentDataNode;

            // Provide warning of a better method
            if (dataNode != null && dataNode.IsDeterministic(key) && (uniquenessCheck == DataNode.UniquenessCheck.CONTRACT_ALL || uniquenessCheck == DataNode.UniquenessCheck.CONTRACT_ACTIVE))
            {
                IContractConfiguratorFactory factory = BaseParser.currentParser.currentDataNode.Factory;
                LoggingUtil.LogWarning(factory, factory.ErrorPrefix() + ": Consider using a DATA_EXPAND node instead of the SelectUnique function when the values are deterministic - this will cause the player to see the full set of values in mission control before the contract is offered.");
            }

            // Check for properly uniquness check
            if (uniquenessCheck == DataNode.UniquenessCheck.NONE)
            {
                throw new NotSupportedException("The SelectUnique method can only be used in DATA nodes with the uniquenessCheck attribute set.");
            }

            // Get the active/offered contract lists
            IEnumerable <ConfiguredContract> contractList = ConfiguredContract.CurrentContracts.
                                                            Where(c => c != null && c.contractType != null);

            // Add in finished contracts
            if (uniquenessCheck == DataNode.UniquenessCheck.CONTRACT_ALL || uniquenessCheck == DataNode.UniquenessCheck.GROUP_ALL)
            {
                contractList = contractList.Union(ConfiguredContract.CompletedContracts.
                                                  Where(c => c != null && c.contractType != null));
            }

            // Filter anything that doesn't have our key
            contractList = contractList.Where(c => c.uniqueData.ContainsKey(key));

            // Check for contracts of the same type
            if (uniquenessCheck == DataNode.UniquenessCheck.CONTRACT_ALL || uniquenessCheck == DataNode.UniquenessCheck.CONTRACT_ACTIVE)
            {
                contractList = contractList.Where(c => c.contractType.name == contractType.name);
            }
            // Check for a shared group
            else if (contractType.group != null)
            {
                contractList = contractList.Where(c => c.contractType.group != null && c.contractType.group.name == contractType.group.name);
            }
            // Shared lack of group
            else
            {
                contractList = contractList.Where(c => c.contractType.group == null);
            }

            // Get the valid values
            IEnumerable <T> values;

            // Special case for vessels
            if (typeof(T) == typeof(Vessel))
            {
                values = input.Where(t => !contractList.Any(c => c.uniqueData[key].Equals(((Vessel)(object)t).id)));
            }
            else
            {
                values = input.Where(t => !contractList.Any(c => c.uniqueData[key].Equals(t)));
            }

            // Make a random selection from what's left
            return(values.Skip(r.Next(values.Count())).FirstOrDefault());
        }
        /// <summary>
        /// Ensures the given config node has at exactly  one of the given values.
        /// </summary>
        /// <param name="configNode"></param>
        /// <param name="values"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool OnlyOne(ConfigNode configNode, string[] values, IContractConfiguratorFactory obj)
        {
            int    count  = 0;
            string output = "";

            foreach (string value in values)
            {
                if (configNode.HasValue(value))
                {
                    count++;
                }

                if (value == values.First())
                {
                    output = value;
                }
                else if (value == values.Last())
                {
                    output += " or " + value;
                }
                else
                {
                    output += ", " + value;
                }
            }

            if (count != 1)
            {
                LoggingUtil.LogError(obj, "{0}: Exactly one of the following types is allowed: {1}", obj.ErrorPrefix(configNode), output);
                return(false);
            }
            else
            {
                return(true);
            }
        }
Exemplo n.º 16
0
        /// <summary>
        /// Attempts to parse a value from the config node.  Returns a default value if not found.
        /// Validates return values using the given function.
        /// </summary>
        /// <typeparam name="T">The type of value to convert to.</typeparam>
        /// <param name="configNode">The ConfigNode to read from.</param>
        /// <param name="key">The key to examine.</param>
        /// <param name="setter">Function used to set the output value</param>
        /// <param name="obj">Factory object for error messages.</param>
        /// <param name="defaultValue">Default value to use if there is no key in the config node</param>
        /// <param name="validation">Validation function to run against the returned value</param>
        /// <returns>The parsed value (or default value if not found)</returns>
        public static bool ParseValue <T>(ConfigNode configNode, string key, Action <T> setter, IContractConfiguratorFactory obj, T defaultValue, Func <T, bool> validation)
        {
            // Initialize the data type of the expression
            if (currentDataNode != null && !currentDataNode.IsInitialized(key))
            {
                currentDataNode.BlankInit(key, typeof(T));
            }

            bool valid = true;
            T    value = defaultValue;

            if (configNode.HasValue(key))
            {
                try
                {
                    // Load value
                    value = ParseValue <T>(configNode, key, true);

                    // If value was non-null, run validation
                    if (value != null)
                    {
                        try
                        {
                            valid = validation.Invoke(value);
                            if (!valid)
                            {
                                // In general, the validation function should throw an exception and give a much better message
                                LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": The value supplied for " + key + " (" + value + ") is invalid.");
                            }
                        }
                        catch (Exception e)
                        {
                            LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": The value supplied for " + key + " (" + value + ") is invalid.");
                            LoggingUtil.LogException(e);
                            valid = false;
                        }
                    }
                }
                catch (Exception e)
                {
                    if (e.GetType() == typeof(DataNode.ValueNotInitialized))
                    {
                        string dependency = ((DataNode.ValueNotInitialized)e).key;
                        string path       = currentDataNode.Path() + key;

                        LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), "Trying to load " + key + ", but " + dependency + " is uninitialized.");

                        // Defer loading this value
                        DeferredLoadObject <T> loadObj = null;
                        if (!deferredLoads.ContainsKey(path))
                        {
                            deferredLoads[path] = new DeferredLoadObject <T>(configNode, key, setter, obj, validation, currentDataNode);
                        }
                        loadObj = (DeferredLoadObject <T>)deferredLoads[path];

                        // New dependency - try again
                        if (!loadObj.dependencies.Contains(dependency))
                        {
                            LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), "    New dependency, will re-attempt to load later.");
                            loadObj.dependencies.Add(dependency);
                            return(true);
                        }
                    }

                    LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": Error parsing " + key);
                    LoggingUtil.LogException(e);

                    // Return immediately on deferred load error
                    if (e.GetType() == typeof(DataNode.ValueNotInitialized))
                    {
                        return(false);
                    }

                    valid = false;
                }
                finally
                {
                    AddFoundKey(configNode, key);
                }
            }

            // Store the value
            if (currentDataNode != null)
            {
                LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), "DataNode[" + currentDataNode.Name + "], storing " + key + " = " + value);
                currentDataNode[key] = value;

                if (!currentDataNode.IsDeterministic(key) && initialLoad)
                {
                    currentDataNode.DeferredLoads.Add(new DeferredLoadObject <T>(configNode, key, setter, obj, validation, currentDataNode));
                }
            }

            // Invoke the setter function
            if (valid)
            {
                setter.Invoke(value);
            }

            return(valid);
        }