mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-03 21:40:31 +07:00
performance: Unique caching revamp!
- Cache civ uniques ignoring conditionals, for better reuse - Cache civ uniques *when querying city uniques*, same This allows us to use the same UniqueCache between cities, and we still get the performance boost of "search once filter always", since the searching is the heavier part, and in any case we'll always have to do the filtering by conditionals either way
This commit is contained in:
parent
1e027199a6
commit
97b16d2b5f
@ -33,6 +33,7 @@ import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.Speed
|
||||
import com.unciv.models.ruleset.nation.Difficulty
|
||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.audio.MusicMood
|
||||
@ -669,10 +670,11 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
||||
civInfo.cache.updateCitiesConnectedToCapital(true)
|
||||
|
||||
// We need to determine the GLOBAL happiness state in order to determine the city stats
|
||||
val localUniqueCache = LocalUniqueCache()
|
||||
for (city in civInfo.cities) {
|
||||
city.cityStats.updateTileStats() // Some nat wonders can give happiness!
|
||||
city.cityStats.updateTileStats(localUniqueCache) // Some nat wonders can give happiness!
|
||||
city.cityStats.updateCityHappiness(
|
||||
city.cityConstructions.getStats()
|
||||
city.cityConstructions.getStats(localUniqueCache)
|
||||
)
|
||||
}
|
||||
|
||||
@ -689,7 +691,8 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
||||
if (!ruleset.tileResources.containsKey(city.demandedResource))
|
||||
city.demandedResource = ""
|
||||
|
||||
city.cityStats.update()
|
||||
// No uniques have changed since the cache was created, so we can still use it
|
||||
city.cityStats.update(localUniqueCache=localUniqueCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -345,8 +345,8 @@ object Automation {
|
||||
return when {
|
||||
powerLevelComparison > 2 -> ThreatLevel.VeryHigh
|
||||
powerLevelComparison > 1.5f -> ThreatLevel.High
|
||||
powerLevelComparison < (1 / 1.5f) -> ThreatLevel.Low
|
||||
powerLevelComparison < 0.5f -> ThreatLevel.VeryLow
|
||||
powerLevelComparison < (1 / 1.5f) -> ThreatLevel.Low
|
||||
else -> ThreatLevel.Medium
|
||||
}
|
||||
}
|
||||
|
@ -622,7 +622,7 @@ class City : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
|
||||
// Uniques special to this city
|
||||
private fun getLocalMatchingUniques(uniqueType: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(civ, this)): Sequence<Unique> {
|
||||
fun getLocalMatchingUniques(uniqueType: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(civ, this)): Sequence<Unique> {
|
||||
return (
|
||||
cityConstructions.builtBuildingUniqueMap.getUniques(uniqueType).filter { it.isLocalEffect }
|
||||
+ religion.getUniques().filter { it.isOfType(uniqueType) }
|
||||
|
@ -110,9 +110,8 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
||||
/**
|
||||
* @return [Stats] provided by all built buildings in city plus the bonus from Library
|
||||
*/
|
||||
fun getStats(): StatTreeNode {
|
||||
fun getStats(localUniqueCache: LocalUniqueCache): StatTreeNode {
|
||||
val stats = StatTreeNode()
|
||||
val localUniqueCache = LocalUniqueCache()
|
||||
for (building in getBuiltBuildings())
|
||||
stats.addStats(building.getStats(city, localUniqueCache), building.name)
|
||||
return stats
|
||||
|
@ -354,9 +354,8 @@ class CityStats(val city: City) {
|
||||
//endregion
|
||||
//region State-Changing Methods
|
||||
|
||||
fun updateTileStats() {
|
||||
fun updateTileStats(localUniqueCache:LocalUniqueCache = LocalUniqueCache()) {
|
||||
val stats = Stats()
|
||||
val localUniqueCache = LocalUniqueCache()
|
||||
val workedTiles = city.tilesInRange.asSequence()
|
||||
.filter {
|
||||
city.location == it.position
|
||||
@ -490,12 +489,14 @@ class CityStats(val city: City) {
|
||||
|
||||
fun update(currentConstruction: IConstruction = city.cityConstructions.getCurrentConstruction(),
|
||||
updateTileStats:Boolean = true,
|
||||
updateCivStats:Boolean = true) {
|
||||
if (updateTileStats) updateTileStats()
|
||||
updateCivStats:Boolean = true,
|
||||
localUniqueCache:LocalUniqueCache = LocalUniqueCache()) {
|
||||
|
||||
if (updateTileStats) updateTileStats(localUniqueCache)
|
||||
|
||||
// We need to compute Tile yields before happiness
|
||||
|
||||
val statsFromBuildings = city.cityConstructions.getStats() // this is performance heavy, so calculate once
|
||||
val statsFromBuildings = city.cityConstructions.getStats(localUniqueCache) // this is performance heavy, so calculate once
|
||||
updateBaseStatList(statsFromBuildings)
|
||||
updateCityHappiness(statsFromBuildings)
|
||||
updateStatPercentBonusList(currentConstruction)
|
||||
|
@ -325,11 +325,18 @@ class LocalUniqueCache(val cache:Boolean = true) {
|
||||
uniqueType: UniqueType,
|
||||
ignoreConditionals: Boolean = false
|
||||
): Sequence<Unique> {
|
||||
val stateForConditionals = if (ignoreConditionals) StateForConditionals.IgnoreConditionals
|
||||
else StateForConditionals(city.civ, city)
|
||||
// City uniques are a combination of *global civ* uniques plus *city relevant* uniques (see City.getMatchingUniques())
|
||||
// We can cache the civ uniques separately, so if we have several cities using the same cache,
|
||||
// we can cache the list of *civ uniques* to reuse between cities.
|
||||
// This is assuming that we're ignoring conditionals, because otherwise -
|
||||
// the conditionals will render the the *filtered uniques* different anyway, so there's no reason to cache...
|
||||
val uniques = if (!ignoreConditionals) city.getMatchingUniques(uniqueType, StateForConditionals(city.civ, city))
|
||||
else forCivGetMatchingUniques(city.civ, uniqueType, StateForConditionals.IgnoreConditionals) +
|
||||
city.getLocalMatchingUniques(uniqueType, StateForConditionals.IgnoreConditionals)
|
||||
|
||||
return get(
|
||||
"city-${city.id}-${uniqueType.name}-${ignoreConditionals}",
|
||||
city.getMatchingUniques(uniqueType, stateForConditionals)
|
||||
uniques
|
||||
)
|
||||
}
|
||||
|
||||
@ -340,12 +347,16 @@ class LocalUniqueCache(val cache:Boolean = true) {
|
||||
civ
|
||||
)
|
||||
): Sequence<Unique> {
|
||||
val sequence = civ.getMatchingUniques(uniqueType, stateForConditionals)
|
||||
if (!cache) return sequence // So we don't need to toString the stateForConditionals
|
||||
val sequence = civ.getMatchingUniques(uniqueType, StateForConditionals.IgnoreConditionals)
|
||||
// The uniques CACHED are ALL civ uniques, regardless of conditional matching.
|
||||
// The uniques RETURNED are uniques AFTER conditional matching.
|
||||
// This allows reuse of the cached values, between runs with different conditionals -
|
||||
// for example, iterate on all tiles and get StatPercentForObject uniques relevant for each tile,
|
||||
// each tile will have different conditional state, but they will all reuse the same list of uniques for the civ
|
||||
return get(
|
||||
"civ-${civ.civName}-${uniqueType.name}-${stateForConditionals}",
|
||||
"civ-${civ.civName}-${uniqueType.name}",
|
||||
sequence
|
||||
)
|
||||
).filter { it.conditionalsApply(stateForConditionals) }
|
||||
}
|
||||
|
||||
/** Get cached results as a sequence */
|
||||
|
Loading…
Reference in New Issue
Block a user