Cache civ uniques while refreshing world map tiles. (#9284)

* Cache civ uniques while refreshing world map tiles.

This should have a pretty significant performance improvement of about 10% next rount time.

* Forgot to add stateForConditionals to cache key.

* Make cache keys depend on function

* Restrict LocalUniqueCache API to avoid using it the wrong way.

* Use default values instead of two methods
This commit is contained in:
WhoIsJohannes
2023-04-26 09:17:58 +02:00
committed by GitHub
parent 0b0a400f65
commit cccaa88456
18 changed files with 137 additions and 76 deletions

View File

@ -46,13 +46,11 @@ object Automation {
if (specialist) {
// If you have the Food Bonus, count as 1 extra food production (base is 2food)
for (unique in localUniqueCache.get(UniqueType.FoodConsumptionBySpecialists.name,
city.getMatchingUniques(UniqueType.FoodConsumptionBySpecialists)))
for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.FoodConsumptionBySpecialists))
if (city.matchesFilter(unique.params[1]))
yieldStats.food -= (unique.params[0].toFloat() / 100f) * 2f // base 2 food per Pop
// Specialist Happiness Percentage Change 0f-1f
for (unique in localUniqueCache.get(UniqueType.UnhappinessFromPopulationTypePercentageChange.name,
city.getMatchingUniques(UniqueType.UnhappinessFromPopulationTypePercentageChange)))
for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.UnhappinessFromPopulationTypePercentageChange))
if (city.matchesFilter(unique.params[2]) && unique.params[1] == "Specialists")
yieldStats.happiness -= (unique.params[0].toFloat() / 100f) // relative val is negative, make positive
if (city.civ.getHappiness() < 0) yieldStats.happiness *= 2 // double weight for unhappy civilization

View File

@ -182,10 +182,10 @@ class CityStats(val city: City) {
val specialist = city.getRuleset().specialists[specialistName]
?: return Stats()
val stats = specialist.cloneStats()
for (unique in localUniqueCache.get(UniqueType.StatsFromSpecialist.name, city.getMatchingUniques(UniqueType.StatsFromSpecialist)))
for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.StatsFromSpecialist))
if (city.matchesFilter(unique.params[1]))
stats.add(unique.stats)
for (unique in localUniqueCache.get(UniqueType.StatsFromObject.name, city.civ.getMatchingUniques(UniqueType.StatsFromObject)))
for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.StatsFromObject))
if (unique.params[1] == specialistName)
stats.add(unique.stats)
return stats

View File

@ -13,7 +13,10 @@ import com.unciv.ui.components.extensions.toPercent
class TileStatFunctions(val tile: Tile) {
fun getTileStats(observingCiv: Civilization?): Stats = getTileStats(tile.getCity(), observingCiv)
fun getTileStats(
observingCiv: Civilization?,
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
): Stats = getTileStats(tile.getCity(), observingCiv, localUniqueCache)
fun getTileStats(city: City?, observingCiv: Civilization?,
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
@ -24,19 +27,21 @@ class TileStatFunctions(val tile: Tile) {
val stateForConditionals = StateForConditionals(civInfo = observingCiv, city = city, tile = tile)
if (city != null) {
var tileUniques = city.getMatchingUniques(UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals)
.filter { city.matchesFilter(it.params[2]) }
tileUniques += city.getMatchingUniques(UniqueType.StatsFromObject, StateForConditionals.IgnoreConditionals)
for (unique in localUniqueCache.get("StatsFromTilesAndObjects", tileUniques)) {
var tileUniques =
localUniqueCache.forCityGetMatchingUniques(
city, UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals)
.filter { city.matchesFilter(it.params[2]) }
tileUniques += localUniqueCache.forCityGetMatchingUniques(
city, UniqueType.StatsFromObject, StateForConditionals.IgnoreConditionals)
for (unique in tileUniques) {
if (!unique.conditionalsApply(stateForConditionals)) continue
val tileType = unique.params[1]
if (!tile.matchesTerrainFilter(tileType, observingCiv)) continue
stats.add(unique.stats)
}
for (unique in localUniqueCache.get("StatsFromTilesWithout",
city.getMatchingUniques(UniqueType.StatsFromTilesWithout, StateForConditionals.IgnoreConditionals))
) {
for (unique in localUniqueCache.forCityGetMatchingUniques(
city, UniqueType.StatsFromTilesWithout, StateForConditionals.IgnoreConditionals)) {
if (
unique.conditionalsApply(stateForConditionals) &&
tile.matchesTerrainFilter(unique.params[1]) &&
@ -109,19 +114,19 @@ class TileStatFunctions(val tile: Tile) {
if (city != null) {
// Since the tile changes every time, we cache all uniques, and filter by conditional state only when iterating
val cachedStatPercentFromObjectUniques = uniqueCache.get(UniqueType.StatPercentFromObject.name,
city.getMatchingUniques(UniqueType.StatPercentFromObject, StateForConditionals.IgnoreConditionals))
val cachedStatPercentFromObjectCityUniques = uniqueCache.forCityGetMatchingUniques(
city, UniqueType.StatPercentFromObject, StateForConditionals.IgnoreConditionals)
for (unique in cachedStatPercentFromObjectUniques) {
for (unique in cachedStatPercentFromObjectCityUniques) {
if (!unique.conditionalsApply(stateForConditionals)) continue
val tileFilter = unique.params[2]
if (tile.matchesTerrainFilter(tileFilter, observingCiv))
stats[Stat.valueOf(unique.params[1])] += unique.params[0].toFloat()
}
val cachedAllStatPercentFromObjectUniques = uniqueCache.get(UniqueType.AllStatsPercentFromObject.name,
city.getMatchingUniques(UniqueType.AllStatsPercentFromObject, StateForConditionals.IgnoreConditionals))
for (unique in cachedAllStatPercentFromObjectUniques) {
val cachedAllStatPercentFromObjectCityUniques = uniqueCache.forCityGetMatchingUniques(
city, UniqueType.AllStatsPercentFromObject, StateForConditionals.IgnoreConditionals)
for (unique in cachedAllStatPercentFromObjectCityUniques) {
if (!unique.conditionalsApply(stateForConditionals)) continue
val tileFilter = unique.params[1]
if (!tile.matchesTerrainFilter(tileFilter, observingCiv)) continue
@ -131,13 +136,17 @@ class TileStatFunctions(val tile: Tile) {
}
} else if (observingCiv != null) {
for (unique in observingCiv.getMatchingUniques(UniqueType.StatPercentFromObject, stateForConditionals)) {
val cachedStatPercentFromObjectCivUniques = uniqueCache.forCivGetMatchingUniques(
observingCiv, UniqueType.StatPercentFromObject, stateForConditionals)
for (unique in cachedStatPercentFromObjectCivUniques) {
val tileFilter = unique.params[2]
if (tile.matchesTerrainFilter(tileFilter, observingCiv))
stats[Stat.valueOf(unique.params[1])] += unique.params[0].toFloat()
}
for (unique in observingCiv.getMatchingUniques(UniqueType.AllStatsPercentFromObject, stateForConditionals)) {
val cachedAllStatPercentFromObjectCivUniques = uniqueCache.forCivGetMatchingUniques(
observingCiv, UniqueType.AllStatsPercentFromObject, stateForConditionals)
for (unique in cachedAllStatPercentFromObjectCivUniques) {
val tileFilter = unique.params[1]
if (!tile.matchesTerrainFilter(tileFilter, observingCiv)) continue
val statPercentage = unique.params[0].toFloat()
@ -222,7 +231,7 @@ class TileStatFunctions(val tile: Tile) {
improvement: TileImprovement,
city: City,
conditionalState: StateForConditionals,
cityUniqueCache: LocalUniqueCache
uniqueCache: LocalUniqueCache
): Stats {
val stats = Stats()
@ -231,9 +240,8 @@ class TileStatFunctions(val tile: Tile) {
// therefore if we want the cache to be useful it needs to hold the pre-filtered uniques,
// and then for each improvement we'll filter the uniques locally.
// This is still a MASSIVE save of RAM!
val tileUniques = cityUniqueCache.get(UniqueType.StatsFromTiles.name,
city.getMatchingUniques(UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals)
.filter { city.matchesFilter(it.params[2]) }) // These are the uniques for all improvements for this city,
val tileUniques = uniqueCache.forCityGetMatchingUniques(city, UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals)
.filter { city.matchesFilter(it.params[2]) } // These are the uniques for all improvements for this city,
.filter { it.conditionalsApply(conditionalState) } // ...and this is those with applicable conditions
val improvementUniques =
improvement.getMatchingUniques(UniqueType.ImprovementStatsOnTile, conditionalState)
@ -250,9 +258,11 @@ class TileStatFunctions(val tile: Tile) {
fun statsFromObject() {
// Same as above - cache holds unfiltered uniques for the city, while we use only the filtered ones
val uniques = cityUniqueCache.get(UniqueType.StatsFromObject.name,
city.getMatchingUniques(UniqueType.StatsFromObject, StateForConditionals.IgnoreConditionals))
.filter { it.conditionalsApply(conditionalState) }
val uniques = uniqueCache.forCityGetMatchingUniques(
city,
UniqueType.StatsFromObject,
StateForConditionals.IgnoreConditionals
).filter { it.conditionalsApply(conditionalState) }
for (unique in uniques) {
if (improvement.matchesFilter(unique.params[1])) {
stats.add(unique.stats)
@ -280,9 +290,11 @@ class TileStatFunctions(val tile: Tile) {
if (city != null) {
// As above, since the conditional is tile-dependant,
// we save uniques in the cache without conditional filtering, and use only filtered ones
val allStatPercentUniques = cityUniqueCache.get(UniqueType.AllStatsPercentFromObject.name,
city.getMatchingUniques(UniqueType.AllStatsPercentFromObject, StateForConditionals.IgnoreConditionals))
.filter { it.conditionalsApply(conditionalState) }
val allStatPercentUniques = cityUniqueCache.forCityGetMatchingUniques(
city,
UniqueType.AllStatsPercentFromObject,
StateForConditionals.IgnoreConditionals
).filter { it.conditionalsApply(conditionalState) }
for (unique in allStatPercentUniques) {
if (!improvement.matchesFilter(unique.params[1])) continue
for (stat in Stat.values()) {
@ -291,9 +303,11 @@ class TileStatFunctions(val tile: Tile) {
}
// Same trick different unique - not sure if worth generalizing this 'late apply' of conditions?
val statPercentUniques = cityUniqueCache.get(UniqueType.StatPercentFromObject.name,
city.getMatchingUniques(UniqueType.StatPercentFromObject, StateForConditionals.IgnoreConditionals))
.filter { it.conditionalsApply(conditionalState) }
val statPercentUniques = cityUniqueCache.forCityGetMatchingUniques(
city,
UniqueType.StatPercentFromObject,
StateForConditionals.IgnoreConditionals
).filter { it.conditionalsApply(conditionalState) }
for (unique in statPercentUniques) {
if (!improvement.matchesFilter(unique.params[2])) continue