コード例 #1
0
        /// <inheritdoc />
        public virtual bool TryValidateValues(IInputArguments input, IInvariantSet values, IContext context, [NotNullWhen(false)] out string?error)
        {
            // 'contains' limited to true/false
            if (input.ReservedArgs.ContainsKey(InputArguments.ContainsKey))
            {
                string[] invalidValues = values
                                         .Where(p => !bool.TryParse(p, out bool _))
                                         .Distinct(StringComparer.OrdinalIgnoreCase)
                                         .ToArray();
                if (invalidValues.Any())
                {
                    error = $"invalid values ({string.Join(", ", invalidValues)}); expected 'true' or 'false' when used with 'contains'.";
                    return(false);
                }

                error = null;
                return(true);
            }

            // default logic
            if (!this.TryValidateInput(input, out error) || !this.Values.TryValidateValues(input, values, out error))
            {
                return(false);
            }

            error = null;
            return(true);
        }
コード例 #2
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="tokenContext">The tokens available for this content pack.</param>
 /// <param name="forMod">The manifest for the content pack being parsed.</param>
 /// <param name="migrator">The migrator which validates and migrates content pack data.</param>
 /// <param name="installedMods">The mod IDs which are currently installed.</param>
 public TokenParser(IContext tokenContext, IManifest forMod, IMigration migrator, IInvariantSet installedMods)
 {
     this.Context       = tokenContext;
     this.ForMod        = forMod;
     this.Migrator      = migrator;
     this.InstalledMods = installedMods;
 }
コード例 #3
0
        /// <summary>Parse a string which can contain tokens, and validate that it's valid.</summary>
        /// <param name="rawValue">The raw string which may contain tokens.</param>
        /// <param name="assumeModIds">Mod IDs to assume are installed for purposes of token validation.</param>
        /// <param name="path">The path to the value from the root content file.</param>
        /// <param name="error">An error phrase indicating why parsing failed (if applicable).</param>
        /// <param name="parsed">The parsed value.</param>
        public bool TryParseString(string?rawValue, IInvariantSet assumeModIds, LogPathBuilder path, [NotNullWhen(false)] out string?error, [NotNullWhen(true)] out IManagedTokenString?parsed)
        {
            // parse lexical bits
            ILexToken[] bits = this.Lexer.ParseBits(rawValue, impliedBraces: false).ToArray();
            for (int i = 0; i < bits.Length; i++)
            {
                if (!this.Migrator.TryMigrate(ref bits[i], out error))
                {
                    parsed = null;
                    return(false);
                }
            }

            // get token string
            parsed = this.CreateTokenString(bits, this.Context, path);
            if (!this.Migrator.TryMigrate(parsed, out error))
            {
                return(false);
            }

            // validate tokens
            foreach (LexTokenToken lexToken in parsed.GetTokenPlaceholders(recursive: false))
            {
                if (!this.TryValidateToken(lexToken, assumeModIds, out error))
                {
                    return(false);
                }
            }

            // looks OK
            error = null;
            return(true);
        }
コード例 #4
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="name">The value provider name.</param>
 /// <param name="values">Get the current token values.</param>
 /// <param name="allowedValues">The allowed values (or <c>null</c> if any value is allowed).</param>
 /// <param name="canHaveMultipleValues">Whether the root may contain multiple values (or <c>null</c> to set it based on the given values).</param>
 /// <param name="isMutable">Whether to mark the value provider as mutable. The value provider will be immutable regardless, but this avoids optimizations in cases where the value provider may be replaced later.</param>
 public ImmutableValueProvider(string name, IInvariantSet?values, IInvariantSet?allowedValues = null, bool?canHaveMultipleValues = null, bool isMutable = false)
     : base(name, mayReturnMultipleValuesForRoot: false)
 {
     this.Values            = values ?? InvariantSets.Empty;
     this.AllowedRootValues = allowedValues?.Any() == true ? allowedValues : null;
     this.MayReturnMultipleValuesForRoot = canHaveMultipleValues ?? (this.Values.Count > 1 || this.AllowedRootValues == null || this.AllowedRootValues.Count > 1);
     this.IsMutable = isMutable;
 }
コード例 #5
0
 /// <inheritdoc />
 public override bool UpdateContext(IContext context)
 {
     return(this.IsChanged(this.Values, () =>
     {
         return this.Values = this.MarkReady(this.IsValidInContextImpl == null || this.IsValidInContextImpl())
             ? InvariantSets.From(this.FetchValues())
             : InvariantSets.Empty;
     }));
 }
コード例 #6
0
        /// <inheritdoc />
        public override bool UpdateContext(IContext context)
        {
            return(this.IsChanged(() =>
            {
                bool changed = false;

                if (this.MarkReady(this.SaveReader.IsReady))
                {
                    // update host/local player IDs
                    long hostId = this.SaveReader.GetPlayer(PlayerType.HostPlayer)?.UniqueMultiplayerID ?? 0;
                    long localId = this.SaveReader.GetPlayer(PlayerType.CurrentPlayer)?.UniqueMultiplayerID ?? 0;

                    changed |= this.HostPlayerId != hostId || this.LocalPlayerId != localId;

                    this.HostPlayerId = hostId;
                    this.LocalPlayerId = localId;

                    // update values by player ID
                    HashSet <long> removeIds = new HashSet <long>(this.Values.Keys);
                    foreach (Farmer player in this.SaveReader.GetAllPlayers())
                    {
                        // get values
                        long id = player.UniqueMultiplayerID;
                        IInvariantSet newValues = this.FetchValues(player);
                        if (!this.Values.TryGetValue(id, out IInvariantSet? oldValues))
                        {
                            oldValues = null;
                        }

                        // track changes
                        removeIds.Remove(id);
                        changed |= oldValues == null || this.IsChanged(oldValues, newValues);

                        // update
                        this.Values[id] = newValues;
                    }

                    // remove players if needed
                    foreach (long id in removeIds)
                    {
                        this.Values.Remove(id);
                        changed = true;
                    }
                }
                else
                {
                    this.Values.Clear();
                    this.HostPlayerId = 0;
                    this.LocalPlayerId = 0;
                }

                return changed;
            }));
        }
コード例 #7
0
        /// <summary>Parse a JSON structure which can contain tokens, and validate that it's valid.</summary>
        /// <param name="rawJson">The raw JSON structure which may contain tokens.</param>
        /// <param name="assumeModIds">Mod IDs to assume are installed for purposes of token validation.</param>
        /// <param name="path">The path to the value from the root content file.</param>
        /// <param name="error">An error phrase indicating why parsing failed (if applicable).</param>
        /// <param name="parsed">The parsed value, which may be legitimately <c>null</c> even if successful.</param>
        public bool TryParseJson(JToken?rawJson, IInvariantSet assumeModIds, LogPathBuilder path, [NotNullWhen(false)] out string?error, out TokenizableJToken?parsed)
        {
            if (rawJson == null || rawJson.Type == JTokenType.Null)
            {
                error  = null;
                parsed = null;
                return(true);
            }

            // extract mutable fields
            if (!this.TryInjectJsonProxyFields(rawJson, assumeModIds, path, out error, out TokenizableProxy[]? proxyFields))
コード例 #8
0
 /// <inheritdoc />
 public override bool TryValidateValues(IInputArguments input, IInvariantSet values, IContext context, [NotNullWhen(false)] out string?error)
 {
     try
     {
         return(base.TryValidateValues(input, values, context, out error));
     }
     catch (Exception ex)
     {
         this.Log(ex);
         error = null;
         return(true);
     }
 }
コード例 #9
0
        /// <inheritdoc />
        public override bool UpdateContext(IContext context)
        {
            return(this.IsChanged(this.Values, () =>
            {
                IEnumerable <string>?raw = this.GetValueImpl();
                this.Values = raw != null
                    ? InvariantSets.From(raw)
                    : InvariantSets.Empty;

                this.MarkReady(this.Values.Any());

                return this.Values;
            }));
        }
コード例 #10
0
        /*********
        ** Public methods
        *********/
        /// <summary>Get an immutable set for the given values.</summary>
        /// <param name="values">The values for which to get a set.</param>
        public static IInvariantSet From(IEnumerable <string> values)
        {
            // shortcut predefined values
            switch (values)
            {
            // already an invariant set
            case IInvariantSet set:
                return(set);

            // use predefined set if possible
            case IList <string> list:
                switch (list.Count)
                {
                case 0:
                    return(InvariantSets.Empty);

                case 1:
                    return(InvariantSets.FromValue(list[0]));

                case 2 when bool.TryParse(list[0], out bool left) && bool.TryParse(list[1], out bool right):
                    if (left != right)
                    {
                        return(InvariantSets.Boolean);
                    }
                    return(left
                                ? InvariantSets.True
                                : InvariantSets.False);
                }
                break;
            }

            // create custom set
            IInvariantSet result = values is MutableInvariantSet mutableSet
                ? mutableSet.GetImmutable()
                : new InvariantSet(values);

            return(result.Count == 0
                ? InvariantSets.Empty
                : result);
        }
コード例 #11
0
 /// <inheritdoc />
 public override bool HasBoundedValues(IInputArguments input, out IInvariantSet allowedValues)
 {
     allowedValues = InvariantSets.From(this.GetValues(input));
     return(true);
 }
コード例 #12
0
 /// <inheritdoc />
 public override bool HasBoundedValues(IInputArguments input, out IInvariantSet allowedValues)
 {
     allowedValues = this.ValidWeathers;
     return(true);
 }
コード例 #13
0
 /// <inheritdoc />
 public override bool HasBoundedValues(IInputArguments input, out IInvariantSet allowedValues)
 {
     allowedValues = InvariantSets.From(input.PositionalArgs);
     return(true);
 }
コード例 #14
0
        /// <summary>Load config values from the content pack.</summary>
        /// <param name="contentPack">The content pack whose config file to read.</param>
        /// <param name="config">The config schema.</param>
        /// <param name="logWarning">The callback to invoke on each validation warning, passed the field name and reason respectively.</param>
        private void LoadConfigValues(IContentPack contentPack, InvariantDictionary <ConfigField> config, Action <string, string> logWarning)
        {
            if (!config.Any())
            {
                return;
            }

            // read raw config
            InvariantDictionary <IInvariantSet> configValues = new(
                from entry in (contentPack.ReadJsonFile <InvariantDictionary <string> >(this.Filename) ?? new())
                let key = entry.Key.Trim()
                          let value = this.ParseCommaDelimitedField(entry.Value)
                                      select new KeyValuePair <string, IInvariantSet>(key, value)
                );

            // remove invalid values
            foreach (string key in configValues.Keys.ExceptIgnoreCase(config.Keys).ToArray())
            {
                logWarning(key, "no such field supported by this content pack.");
                configValues.Remove(key);
            }

            // inject default values
            foreach (string key in config.Keys)
            {
                ConfigField field = config[key];
                if (!configValues.TryGetValue(key, out IInvariantSet? values) || (!field.AllowBlank && !values.Any()))
                {
                    configValues[key] = field.DefaultValues;
                }
            }

            // parse each field
            foreach (string key in config.Keys)
            {
                ConfigField field = config[key];

                // validate allow-multiple
                if (!field.AllowMultiple && field.Value.Count > 1)
                {
                    logWarning(key, "field only allows a single value.");
                    field.SetValue(field.DefaultValues);
                    continue;
                }

                // validate allow-values
                if (field.AllowValues.Any())
                {
                    IInvariantSet invalidValues = field.Value.GetWithout(field.AllowValues);
                    if (invalidValues.Any())
                    {
                        logWarning(key, $"found invalid values ({string.Join(", ", invalidValues)}), expected: {string.Join(", ", field.AllowValues)}.");
                        field.SetValue(field.DefaultValues);
                        continue;
                    }
                }

                // save values
                field.SetValue(configValues[key]);
            }
        }
コード例 #15
0
 /// <inheritdoc />
 public override bool HasBoundedValues(IInputArguments input, out IInvariantSet allowedValues)
 {
     allowedValues = InvariantSets.Boolean;
     return(true);
 }
コード例 #16
0
 /// <summary>Override the config value.</summary>
 /// <param name="value">The config value to set.</param>
 public void SetValue(IInvariantSet value)
 {
     this.Value = value;
 }
コード例 #17
0
        /*********
        ** Private methods
        *********/
        /// <summary>Parse a raw config schema for a content pack.</summary>
        /// <param name="rawSchema">The raw config schema.</param>
        /// <param name="logWarning">The callback to invoke on each validation warning, passed the field name and reason respectively.</param>
        /// <param name="formatVersion">The content format version.</param>
        private InvariantDictionary <ConfigField> LoadConfigSchema(InvariantDictionary <ConfigSchemaFieldConfig?>?rawSchema, Action <string, string> logWarning, ISemanticVersion formatVersion)
        {
            InvariantDictionary <ConfigField> schema = new();

            if (rawSchema == null || !rawSchema.Any())
            {
                return(schema);
            }

            foreach (string rawKey in rawSchema.Keys)
            {
                ConfigSchemaFieldConfig?field = rawSchema[rawKey];
                if (field is null)
                {
                    continue;
                }

                // validate format
                if (string.IsNullOrWhiteSpace(rawKey))
                {
                    logWarning(rawKey, "the config field name can't be empty.");
                    continue;
                }
                if (rawKey.Contains(InternalConstants.PositionalInputArgSeparator) || rawKey.Contains(InternalConstants.NamedInputArgSeparator))
                {
                    logWarning(rawKey, $"the name '{rawKey}' can't have input arguments ({InternalConstants.PositionalInputArgSeparator} or {InternalConstants.NamedInputArgSeparator} character).");
                    continue;
                }

                // validate reserved keys
                if (Enum.TryParse <ConditionType>(rawKey, true, out _))
                {
                    logWarning(rawKey, $"can't use {rawKey} as a config field, because it's a reserved condition key.");
                    continue;
                }

                // read allowed/default values
                IInvariantSet allowValues   = this.ParseCommaDelimitedField(field.AllowValues);
                IInvariantSet defaultValues = this.ParseCommaDelimitedField(field.Default);

                // pre-1.7 behaviour
                if (formatVersion.IsOlderThan("1.7"))
                {
                    // allowed values are required
                    if (!allowValues.Any())
                    {
                        logWarning(rawKey, $"no {nameof(ConfigSchemaFieldConfig.AllowValues)} specified (and format version is less than 1.7).");
                        continue;
                    }

                    // inject default if needed
                    if (!defaultValues.Any() && !field.AllowBlank)
                    {
                        defaultValues = InvariantSets.FromValue(allowValues.First());
                    }
                }

                // validate allowed values
                if (!field.AllowBlank && !defaultValues.Any())
                {
                    logWarning(rawKey, $"if {nameof(field.AllowBlank)} is false, you must specify {nameof(field.Default)}.");
                    continue;
                }
                if (allowValues.Any() && defaultValues.Any())
                {
                    IInvariantSet invalidValues = defaultValues.GetWithout(allowValues);
                    if (invalidValues.Any())
                    {
                        logWarning(rawKey, $"default values '{string.Join(", ", invalidValues)}' are not allowed according to {nameof(ConfigSchemaFieldConfig.AllowValues)}.");
                        continue;
                    }
                }

                // validate allow multiple
                if (!field.AllowMultiple && defaultValues.Count > 1)
                {
                    logWarning(rawKey, $"can't have multiple default values because {nameof(ConfigSchemaFieldConfig.AllowMultiple)} is false.");
                    continue;
                }

                // add to schema
                schema[rawKey] = new ConfigField(
                    allowValues: allowValues,
                    defaultValues: defaultValues,
                    value: InvariantSets.Empty,
                    allowBlank: field.AllowBlank,
                    allowMultiple: field.AllowMultiple,
                    description: field.Description,
                    section: field.Section
                    );
            }

            return(schema);
        }