Exemplo n.º 1
0
        /// <summary>Validate whether a token referenced in a content pack field is valid.</summary>
        /// <param name="lexToken">The lexical token reference to check.</param>
        /// <param name="assumeModIds">Mod IDs to assume are installed for purposes of token validation.</param>
        /// <param name="error">An error phrase indicating why validation failed (if applicable).</param>
        public bool TryValidateToken(LexTokenToken lexToken, InvariantHashSet assumeModIds, out string error)
        {
            // token doesn't exist
            IToken token = this.Context.GetToken(lexToken.Name, enforceContext: false);

            if (token == null)
            {
                // special case: requires a missing mod that's checked via HasMod
                string requiredModId = lexToken.GetProviderModId();
                if (assumeModIds?.Contains(requiredModId) == true && !this.InstalledMods.Contains(requiredModId))
                {
                    error = null;
                    return(true);
                }

                // else error
                error = $"'{lexToken}' can't be used as a token because that token could not be found.";
                return(false);
            }

            // token requires dependency
            if (token is ModProvidedToken modToken && !this.ForMod.HasDependency(modToken.Mod.UniqueID, canBeOptional: false))
            {
                if (assumeModIds == null || !assumeModIds.Contains(modToken.Mod.UniqueID))
                {
                    error = $"'{lexToken}' can't be used because it's provided by {modToken.Mod.Name} (ID: {modToken.Mod.UniqueID}), but {this.ForMod.Name} doesn't list it as a dependency and the patch doesn't have an immutable {ConditionType.HasMod} condition for that mod.";
                    return(false);
                }
            }

            // no error found
            error = null;
            return(true);
        }
Exemplo n.º 2
0
        /// <summary>Parse a boolean <see cref="PatchConfig.Enabled"/> value from 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="tokenContext">The tokens available for this content pack.</param>
        /// <param name="migrator">The migrator which validates and migrates content pack data.</param>
        /// <param name="error">An error phrase indicating why parsing failed (if applicable).</param>
        /// <param name="parsed">The parsed value.</param>
        private bool TryParseEnabled(string rawValue, IContext tokenContext, IMigration migrator, out string error, out bool parsed)
        {
            parsed = false;

            // analyse string
            if (!this.TryParseStringTokens(rawValue, tokenContext, migrator, out error, out ITokenString tokenString))
            {
                return(false);
            }

            // validate & extract tokens
            string text = rawValue;

            if (tokenString.HasAnyTokens)
            {
                // only one token allowed
                if (!tokenString.IsSingleTokenOnly)
                {
                    error = "can't be treated as a true/false value because it contains multiple tokens.";
                    return(false);
                }

                // parse token
                LexTokenToken lexToken = tokenString.GetTokenPlaceholders(recursive: false).Single();
                IToken        token    = tokenContext.GetToken(lexToken.Name, enforceContext: false);
                ITokenString  input    = new TokenString(lexToken.InputArg, tokenContext);

                // check token options
                InvariantHashSet allowedValues = token?.GetAllowedValues(input);
                if (token == null || token.IsMutable || !token.IsReady)
                {
                    error = $"can only use static tokens in this field, consider using a {nameof(PatchConfig.When)} condition instead.";
                    return(false);
                }
                if (allowedValues == null || !allowedValues.All(p => bool.TryParse(p, out _)))
                {
                    error = "that token isn't restricted to 'true' or 'false'.";
                    return(false);
                }
                if (token.CanHaveMultipleValues(input))
                {
                    error = "can't be treated as a true/false value because that token can have multiple values.";
                    return(false);
                }

                text = token.GetValues(input).First();
            }

            // parse text
            if (!bool.TryParse(text, out parsed))
            {
                error = $"can't parse {tokenString.Raw} as a true/false value.";
                return(false);
            }
            return(true);
        }
Exemplo n.º 3
0
        /// <inheritdoc />
        public override bool TryMigrate(ref ILexToken lexToken, [NotNullWhen(false)] out string?error)
        {
            if (!base.TryMigrate(ref lexToken, out error))
            {
                return(false);
            }

            // ignore non-tokens
            if (lexToken is not LexTokenToken token || !Enum.TryParse(token.Name, ignoreCase: true, out ConditionType type))
            {
                return(true);
            }

            // 1.24 changes input arguments for player tokens
            if (token.HasInputArgs())
            {
                bool wasPlayerToken   = this.IsOldPlayerToken(type);
                bool isNewPlayerToken = !wasPlayerToken && this.IsNewPlayerToken(type);

                if (wasPlayerToken || isNewPlayerToken)
                {
                    var input = new InputArguments(new LiteralString(token.InputArgs.ToString(), new LogPathBuilder()));

                    // can't use player ID before 1.24
                    if (wasPlayerToken && long.TryParse(input.PositionalArgs.FirstOrDefault(), out _))
                    {
                        error = this.GetNounPhraseError($"using {type} token with a player ID");
                        return(false);
                    }

                    // didn't accept input before 1.24
                    if (isNewPlayerToken && input.PositionalArgs.Any())
                    {
                        error = this.GetNounPhraseError($"using {type} token with input arguments");
                        return(false);
                    }
                }
            }

            // 1.24 splits {{Roommate}} token out of {{Spouse}}
            if (type == ConditionType.Spouse)
            {
                lexToken = new LexTokenToken(
                    name: nameof(ConditionType.Merge),
                    inputArgs: new LexTokenInput(new ILexToken[]
                {
                    new LexTokenToken(nameof(ConditionType.Roommate), null, impliedBraces: false),
                    new LexTokenLiteral(","),
                    new LexTokenToken(nameof(ConditionType.Spouse), token.InputArgs, impliedBraces: false)
                }),
                    impliedBraces: token.ImpliedBraces
                    );
            }

            return(true);
        }
Exemplo n.º 4
0
        /// <inheritdoc />
        public override bool TryMigrate(ref ILexToken lexToken, [NotNullWhen(false)] out string?error)
        {
            if (!base.TryMigrate(ref lexToken, out error))
            {
                return(false);
            }

            string weatherName = nameof(ConditionType.Weather);

            // 1.20 adds input arguments for Weather token, and changes default from valley to current context
            if (lexToken is LexTokenToken token && token.Name.EqualsIgnoreCase(weatherName))
            {
                // can't add positional input args before 1.20
                if (token.HasInputArgs())
                {
                    var input = new InputArguments(new LiteralString(token.InputArgs.ToString(), new LogPathBuilder()));
                    if (input.HasPositionalArgs)
                    {
                        error = this.GetNounPhraseError($"using {weatherName} token with an input argument");
                        return(false);
                    }
                }

                // default to valley
                ILexToken[] valleyArg = new ILexToken[] { new LexTokenLiteral(LocationContext.Valley.ToString()) };
                ILexToken[]? inputParts = token.InputArgs?.Parts;
                lexToken = new LexTokenToken(
                    name: token.Name,
                    inputArgs: new LexTokenInput(
                        inputParts?.Any() == true
                            ? valleyArg.Concat(inputParts).ToArray()
                            : valleyArg
                        ),
                    impliedBraces: true
                    );
            }

            return(true);
        }