RulesetValidator reorg - instead of running 2 sets of unique checks for base rulesets and filtering out results from the first to get the second, we now separate and have one function for non-base-rulesets, and one for base rulesets.

This commit is contained in:
Yair Morgenstern 2023-10-30 12:37:07 +02:00
parent fae5e84675
commit eb3f92c989
7 changed files with 217 additions and 193 deletions

View File

@ -383,7 +383,7 @@ class Ruleset {
Unique(it), Unique(it),
false, false,
cityStateType, cityStateType,
UniqueType.UniqueParameterErrorSeverity.RulesetSpecific true
).isEmpty() ).isEmpty()
}) })
allyBonusUniques = ArrayList(cityStateType.allyBonusUniques.filter { allyBonusUniques = ArrayList(cityStateType.allyBonusUniques.filter {
@ -391,7 +391,7 @@ class Ruleset {
Unique(it), Unique(it),
false, false,
cityStateType, cityStateType,
UniqueType.UniqueParameterErrorSeverity.RulesetSpecific true
).isEmpty() ).isEmpty()
}) })
} }

View File

@ -122,7 +122,7 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
val unique = Unique(it) val unique = Unique(it)
val errors = UniqueValidator(ruleset).checkUnique( val errors = UniqueValidator(ruleset).checkUnique(
unique, true, null, unique, true, null,
UniqueType.UniqueParameterErrorSeverity.RulesetSpecific true
) )
errors.isEmpty() errors.isEmpty()
} }

View File

@ -47,6 +47,13 @@ enum class UniqueParameterType(
} }
}, },
Fraction("fraction", docExample = "0.5", "Indicates a fractional number, which can be negative"){
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? {
return if (parameterText.toFloatOrNull () == null) UniqueType.UniqueParameterErrorSeverity.RulesetInvariant
else null
}
},
RelativeNumber("relativeAmount", "+20", "This indicates a number, usually with a + or - sign, such as `+25` (this kind of parameter is often followed by '%' which is nevertheless not part of the value)") { RelativeNumber("relativeAmount", "+20", "This indicates a number, usually with a + or - sign, such as `+25` (this kind of parameter is often followed by '%' which is nevertheless not part of the value)") {
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
UniqueType.UniqueParameterErrorSeverity? { UniqueType.UniqueParameterErrorSeverity? {

View File

@ -529,7 +529,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
HasQuality("Considered [terrainQuality] when determining start locations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), HasQuality("Considered [terrainQuality] when determining start locations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
NoNaturalGeneration("Doesn't generate naturally", UniqueTarget.Terrain, UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers), NoNaturalGeneration("Doesn't generate naturally", UniqueTarget.Terrain, UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers),
TileGenerationConditions("Occurs at temperature between [amount] and [amount] and humidity between [amount] and [amount]", UniqueTarget.Terrain, UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers), TileGenerationConditions("Occurs at temperature between [fraction] and [amount] and humidity between [amount] and [amount]", UniqueTarget.Terrain, UniqueTarget.Resource, flags = UniqueFlag.setOfHiddenToUsers),
OccursInChains("Occurs in chains at high elevations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), OccursInChains("Occurs in chains at high elevations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
OccursInGroups("Occurs in groups around high elevations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), OccursInGroups("Occurs in groups around high elevations", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
MajorStrategicFrequency("Every [amount] tiles with this terrain will receive a major deposit of a strategic resource.", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers), MajorStrategicFrequency("Every [amount] tiles with this terrain will receive a major deposit of a strategic resource.", UniqueTarget.Terrain, flags = UniqueFlag.setOfHiddenToUsers),
@ -1174,8 +1174,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
/** This is a warning, regardless of what ruleset we're in. /** This is a warning, regardless of what ruleset we're in.
* This is for filters that can also potentially accept free text, like UnitFilter and TileFilter */ * This is for filters that can also potentially accept free text, like UnitFilter and TileFilter */
WarningOnly { WarningOnly {
override fun getRulesetErrorSeverity(severityToReport: UniqueParameterErrorSeverity) = override fun getRulesetErrorSeverity() = RulesetErrorSeverity.WarningOptionsOnly
RulesetErrorSeverity.WarningOptionsOnly
}, },
/** An error, but only because of other information in the current ruleset. /** An error, but only because of other information in the current ruleset.
@ -1184,15 +1183,13 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
RulesetSpecific { RulesetSpecific {
// Report Warning on the first pass of RulesetValidator only, where mods are checked standalone // Report Warning on the first pass of RulesetValidator only, where mods are checked standalone
// but upgrade to error when the econd pass asks, which runs only for combined or base rulesets. // but upgrade to error when the econd pass asks, which runs only for combined or base rulesets.
override fun getRulesetErrorSeverity(severityToReport: UniqueParameterErrorSeverity) = override fun getRulesetErrorSeverity() = RulesetErrorSeverity.Warning
RulesetErrorSeverity.Warning
}, },
/** An error, regardless of the ruleset we're in. /** An error, regardless of the ruleset we're in.
* This is a problem like "numbers don't parse", "stat isn't stat", "city filter not applicable" */ * This is a problem like "numbers don't parse", "stat isn't stat", "city filter not applicable" */
RulesetInvariant { RulesetInvariant {
override fun getRulesetErrorSeverity(severityToReport: UniqueParameterErrorSeverity) = override fun getRulesetErrorSeverity() = RulesetErrorSeverity.Error
RulesetErrorSeverity.Error
}, },
; ;
@ -1201,7 +1198,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
* first pass that also runs for extension mods without a base mixed in; the complex check * first pass that also runs for extension mods without a base mixed in; the complex check
* runs with [severityToReport]==[RulesetSpecific]. * runs with [severityToReport]==[RulesetSpecific].
*/ */
abstract fun getRulesetErrorSeverity(severityToReport: UniqueParameterErrorSeverity): RulesetErrorSeverity abstract fun getRulesetErrorSeverity(): RulesetErrorSeverity
} }
fun getDeprecationAnnotation(): Deprecated? = declaringJavaClass.getField(name) fun getDeprecationAnnotation(): Deprecated? = declaringJavaClass.getField(name)

View File

@ -8,8 +8,10 @@ import com.unciv.json.fromJsonFile
import com.unciv.json.json import com.unciv.json.json
import com.unciv.logic.map.tile.RoadStatus import com.unciv.logic.map.tile.RoadStatus
import com.unciv.models.metadata.BaseRuleset import com.unciv.models.metadata.BaseRuleset
import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.ruleset.nation.Nation
import com.unciv.models.ruleset.nation.getContrastRatio import com.unciv.models.ruleset.nation.getContrastRatio
import com.unciv.models.ruleset.nation.getRelativeLuminance import com.unciv.models.ruleset.nation.getRelativeLuminance
import com.unciv.models.ruleset.tile.TerrainType import com.unciv.models.ruleset.tile.TerrainType
@ -23,66 +25,75 @@ import com.unciv.models.tilesets.TileSetConfig
class RulesetValidator(val ruleset: Ruleset) { class RulesetValidator(val ruleset: Ruleset) {
val uniqueValidator = UniqueValidator(ruleset) private val uniqueValidator = UniqueValidator(ruleset)
fun getErrorList(tryFixUnknownUniques: Boolean = false): RulesetErrorList { fun getErrorList(tryFixUnknownUniques: Boolean = false): RulesetErrorList {
// When no base ruleset is loaded - references cannot be checked
if (!ruleset.modOptions.isBaseRuleset) return getNonBaseRulesetErrorList(tryFixUnknownUniques)
return getBaseRulesetErrorList(tryFixUnknownUniques)
}
private fun getNonBaseRulesetErrorList(tryFixUnknownUniques: Boolean): RulesetErrorList {
val lines = RulesetErrorList() val lines = RulesetErrorList()
/********************** Ruleset Invariant Part **********************/
// Checks for ALL MODS - only those that can succeed without loading a base ruleset
// When not checking the entire ruleset, we can only really detect ruleset-invariant errors in uniques // When not checking the entire ruleset, we can only really detect ruleset-invariant errors in uniques
uniqueValidator.checkUniques(ruleset.globalUniques, lines, false, tryFixUnknownUniques)
val rulesetInvariant = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant addUnitErrorsRulesetInvariant(lines, tryFixUnknownUniques)
val rulesetSpecific = UniqueType.UniqueParameterErrorSeverity.RulesetSpecific addTechErrorsRulesetInvariant(lines, tryFixUnknownUniques)
uniqueValidator.checkUniques(ruleset.globalUniques, lines, rulesetInvariant, tryFixUnknownUniques)
addUnitErrorsRulesetInvariant(lines, rulesetInvariant, tryFixUnknownUniques)
addTechErrorsRulesetInvariant(lines, rulesetInvariant, tryFixUnknownUniques)
addTechColumnErrorsRulesetInvariant(lines) addTechColumnErrorsRulesetInvariant(lines)
addBuildingErrorsRulesetInvariant(lines, rulesetInvariant, tryFixUnknownUniques) addBuildingErrorsRulesetInvariant(lines, tryFixUnknownUniques)
addNationErrorsRulesetInvariant(lines, rulesetInvariant, tryFixUnknownUniques) addNationErrorsRulesetInvariant(lines, tryFixUnknownUniques)
addPromotionErrorsRulesetInvariant(lines, rulesetInvariant, tryFixUnknownUniques) addPromotionErrorsRulesetInvariant(lines, tryFixUnknownUniques)
addResourceErrorsRulesetInvariant(lines, rulesetInvariant, tryFixUnknownUniques) addResourceErrorsRulesetInvariant(lines, tryFixUnknownUniques)
/********************** Tileset tests **********************/ /********************** **********************/
// e.g. json configs complete and parseable // e.g. json configs complete and parseable
// Check for mod or Civ_V_GnK to avoid running the same test twice (~200ms for the builtin assets)
if (ruleset.folderLocation != null) {
checkTilesetSanity(lines)
}
return lines
}
private fun getBaseRulesetErrorList(tryFixUnknownUniques: Boolean): RulesetErrorList{
val lines = RulesetErrorList()
uniqueValidator.checkUniques(ruleset.globalUniques, lines, true, tryFixUnknownUniques)
addUnitErrorsBaseRuleset(lines, tryFixUnknownUniques)
addBuildingErrors(lines, tryFixUnknownUniques)
addSpecialistErrors(lines)
addResourceErrors(lines, tryFixUnknownUniques)
addImprovementErrors(lines, tryFixUnknownUniques)
addTerrainErrors(lines, tryFixUnknownUniques)
addTechErrors(lines, tryFixUnknownUniques)
addTechColumnErrorsRulesetInvariant(lines)
addEraErrors(lines, tryFixUnknownUniques)
addSpeedErrors(lines)
addBeliefErrors(lines, tryFixUnknownUniques)
addNationErrors(lines, tryFixUnknownUniques)
addPolicyErrors(lines, tryFixUnknownUniques)
addRuinsErrors(lines, tryFixUnknownUniques)
addPromotionErrors(lines, tryFixUnknownUniques)
addUnitTypeErrors(lines, tryFixUnknownUniques)
addVictoryTypeErrors(lines)
addDifficutlyErrors(lines)
addCityStateTypeErrors(tryFixUnknownUniques, lines)
// Check for mod or Civ_V_GnK to avoid running the same test twice (~200ms for the builtin assets) // Check for mod or Civ_V_GnK to avoid running the same test twice (~200ms for the builtin assets)
if (ruleset.folderLocation != null || ruleset.name == BaseRuleset.Civ_V_GnK.fullName) { if (ruleset.folderLocation != null || ruleset.name == BaseRuleset.Civ_V_GnK.fullName) {
checkTilesetSanity(lines) checkTilesetSanity(lines)
} }
// Quit here when no base ruleset is loaded - references cannot be checked
if (!ruleset.modOptions.isBaseRuleset) return lines
/********************** Ruleset Specific Part **********************/
uniqueValidator.checkUniques(ruleset.globalUniques, lines, rulesetSpecific, tryFixUnknownUniques)
addUnitErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addBuildingErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addSpecialistErrors(lines)
addResourceErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addImprovementErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addTerrainErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addTechErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addEraErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addSpeedErrors(lines)
addBeliefErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addNationErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addPolicyErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addRuinsErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addPromotionErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addUnitTypeErrors(lines, rulesetSpecific, tryFixUnknownUniques)
addVictoryTypeErrors(lines)
addDifficutlyErrors(lines)
addCityStateTypeErrors(tryFixUnknownUniques, rulesetSpecific, lines)
return lines return lines
} }
private fun addCityStateTypeErrors( private fun addCityStateTypeErrors(
tryFixUnknownUniques: Boolean, tryFixUnknownUniques: Boolean,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
lines: RulesetErrorList lines: RulesetErrorList
) { ) {
for (cityStateType in ruleset.cityStateTypes.values) { for (cityStateType in ruleset.cityStateTypes.values) {
@ -91,7 +102,7 @@ class RulesetValidator(val ruleset: Ruleset) {
unique, unique,
tryFixUnknownUniques, tryFixUnknownUniques,
cityStateType, cityStateType,
rulesetSpecific true
) )
lines.addAll(errors) lines.addAll(errors)
} }
@ -131,20 +142,20 @@ class RulesetValidator(val ruleset: Ruleset) {
private fun addUnitTypeErrors( private fun addUnitTypeErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (unitType in ruleset.unitTypes.values) { for (unitType in ruleset.unitTypes.values) {
uniqueValidator.checkUniques(unitType, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(unitType, lines, true, tryFixUnknownUniques)
} }
} }
private fun addPromotionErrors( private fun addPromotionErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (promotion in ruleset.unitPromotions.values) { for (promotion in ruleset.unitPromotions.values) {
addPromotionErrorRulesetInvariant(promotion, lines)
// These are warning as of 3.17.5 to not break existing mods and give them time to correct, should be upgraded to error in the future // These are warning as of 3.17.5 to not break existing mods and give them time to correct, should be upgraded to error in the future
for (prereq in promotion.prerequisites) for (prereq in promotion.prerequisites)
if (!ruleset.unitPromotions.containsKey(prereq)) if (!ruleset.unitPromotions.containsKey(prereq))
@ -158,27 +169,25 @@ class RulesetValidator(val ruleset: Ruleset) {
RulesetErrorSeverity.Warning RulesetErrorSeverity.Warning
) )
} }
uniqueValidator.checkUniques(promotion, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(promotion, lines, true, tryFixUnknownUniques)
} }
checkPromotionCircularReferences(lines) checkPromotionCircularReferences(lines)
} }
private fun addRuinsErrors( private fun addRuinsErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (reward in ruleset.ruinRewards.values) { for (reward in ruleset.ruinRewards.values) {
for (difficulty in reward.excludedDifficulties) for (difficulty in reward.excludedDifficulties)
if (!ruleset.difficulties.containsKey(difficulty)) if (!ruleset.difficulties.containsKey(difficulty))
lines += "${reward.name} references difficulty ${difficulty}, which does not exist!" lines += "${reward.name} references difficulty ${difficulty}, which does not exist!"
uniqueValidator.checkUniques(reward, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(reward, lines, true, tryFixUnknownUniques)
} }
} }
private fun addPolicyErrors( private fun addPolicyErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (policy in ruleset.policies.values) { for (policy in ruleset.policies.values) {
@ -186,7 +195,7 @@ class RulesetValidator(val ruleset: Ruleset) {
for (prereq in policy.requires!!) for (prereq in policy.requires!!)
if (!ruleset.policies.containsKey(prereq)) if (!ruleset.policies.containsKey(prereq))
lines += "${policy.name} requires policy $prereq which does not exist!" lines += "${policy.name} requires policy $prereq which does not exist!"
uniqueValidator.checkUniques(policy, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(policy, lines, true, tryFixUnknownUniques)
} }
for (branch in ruleset.policyBranches.values) for (branch in ruleset.policyBranches.values)
@ -202,11 +211,12 @@ class RulesetValidator(val ruleset: Ruleset) {
private fun addNationErrors( private fun addNationErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (nation in ruleset.nations.values) { for (nation in ruleset.nations.values) {
uniqueValidator.checkUniques(nation, lines, rulesetSpecific, tryFixUnknownUniques) addNationErrorRulesetInvariant(nation, lines)
uniqueValidator.checkUniques(nation, lines, true, tryFixUnknownUniques)
if (nation.cityStateType != null && nation.cityStateType !in ruleset.cityStateTypes) if (nation.cityStateType != null && nation.cityStateType !in ruleset.cityStateTypes)
lines += "${nation.name} is of city-state type ${nation.cityStateType} which does not exist!" lines += "${nation.name} is of city-state type ${nation.cityStateType} which does not exist!"
@ -217,11 +227,10 @@ class RulesetValidator(val ruleset: Ruleset) {
private fun addBeliefErrors( private fun addBeliefErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (belief in ruleset.beliefs.values) { for (belief in ruleset.beliefs.values) {
uniqueValidator.checkUniques(belief, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(belief, lines, true, tryFixUnknownUniques)
} }
} }
@ -236,7 +245,6 @@ class RulesetValidator(val ruleset: Ruleset) {
private fun addEraErrors( private fun addEraErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
if (ruleset.eras.isEmpty()) { if (ruleset.eras.isEmpty()) {
@ -288,13 +296,12 @@ class RulesetValidator(val ruleset: Ruleset) {
RulesetErrorSeverity.WarningOptionsOnly RulesetErrorSeverity.WarningOptionsOnly
) )
uniqueValidator.checkUniques(era, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(era, lines, true, tryFixUnknownUniques)
} }
} }
private fun addTechErrors( private fun addTechErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (tech in ruleset.technologies.values) { for (tech in ruleset.technologies.values) {
@ -316,13 +323,12 @@ class RulesetValidator(val ruleset: Ruleset) {
} }
if (tech.era() !in ruleset.eras) if (tech.era() !in ruleset.eras)
lines += "Unknown era ${tech.era()} referenced in column of tech ${tech.name}" lines += "Unknown era ${tech.era()} referenced in column of tech ${tech.name}"
uniqueValidator.checkUniques(tech, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(tech, lines, true, tryFixUnknownUniques)
} }
} }
private fun addTerrainErrors( private fun addTerrainErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
if (ruleset.terrains.values.none { it.type == TerrainType.Land && !it.impassable }) if (ruleset.terrains.values.none { it.type == TerrainType.Land && !it.impassable })
@ -345,13 +351,12 @@ class RulesetValidator(val ruleset: Ruleset) {
// See https://github.com/hackedpassword/Z2/blob/main/HybridTileTech.md for a clever exploit // See https://github.com/hackedpassword/Z2/blob/main/HybridTileTech.md for a clever exploit
lines.add("${terrain.name} turns into terrain ${terrain.turnsInto} which is not a base terrain!", RulesetErrorSeverity.Warning) lines.add("${terrain.name} turns into terrain ${terrain.turnsInto} which is not a base terrain!", RulesetErrorSeverity.Warning)
} }
uniqueValidator.checkUniques(terrain, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(terrain, lines, true, tryFixUnknownUniques)
} }
} }
private fun addImprovementErrors( private fun addImprovementErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (improvement in ruleset.tileImprovements.values) { for (improvement in ruleset.tileImprovements.values) {
@ -400,13 +405,12 @@ class RulesetValidator(val ruleset: Ruleset) {
RulesetErrorSeverity.Warning RulesetErrorSeverity.Warning
) )
} }
uniqueValidator.checkUniques(improvement, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(improvement, lines, true, tryFixUnknownUniques)
} }
} }
private fun addResourceErrors( private fun addResourceErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (resource in ruleset.tileResources.values) { for (resource in ruleset.tileResources.values) {
@ -420,7 +424,7 @@ class RulesetValidator(val ruleset: Ruleset) {
for (terrain in resource.terrainsCanBeFoundOn) for (terrain in resource.terrainsCanBeFoundOn)
if (!ruleset.terrains.containsKey(terrain)) if (!ruleset.terrains.containsKey(terrain))
lines += "${resource.name} can be found on terrain $terrain which does not exist!" lines += "${resource.name} can be found on terrain $terrain which does not exist!"
uniqueValidator.checkUniques(resource, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(resource, lines, true, tryFixUnknownUniques)
} }
} }
@ -437,13 +441,13 @@ class RulesetValidator(val ruleset: Ruleset) {
private fun addBuildingErrors( private fun addBuildingErrors(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (building in ruleset.buildings.values) { for (building in ruleset.buildings.values) {
addBuildingErrorRulesetInvariant(building, lines)
if (building.requiredTech != null && !ruleset.technologies.containsKey(building.requiredTech!!)) if (building.requiredTech != null && !ruleset.technologies.containsKey(building.requiredTech!!))
lines += "${building.name} requires tech ${building.requiredTech} which does not exist!" lines += "${building.name} requires tech ${building.requiredTech} which does not exist!"
for (specialistName in building.specialistSlots.keys) for (specialistName in building.specialistSlots.keys)
if (!ruleset.specialists.containsKey(specialistName)) if (!ruleset.specialists.containsKey(specialistName))
lines += "${building.name} provides specialist $specialistName which does not exist!" lines += "${building.name} provides specialist $specialistName which does not exist!"
@ -454,127 +458,147 @@ class RulesetValidator(val ruleset: Ruleset) {
lines += "${building.name} replaces ${building.replaces} which does not exist!" lines += "${building.name} replaces ${building.replaces} which does not exist!"
if (building.requiredBuilding != null && !ruleset.buildings.containsKey(building.requiredBuilding!!)) if (building.requiredBuilding != null && !ruleset.buildings.containsKey(building.requiredBuilding!!))
lines += "${building.name} requires ${building.requiredBuilding} which does not exist!" lines += "${building.name} requires ${building.requiredBuilding} which does not exist!"
uniqueValidator.checkUniques(building, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(building, lines, true, tryFixUnknownUniques)
} }
} }
private fun addUnitErrors(
private fun addUnitErrorsRulesetInvariant(
lines: RulesetErrorList,
tryFixUnknownUniques: Boolean
) {
for (unit in ruleset.units.values) {
checkUnitRulesetInvariant(unit, lines)
uniqueValidator.checkUniques(unit, lines, false, tryFixUnknownUniques)
}
}
private fun addUnitErrorsBaseRuleset(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetSpecific: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
if (ruleset.units.values.none { it.isCityFounder() }) if (ruleset.units.values.none { it.isCityFounder() })
lines += "No city-founding units in ruleset!" lines += "No city-founding units in ruleset!"
for (unit in ruleset.units.values) { for (unit in ruleset.units.values) {
checkUnitRulesetInvariant(unit, lines)
checkUnitRulesetSpecific(unit, lines) checkUnitRulesetSpecific(unit, lines)
uniqueValidator.checkUniques(unit, lines, rulesetSpecific, tryFixUnknownUniques) uniqueValidator.checkUniques(unit, lines, false, tryFixUnknownUniques)
} }
} }
private fun addResourceErrorsRulesetInvariant( private fun addResourceErrorsRulesetInvariant(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetInvariant: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (resource in ruleset.tileResources.values) { for (resource in ruleset.tileResources.values) {
uniqueValidator.checkUniques(resource, lines, rulesetInvariant, tryFixUnknownUniques) uniqueValidator.checkUniques(resource, lines, false, tryFixUnknownUniques)
} }
} }
private fun addPromotionErrorsRulesetInvariant( private fun addPromotionErrorsRulesetInvariant(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetInvariant: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (promotion in ruleset.unitPromotions.values) { for (promotion in ruleset.unitPromotions.values) {
uniqueValidator.checkUniques(promotion, lines, rulesetInvariant, tryFixUnknownUniques) uniqueValidator.checkUniques(promotion, lines, false, tryFixUnknownUniques)
if (promotion.row < -1) lines += "Promotion ${promotion.name} has invalid row value: ${promotion.row}"
if (promotion.column < 0) lines += "Promotion ${promotion.name} has invalid column value: ${promotion.column}" addPromotionErrorRulesetInvariant(promotion, lines)
if (promotion.row == -1) continue
for (otherPromotion in ruleset.unitPromotions.values)
if (promotion != otherPromotion && promotion.column == otherPromotion.column && promotion.row == otherPromotion.row)
lines += "Promotions ${promotion.name} and ${otherPromotion.name} have the same position: ${promotion.row}/${promotion.column}"
} }
} }
private fun addPromotionErrorRulesetInvariant(promotion: Promotion, lines: RulesetErrorList) {
if (promotion.row < -1) lines += "Promotion ${promotion.name} has invalid row value: ${promotion.row}"
if (promotion.column < 0) lines += "Promotion ${promotion.name} has invalid column value: ${promotion.column}"
if (promotion.row == -1) return
for (otherPromotion in ruleset.unitPromotions.values)
if (promotion != otherPromotion && promotion.column == otherPromotion.column && promotion.row == otherPromotion.row)
lines += "Promotions ${promotion.name} and ${otherPromotion.name} have the same position: ${promotion.row}/${promotion.column}"
}
private fun addNationErrorsRulesetInvariant( private fun addNationErrorsRulesetInvariant(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetInvariant: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (nation in ruleset.nations.values) { for (nation in ruleset.nations.values) {
if (nation.cities.isEmpty() && !nation.isSpectator && !nation.isBarbarian) { addNationErrorRulesetInvariant(nation, lines)
lines += "${nation.name} can settle cities, but has no city names!"
uniqueValidator.checkUniques(nation, lines, false, tryFixUnknownUniques)
}
}
private fun addNationErrorRulesetInvariant(nation: Nation, lines: RulesetErrorList) {
if (nation.cities.isEmpty() && !nation.isSpectator && !nation.isBarbarian) {
lines += "${nation.name} can settle cities, but has no city names!"
}
// https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast
val constrastRatio = nation.getContrastRatio()
if (constrastRatio < 3) {
val innerColorLuminance = getRelativeLuminance(nation.getInnerColor())
val outerColorLuminance = getRelativeLuminance(nation.getOuterColor())
val innerLerpColor: Color
val outerLerpColor: Color
if (innerColorLuminance > outerColorLuminance) { // inner is brighter
innerLerpColor = Color.WHITE
outerLerpColor = Color.BLACK
} else {
innerLerpColor = Color.BLACK
outerLerpColor = Color.WHITE
} }
// https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast var text = "${nation.name}'s colors do not contrast enough - it is unreadable!"
val constrastRatio = nation.getContrastRatio()
if (constrastRatio < 3) {
val innerColorLuminance = getRelativeLuminance(nation.getInnerColor())
val outerColorLuminance = getRelativeLuminance(nation.getOuterColor())
val innerLerpColor: Color for (i in 1..10) {
val outerLerpColor: Color val newInnerColor = nation.getInnerColor().cpy().lerp(innerLerpColor, 0.05f * i)
val newOuterColor = nation.getOuterColor().cpy().lerp(outerLerpColor, 0.05f * i)
if (innerColorLuminance > outerColorLuminance) { // inner is brighter if (getContrastRatio(newInnerColor, newOuterColor) > 3) {
innerLerpColor = Color.WHITE text += "\nSuggested colors: "
outerLerpColor = Color.BLACK text += "\n\t\t\"outerColor\": [${(newOuterColor.r * 255).toInt()}, ${(newOuterColor.g * 255).toInt()}, ${(newOuterColor.b * 255).toInt()}],"
} else { text += "\n\t\t\"innerColor\": [${(newInnerColor.r * 255).toInt()}, ${(newInnerColor.g * 255).toInt()}, ${(newInnerColor.b * 255).toInt()}],"
innerLerpColor = Color.BLACK break
outerLerpColor = Color.WHITE
} }
var text = "${nation.name}'s colors do not contrast enough - it is unreadable!"
for (i in 1..10) {
val newInnerColor = nation.getInnerColor().cpy().lerp(innerLerpColor, 0.05f * i)
val newOuterColor = nation.getOuterColor().cpy().lerp(outerLerpColor, 0.05f * i)
if (getContrastRatio(newInnerColor, newOuterColor) > 3) {
text += "\nSuggested colors: "
text += "\n\t\t\"outerColor\": [${(newOuterColor.r * 255).toInt()}, ${(newOuterColor.g * 255).toInt()}, ${(newOuterColor.b * 255).toInt()}],"
text += "\n\t\t\"innerColor\": [${(newInnerColor.r * 255).toInt()}, ${(newInnerColor.g * 255).toInt()}, ${(newInnerColor.b * 255).toInt()}],"
break
}
}
lines.add(
text, RulesetErrorSeverity.WarningOptionsOnly
)
} }
uniqueValidator.checkUniques(nation, lines, rulesetInvariant, tryFixUnknownUniques) lines.add(
text, RulesetErrorSeverity.WarningOptionsOnly
)
} }
} }
private fun addBuildingErrorsRulesetInvariant( private fun addBuildingErrorsRulesetInvariant(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetInvariant: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (building in ruleset.buildings.values) { for (building in ruleset.buildings.values) {
if (building.requiredTech == null && building.cost == -1 && !building.hasUnique( addBuildingErrorRulesetInvariant(building, lines)
UniqueType.Unbuildable
)
)
lines.add(
"${building.name} is buildable and therefore should either have an explicit cost or reference an existing tech!",
RulesetErrorSeverity.Warning
)
for (gpp in building.greatPersonPoints) uniqueValidator.checkUniques(building, lines, false, tryFixUnknownUniques)
if (gpp.key !in ruleset.units)
lines.add(
"Building ${building.name} has greatPersonPoints for ${gpp.key}, which is not a unit in the ruleset!",
RulesetErrorSeverity.Warning
)
uniqueValidator.checkUniques(building, lines, rulesetInvariant, tryFixUnknownUniques)
} }
} }
private fun addBuildingErrorRulesetInvariant(building: Building, lines: RulesetErrorList) {
if (building.requiredTech == null && building.cost == -1 && !building.hasUnique(
UniqueType.Unbuildable
)
)
lines.add(
"${building.name} is buildable and therefore should either have an explicit cost or reference an existing tech!",
RulesetErrorSeverity.Warning
)
for (gpp in building.greatPersonPoints)
if (gpp.key !in ruleset.units)
lines.add(
"Building ${building.name} has greatPersonPoints for ${gpp.key}, which is not a unit in the ruleset!",
RulesetErrorSeverity.Warning
)
}
private fun addTechColumnErrorsRulesetInvariant(lines: RulesetErrorList) { private fun addTechColumnErrorsRulesetInvariant(lines: RulesetErrorList) {
for (techColumn in ruleset.techColumns) { for (techColumn in ruleset.techColumns) {
if (techColumn.columnNumber < 0) if (techColumn.columnNumber < 0)
@ -590,40 +614,25 @@ class RulesetValidator(val ruleset: Ruleset) {
RulesetErrorSeverity.Warning RulesetErrorSeverity.Warning
) )
} }
}
private fun addTechErrorsRulesetInvariant(
lines: RulesetErrorList,
rulesetInvariant: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean
) {
for (tech in ruleset.technologies.values) { for (tech in ruleset.technologies.values) {
for (otherTech in ruleset.technologies.values) { for (otherTech in ruleset.technologies.values) {
if (tech != otherTech && otherTech.column?.columnNumber == tech.column?.columnNumber && otherTech.row == tech.row) if (tech != otherTech && otherTech.column?.columnNumber == tech.column?.columnNumber && otherTech.row == tech.row)
lines += "${tech.name} is in the same row and column as ${otherTech.name}!" lines += "${tech.name} is in the same row and column as ${otherTech.name}!"
} }
uniqueValidator.checkUniques(tech, lines, rulesetInvariant, tryFixUnknownUniques)
} }
} }
private fun addUnitErrorsRulesetInvariant( private fun addTechErrorsRulesetInvariant(
lines: RulesetErrorList, lines: RulesetErrorList,
rulesetInvariant: UniqueType.UniqueParameterErrorSeverity,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (unit in ruleset.units.values) { for (tech in ruleset.technologies.values) {
if (unit.upgradesTo == unit.name || (unit.upgradesTo != null && unit.upgradesTo == unit.replaces)) uniqueValidator.checkUniques(tech, lines, false, tryFixUnknownUniques)
lines += "${unit.name} upgrades to itself!"
if (!unit.isCivilian() && unit.strength == 0)
lines += "${unit.name} is a military unit but has no assigned strength!"
if (unit.isRanged() && unit.rangedStrength == 0 && !unit.hasUnique(UniqueType.CannotAttack))
lines += "${unit.name} is a ranged unit but has no assigned rangedStrength!"
uniqueValidator.checkUniques(unit, lines, rulesetInvariant, tryFixUnknownUniques)
} }
} }
/** Collects known technology prerequisite paths: key is the technology name, /** Collects known technology prerequisite paths: key is the technology name,
* value a Set of its prerequisites including indirect ones */ * value a Set of its prerequisites including indirect ones */
private val prereqsHashMap = HashMap<String, HashSet<String>>() private val prereqsHashMap = HashMap<String, HashSet<String>>()
@ -640,6 +649,15 @@ class RulesetValidator(val ruleset: Ruleset) {
return techHashSet return techHashSet
} }
private fun checkUnitRulesetInvariant(unit: BaseUnit, lines: RulesetErrorList) {
if (unit.upgradesTo == unit.name || (unit.upgradesTo != null && unit.upgradesTo == unit.replaces))
lines += "${unit.name} upgrades to itself!"
if (!unit.isCivilian() && unit.strength == 0)
lines += "${unit.name} is a military unit but has no assigned strength!"
if (unit.isRanged() && unit.rangedStrength == 0 && !unit.hasUnique(UniqueType.CannotAttack))
lines += "${unit.name} is a ranged unit but has no assigned rangedStrength!"
}
/** Collects all RulesetSpecific checks for a BaseUnit */ /** Collects all RulesetSpecific checks for a BaseUnit */
private fun checkUnitRulesetSpecific(unit: BaseUnit, lines: RulesetErrorList) { private fun checkUnitRulesetSpecific(unit: BaseUnit, lines: RulesetErrorList) {
if (unit.requiredTech != null && !ruleset.technologies.containsKey(unit.requiredTech!!)) if (unit.requiredTech != null && !ruleset.technologies.containsKey(unit.requiredTech!!))

View File

@ -16,7 +16,7 @@ class UniqueValidator(val ruleset: Ruleset) {
fun checkUniques( fun checkUniques(
uniqueContainer: IHasUniques, uniqueContainer: IHasUniques,
lines: RulesetErrorList, lines: RulesetErrorList,
severityToReport: UniqueType.UniqueParameterErrorSeverity, reportRulesetSpecificErrors: Boolean,
tryFixUnknownUniques: Boolean tryFixUnknownUniques: Boolean
) { ) {
for (unique in uniqueContainer.uniqueObjects) { for (unique in uniqueContainer.uniqueObjects) {
@ -24,7 +24,7 @@ class UniqueValidator(val ruleset: Ruleset) {
unique, unique,
tryFixUnknownUniques, tryFixUnknownUniques,
uniqueContainer as? INamed, uniqueContainer as? INamed,
severityToReport reportRulesetSpecificErrors
) )
lines.addAll(errors) lines.addAll(errors)
} }
@ -34,7 +34,7 @@ class UniqueValidator(val ruleset: Ruleset) {
unique: Unique, unique: Unique,
tryFixUnknownUniques: Boolean, tryFixUnknownUniques: Boolean,
namedObj: INamed?, namedObj: INamed?,
severityToReport: UniqueType.UniqueParameterErrorSeverity reportRulesetSpecificErrors: Boolean
): List<RulesetError> { ): List<RulesetError> {
val prefix by lazy { (if (namedObj is IRulesetObject) "${namedObj.originRuleset}: " else "") + val prefix by lazy { (if (namedObj is IRulesetObject) "${namedObj.originRuleset}: " else "") +
(if (namedObj == null) "The" else "${namedObj.name}'s") } (if (namedObj == null) "The" else "${namedObj.name}'s") }
@ -47,25 +47,25 @@ class UniqueValidator(val ruleset: Ruleset) {
val typeComplianceErrors = getComplianceErrors(unique) val typeComplianceErrors = getComplianceErrors(unique)
for (complianceError in typeComplianceErrors) { for (complianceError in typeComplianceErrors) {
if (complianceError.errorSeverity <= severityToReport) if (!reportRulesetSpecificErrors && complianceError.errorSeverity == UniqueType.UniqueParameterErrorSeverity.RulesetSpecific)
rulesetErrors.add(RulesetError("$prefix unique \"${unique.text}\" contains parameter ${complianceError.parameterName}," + continue
" which does not fit parameter type" +
" ${complianceError.acceptableParameterTypes.joinToString(" or ") { it.parameterName }} !", rulesetErrors.add(RulesetError("$prefix unique \"${unique.text}\" contains parameter ${complianceError.parameterName}," +
complianceError.errorSeverity.getRulesetErrorSeverity(severityToReport) " which does not fit parameter type" +
)) " ${complianceError.acceptableParameterTypes.joinToString(" or ") { it.parameterName }} !",
complianceError.errorSeverity.getRulesetErrorSeverity()
))
} }
for (conditional in unique.conditionals) { for (conditional in unique.conditionals) {
addConditionalErrors(conditional, rulesetErrors, prefix, unique, severityToReport) addConditionalErrors(conditional, rulesetErrors, prefix, unique, reportRulesetSpecificErrors)
} }
if (severityToReport != UniqueType.UniqueParameterErrorSeverity.RulesetSpecific) if (reportRulesetSpecificErrors)
// If we don't filter these messages will be listed twice as this function is called twice on most objects // If we don't filter these messages will be listed twice as this function is called twice on most objects
// The tests are RulesetInvariant in nature, but RulesetSpecific is called for _all_ objects, invariant is not. // The tests are RulesetInvariant in nature, but RulesetSpecific is called for _all_ objects, invariant is not.
return rulesetErrors addDeprecationAnnotationErrors(unique, prefix, rulesetErrors)
addDeprecationAnnotationErrors(unique, prefix, rulesetErrors)
return rulesetErrors return rulesetErrors
} }
@ -75,7 +75,7 @@ class UniqueValidator(val ruleset: Ruleset) {
rulesetErrors: RulesetErrorList, rulesetErrors: RulesetErrorList,
prefix: String, prefix: String,
unique: Unique, unique: Unique,
severityToReport: UniqueType.UniqueParameterErrorSeverity reportRulesetSpecificErrors: Boolean
) { ) {
if (conditional.type == null) { if (conditional.type == null) {
rulesetErrors.add( rulesetErrors.add(
@ -104,16 +104,19 @@ class UniqueValidator(val ruleset: Ruleset) {
val conditionalComplianceErrors = val conditionalComplianceErrors =
getComplianceErrors(conditional) getComplianceErrors(conditional)
for (complianceError in conditionalComplianceErrors) { for (complianceError in conditionalComplianceErrors) {
if (complianceError.errorSeverity == severityToReport) if (!reportRulesetSpecificErrors && complianceError.errorSeverity == UniqueType.UniqueParameterErrorSeverity.RulesetSpecific)
rulesetErrors.add( continue
RulesetError(
"$prefix unique \"${unique.text}\" contains the conditional \"${conditional.text}\"." + rulesetErrors.add(
" This contains the parameter ${complianceError.parameterName} which does not fit parameter type" + RulesetError(
" ${complianceError.acceptableParameterTypes.joinToString(" or ") { it.parameterName }} !", "$prefix unique \"${unique.text}\" contains the conditional \"${conditional.text}\"." +
complianceError.errorSeverity.getRulesetErrorSeverity(severityToReport) " This contains the parameter ${complianceError.parameterName} which does not fit parameter type" +
) " ${complianceError.acceptableParameterTypes.joinToString(" or ") { it.parameterName }} !",
complianceError.errorSeverity.getRulesetErrorSeverity()
) )
)
} }
} }

View File

@ -8,19 +8,18 @@ import com.badlogic.gdx.utils.Align
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.ruleset.unique.Unique import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.validation.RulesetError import com.unciv.models.ruleset.validation.RulesetError
import com.unciv.models.ruleset.validation.RulesetErrorSeverity import com.unciv.models.ruleset.validation.RulesetErrorSeverity
import com.unciv.models.ruleset.validation.UniqueValidator import com.unciv.models.ruleset.validation.UniqueValidator
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.components.widgets.ExpanderTab
import com.unciv.ui.components.widgets.TabbedPager
import com.unciv.ui.components.widgets.TranslatedSelectBox
import com.unciv.ui.components.extensions.surroundWithCircle import com.unciv.ui.components.extensions.surroundWithCircle
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toTextButton import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.components.input.onChange import com.unciv.ui.components.input.onChange
import com.unciv.ui.components.input.onClick import com.unciv.ui.components.input.onClick
import com.unciv.ui.components.widgets.ExpanderTab
import com.unciv.ui.components.widgets.TabbedPager
import com.unciv.ui.components.widgets.TranslatedSelectBox
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.ToastPopup import com.unciv.ui.popups.ToastPopup
import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.basescreen.BaseScreen
@ -75,7 +74,7 @@ class ModCheckTab(
runAction() runAction()
} }
fun runModChecker(base: String = MOD_CHECK_WITHOUT_BASE) { private fun runModChecker(base: String = MOD_CHECK_WITHOUT_BASE) {
modCheckFirstRun = false modCheckFirstRun = false
if (modCheckBaseSelect == null) return if (modCheckBaseSelect == null) return
@ -204,7 +203,7 @@ class ModCheckTab(
replacementUnique, replacementUnique,
false, false,
null, null,
UniqueType.UniqueParameterErrorSeverity.RulesetInvariant true
) )
for (error in modInvariantErrors) for (error in modInvariantErrors)
Log.error("ModInvariantError: %s - %s", error.text, error.errorSeverityToReport) Log.error("ModInvariantError: %s - %s", error.text, error.errorSeverityToReport)
@ -215,7 +214,7 @@ class ModCheckTab(
replacementUnique, replacementUnique,
false, false,
null, null,
UniqueType.UniqueParameterErrorSeverity.RulesetInvariant true
) )
for (error in modSpecificErrors) for (error in modSpecificErrors)
Log.error("ModSpecificError: %s - %s", error.text, error.errorSeverityToReport) Log.error("ModSpecificError: %s - %s", error.text, error.errorSeverityToReport)