Fixed memory- and time-intensive unit maintenance checks (#6092)

Get all civwide unit discount uniques once, and then filter them by-conditional only when checking each specific unit.
This saves a LOT of memory allocation and time.
A result of the investigation at #5919.
This commit is contained in:
Yair Morgenstern 2022-02-01 12:14:46 +02:00 committed by GitHub
parent 0a336b3d99
commit 3908fbc9cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 24 additions and 9 deletions

View File

@ -31,18 +31,25 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
unitsToPayFor = unitsToPayFor.filterNot {
it.getTile().isCityCenter() && it.canGarrison()
}
// Handle unit maintenance discounts
// Free Garrison already removed above from sequence
// To try and avoid concurrent modifications leading to crashes,
// we calculate the costs of one unit at a time.
// Each unit starts with 1f aka 100% of cost, and then the discout is addded.
// Each unit starts with 1f aka 100% of cost, and then the discount is added.
// Note all discounts are in the form of -X%, such as -25 for 25% reduction
val costsToPay = ArrayList<Float>()
// We IGNORE the conditionals when we get them civ-wide, so we won't need to do the same thing for EVERY unit in the civ.
// This leads to massive memory and CPU time savings when calculating the maintenance!
val civwideDiscountUniques = civInfo.getMatchingUniques(UniqueType.UnitMaintenanceDiscount, StateForConditionals.IgnoreConditionals).toList()
for (unit in unitsToPayFor) {
val stateForConditionals = StateForConditionals(civInfo=civInfo, unit=unit)
val stateForConditionals = StateForConditionals(civInfo = civInfo, unit = unit)
var unitMaintenance = 1f
for (unique in unit.getMatchingUniques(UniqueType.UnitMaintenanceDiscount, stateForConditionals, true)){
val uniquesThatApply = unit.getMatchingUniques(
UniqueType.UnitMaintenanceDiscount,
stateForConditionals
) + civwideDiscountUniques.asSequence()
.filter { it.conditionalsApply(stateForConditionals) }
for (unique in uniquesThatApply) {
unitMaintenance *= unique.params[0].toPercent()
}
costsToPay.add(unitMaintenance)

View File

@ -19,4 +19,11 @@ data class StateForConditionals(
val combatAction: CombatAction? = null,
val region: Region? = null,
)
val ignoreConditionals:Boolean = false,
) {
companion object {
val IgnoreConditionals = StateForConditionals(ignoreConditionals = true)
}
}

View File

@ -38,7 +38,8 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
}
fun conditionalsApply(state: StateForConditionals?): Boolean {
if (state == null) return conditionals.isEmpty()
if (state == null) return conditionals.isEmpty()
if (state.ignoreConditionals) return true
for (condition in conditionals) {
if (!conditionalApplies(condition, state)) return false
}