diff --git a/core/src/com/unciv/models/ruleset/validation/UniqueValidator.kt b/core/src/com/unciv/models/ruleset/validation/UniqueValidator.kt index b934bb9860..f6b0240e6b 100644 --- a/core/src/com/unciv/models/ruleset/validation/UniqueValidator.kt +++ b/core/src/com/unciv/models/ruleset/validation/UniqueValidator.kt @@ -15,6 +15,8 @@ import com.unciv.models.ruleset.unique.UniqueParameterType import com.unciv.models.ruleset.unique.UniqueTarget import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.expressions.Expressions +import yairm210.purity.annotations.Cache +import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly class UniqueValidator(val ruleset: Ruleset) { @@ -74,7 +76,7 @@ class UniqueValidator(val ruleset: Ruleset) { val prefix by lazy { getUniqueContainerPrefix(uniqueContainer) + "\"${unique.text}\"" } if (unique.type == null) return checkUntypedUnique(unique, tryFixUnknownUniques, uniqueContainer, prefix, reportRulesetSpecificErrors) - val rulesetErrors = RulesetErrorList(ruleset) + @LocalState val rulesetErrors = RulesetErrorList(ruleset) if (uniqueContainer != null && !(unique.type.canAcceptUniqueTarget(uniqueContainer.getUniqueTarget()) || @@ -107,11 +109,11 @@ class UniqueValidator(val ruleset: Ruleset) { complianceError.errorSeverity.getRulesetErrorSeverity(), uniqueContainer, unique ) - addExpressionParseErrors(complianceError, rulesetErrors, uniqueContainer, unique) + rulesetErrors += getExpressionParseErrors(complianceError, uniqueContainer, unique) } for (conditional in unique.modifiers) { - addConditionalErrors(conditional, rulesetErrors, prefix, unique, uniqueContainer, reportRulesetSpecificErrors) + rulesetErrors += getConditionalErrors(conditional, prefix, unique, uniqueContainer, reportRulesetSpecificErrors) } addUniqueTypeSpecificErrors(rulesetErrors, prefix, unique, uniqueContainer, reportRulesetSpecificErrors) @@ -128,7 +130,6 @@ class UniqueValidator(val ruleset: Ruleset) { } } - if (unique.type in MapUnitCache.UnitMovementUniques && unique.modifiers.any { it.type != UniqueType.ConditionalOurUnit || it.params[0] !in Constants.all } ) @@ -149,13 +150,14 @@ class UniqueValidator(val ruleset: Ruleset) { return rulesetErrors } - private fun addExpressionParseErrors( + @Readonly + private fun getExpressionParseErrors( complianceError: UniqueComplianceError, - rulesetErrors: RulesetErrorList, uniqueContainer: IHasUniques?, unique: Unique - ) { - if (!complianceError.acceptableParameterTypes.contains(UniqueParameterType.Countable)) return + ): RulesetErrorList { + @LocalState val rulesetErrors = RulesetErrorList() + if (!complianceError.acceptableParameterTypes.contains(UniqueParameterType.Countable)) return rulesetErrors val parseError = Expressions.getParsingError(complianceError.parameterName) if (parseError != null) { @@ -167,7 +169,7 @@ class UniqueValidator(val ruleset: Ruleset) { val text = "\"${complianceError.parameterName}\" could not be parsed as an expression due to:" + " ${parseError.message}. \n$parameterWithErrorLocationMarked" rulesetErrors.add(text, RulesetErrorSeverity.WarningOptionsOnly, uniqueContainer, unique) - return + return rulesetErrors } val countableErrors = Expressions.getCountableErrors(complianceError.parameterName, ruleset) @@ -176,6 +178,7 @@ class UniqueValidator(val ruleset: Ruleset) { " ${countableErrors.joinToString(", ")}" rulesetErrors.add(text, RulesetErrorSeverity.WarningOptionsOnly, uniqueContainer, unique) } + return rulesetErrors } private val resourceUniques = setOf(UniqueType.ProvidesResources, UniqueType.ConsumesResources, @@ -188,21 +191,21 @@ class UniqueValidator(val ruleset: Ruleset) { UniqueType.ConditionalWhenBelowAmountStatResource, ) - private fun addConditionalErrors( + private fun getConditionalErrors( conditional: Unique, - rulesetErrors: RulesetErrorList, prefix: String, unique: Unique, uniqueContainer: IHasUniques?, reportRulesetSpecificErrors: Boolean - ) { + ): RulesetErrorList { + @LocalState val rulesetErrors = RulesetErrorList() if (unique.hasFlag(UniqueFlag.NoConditionals)) { rulesetErrors.add( "$prefix contains the conditional \"${conditional.text}\"," + " but the unique does not accept conditionals!", RulesetErrorSeverity.Error, uniqueContainer, unique ) - return + return rulesetErrors } if (conditional.type == null) { @@ -221,7 +224,7 @@ class UniqueValidator(val ruleset: Ruleset) { text, RulesetErrorSeverity.Warning, uniqueContainer, unique ) - return + return rulesetErrors } if (conditional.type.targetTypes.none { it.modifierType != UniqueTarget.ModifierType.None }) @@ -276,10 +279,11 @@ class UniqueValidator(val ruleset: Ruleset) { complianceError.errorSeverity.getRulesetErrorSeverity(), uniqueContainer, unique ) - addExpressionParseErrors(complianceError, rulesetErrors, uniqueContainer, unique) + getExpressionParseErrors(complianceError, uniqueContainer, unique) } addDeprecationAnnotationErrors(conditional, "$prefix contains modifier \"${conditional.text}\" which", rulesetErrors, uniqueContainer) + return rulesetErrors } private fun addUniqueTypeSpecificErrors( @@ -334,11 +338,12 @@ class UniqueValidator(val ruleset: Ruleset) { } /** Maps uncompliant parameters to their required types */ + @Readonly private fun getComplianceErrors( unique: Unique, ): List { if (unique.type == null) return emptyList() - val errorList = ArrayList() + @LocalState val errorList = ArrayList() for ((index, param) in unique.params.withIndex()) { // Trying to catch the error at #11404 if (unique.type.parameterTypeMap.size != unique.params.size) { @@ -363,11 +368,12 @@ class UniqueValidator(val ruleset: Ruleset) { return errorList } - private val paramTypeErrorSeverityCache = HashMap>() + @Cache private val paramTypeErrorSeverityCache = HashMap>() + @Readonly private fun getParamTypeErrorSeverityCached(uniqueParameterType: UniqueParameterType, param: String): UniqueType.UniqueParameterErrorSeverity? { if (!paramTypeErrorSeverityCache.containsKey(uniqueParameterType)) paramTypeErrorSeverityCache[uniqueParameterType] = hashMapOf() - val uniqueParamCache = paramTypeErrorSeverityCache[uniqueParameterType]!! + @LocalState val uniqueParamCache = paramTypeErrorSeverityCache[uniqueParameterType]!! if (uniqueParamCache.containsKey(param)) return uniqueParamCache[param] @@ -376,6 +382,7 @@ class UniqueValidator(val ruleset: Ruleset) { return severity } + @Readonly private fun checkUntypedUnique( unique: Unique, tryFixUnknownUniques: Boolean, @@ -405,6 +412,7 @@ class UniqueValidator(val ruleset: Ruleset) { ) } + @Readonly private fun isFilteringUniqueAllowed(unique: Unique, reportRulesetSpecificErrors: Boolean): Boolean { // Isolate this decision, to allow easy change of approach // This says: Must have no conditionals or parameters, and is used in any "filtering" parameter of another Unique @@ -413,6 +421,7 @@ class UniqueValidator(val ruleset: Ruleset) { return unique.text in allUniqueParameters // referenced at least once from elsewhere } + @Readonly private fun tryFixUnknownUnique(unique: Unique, uniqueContainer: IHasUniques?, prefix: String): RulesetErrorList { val similarUniques = UniqueType.entries.filter { getRelativeTextDistance( @@ -450,6 +459,7 @@ class UniqueValidator(val ruleset: Ruleset) { companion object { const val whichDoesNotFitParameterType = "which does not fit parameter type" + @Readonly internal fun getUniqueContainerPrefix(uniqueContainer: IHasUniques?) = (if (uniqueContainer is IRulesetObject) "${uniqueContainer.originRuleset}: " else "") + (if (uniqueContainer == null) "The" else "(${uniqueContainer.getUniqueTarget().name}) ${uniqueContainer.name}'s") +