mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-26 13:27:22 -04:00
Defense against circular references in Promotions (#9694)
* Promotion picker - working defense against circular references * Mod checker - complain about circular references
This commit is contained in:
parent
6eeb630b6c
commit
1694a59fd2
@ -10,6 +10,7 @@ import com.unciv.models.ruleset.unique.IHasUniques
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.Promotion
|
||||
import com.unciv.models.stats.INamed
|
||||
import com.unciv.models.stats.Stats
|
||||
|
||||
@ -387,6 +388,7 @@ class RulesetValidator(val ruleset: Ruleset) {
|
||||
if (!ruleset.unitTypes.containsKey(unitType) && (ruleset.unitTypes.isNotEmpty() || !vanillaRuleset.unitTypes.containsKey(unitType)))
|
||||
lines.add("${promotion.name} references unit type $unitType, which does not exist!", RulesetErrorSeverity.Warning)
|
||||
checkUniques(promotion, lines, rulesetSpecific, tryFixUnknownUniques)
|
||||
checkPromotionCircularReferences(lines)
|
||||
}
|
||||
|
||||
for (unitType in ruleset.unitTypes.values) {
|
||||
@ -426,6 +428,24 @@ class RulesetValidator(val ruleset: Ruleset) {
|
||||
return lines
|
||||
}
|
||||
|
||||
private fun checkPromotionCircularReferences(lines: RulesetErrorList) {
|
||||
fun recursiveCheck(history: LinkedHashSet<Promotion>, promotion: Promotion, level: Int) {
|
||||
if (promotion in history) {
|
||||
lines.add("Circular Reference in Promotions: ${history.joinToString("→") { it.name }}→${promotion.name}", RulesetErrorSeverity.Warning)
|
||||
return
|
||||
}
|
||||
if (level > 99) return
|
||||
history.add(promotion)
|
||||
for (prerequisiteName in promotion.prerequisites) {
|
||||
val prerequisite = ruleset.unitPromotions[prerequisiteName] ?: continue
|
||||
recursiveCheck(history.toCollection(linkedSetOf()), prerequisite, level + 1)
|
||||
}
|
||||
}
|
||||
for (promotion in ruleset.unitPromotions.values) {
|
||||
recursiveCheck(linkedSetOf(), promotion, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun checkUniques(
|
||||
uniqueContainer: IHasUniques,
|
||||
|
@ -6,7 +6,6 @@ import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.Promotion
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.utils.Log
|
||||
|
||||
internal class PromotionTree(val unit: MapUnit) {
|
||||
/** Ordered set of Promotions to show - by Json column/row and translated name */
|
||||
@ -92,10 +91,7 @@ internal class PromotionTree(val unit: MapUnit) {
|
||||
for (node in nodes.values) {
|
||||
for (prerequisite in node.promotion.prerequisites) {
|
||||
val parent = nodes[prerequisite] ?: continue
|
||||
if (node in allChildren(parent)) {
|
||||
Log.debug("Ignoring circular reference: %s requires %s", node, parent)
|
||||
continue
|
||||
}
|
||||
if (detectLoop(node, parent)) continue
|
||||
node.parents += parent
|
||||
parent.children += node
|
||||
if (node.level > 0 && node.baseName == parent.baseName)
|
||||
@ -153,8 +149,21 @@ internal class PromotionTree(val unit: MapUnit) {
|
||||
|
||||
fun allNodes() = nodes.values.asSequence()
|
||||
fun allRoots() = allNodes().filter { it.isRoot }
|
||||
private fun allChildren(node: PromotionNode): Sequence<PromotionNode> {
|
||||
return sequenceOf(node) + node.children.flatMap { allChildren(it) }
|
||||
|
||||
private fun detectLoop(node: PromotionNode, parent: PromotionNode): Boolean {
|
||||
if (parent == node) return true
|
||||
val loopCheck = HashSet<PromotionNode>(nodes.size)
|
||||
loopCheck.add(node)
|
||||
fun detectRecursive(parent: PromotionNode, level: Int): Boolean {
|
||||
if (level > 99) return true
|
||||
if (parent in loopCheck) return true
|
||||
loopCheck.add(parent)
|
||||
for (child in parent.children) {
|
||||
if (detectRecursive(child, level + 1)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return detectRecursive(parent, 0)
|
||||
}
|
||||
|
||||
private fun getReachableNode(promotion: Promotion): PromotionNode? =
|
||||
|
Loading…
x
Reference in New Issue
Block a user