mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 17:59:11 +07:00
Performance: Save civ tiles and neighbors in cache to update visible tiles faster
This commit is contained in:
@ -551,8 +551,10 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
|||||||
) {
|
) {
|
||||||
for (unit in civInfo.units.getCivUnits())
|
for (unit in civInfo.units.getCivUnits())
|
||||||
unit.updateVisibleTiles(false) // this needs to be done after all the units are assigned to their civs and all other transients are set
|
unit.updateVisibleTiles(false) // this needs to be done after all the units are assigned to their civs and all other transients are set
|
||||||
if(civInfo.playerType == PlayerType.Human)
|
if (civInfo.playerType == PlayerType.Human)
|
||||||
civInfo.exploredRegion.setMapParameters(tileMap.mapParameters) // Required for the correct calculation of the explored region on world wrap maps
|
civInfo.exploredRegion.setMapParameters(tileMap.mapParameters) // Required for the correct calculation of the explored region on world wrap maps
|
||||||
|
|
||||||
|
civInfo.cache.updateOurTiles()
|
||||||
civInfo.cache.updateSightAndResources() // only run ONCE and not for each unit - this is a huge performance saver!
|
civInfo.cache.updateSightAndResources() // only run ONCE and not for each unit - this is a huge performance saver!
|
||||||
|
|
||||||
// Since this depends on the cities of ALL civilizations,
|
// Since this depends on the cities of ALL civilizations,
|
||||||
|
@ -17,6 +17,7 @@ import com.unciv.logic.map.tile.RoadStatus
|
|||||||
import com.unciv.logic.map.tile.Tile
|
import com.unciv.logic.map.tile.Tile
|
||||||
import com.unciv.models.ruleset.tile.Terrain
|
import com.unciv.models.ruleset.tile.Terrain
|
||||||
import com.unciv.models.ruleset.tile.TileImprovement
|
import com.unciv.models.ruleset.tile.TileImprovement
|
||||||
|
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
|
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
|
||||||
import com.unciv.utils.Log
|
import com.unciv.utils.Log
|
||||||
@ -370,9 +371,14 @@ class WorkerAutomation(
|
|||||||
}
|
}
|
||||||
if (potentialTileImprovements.isEmpty()) return null
|
if (potentialTileImprovements.isEmpty()) return null
|
||||||
|
|
||||||
|
val cityUniqueCaches = HashMap<City, LocalUniqueCache>()
|
||||||
fun getRankingWithImprovement(improvementName: String): Float {
|
fun getRankingWithImprovement(improvementName: String): Float {
|
||||||
val improvement = ruleSet.tileImprovements[improvementName]!!
|
val improvement = ruleSet.tileImprovements[improvementName]!!
|
||||||
val stats = tile.stats.getImprovementStats(improvement, civInfo, tile.getCity())
|
val city = tile.getCity()
|
||||||
|
val cache =
|
||||||
|
if (city == null) LocalUniqueCache(false)
|
||||||
|
else cityUniqueCaches.getOrPut(city) { LocalUniqueCache() }
|
||||||
|
val stats = tile.stats.getImprovementStats(improvement, civInfo, tile.getCity(), cache)
|
||||||
return Automation.rankStatsValue(stats, unit.civ)
|
return Automation.rankStatsValue(stats, unit.civ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ class CityExpansionManager : IsPartOfGameInfoSerialization {
|
|||||||
|
|
||||||
tile.setOwningCity(null)
|
tile.setOwningCity(null)
|
||||||
|
|
||||||
city.civ.cache.updateCivResources()
|
city.civ.cache.updateOurTiles()
|
||||||
city.cityStats.update()
|
city.cityStats.update()
|
||||||
|
|
||||||
tile.history.recordRelinquishOwnership(tile)
|
tile.history.recordRelinquishOwnership(tile)
|
||||||
@ -169,14 +169,13 @@ class CityExpansionManager : IsPartOfGameInfoSerialization {
|
|||||||
city.tiles = city.tiles.withItem(tile.position)
|
city.tiles = city.tiles.withItem(tile.position)
|
||||||
tile.setOwningCity(city)
|
tile.setOwningCity(city)
|
||||||
city.population.autoAssignPopulation()
|
city.population.autoAssignPopulation()
|
||||||
city.civ.cache.updateCivResources()
|
city.civ.cache.updateOurTiles()
|
||||||
city.cityStats.update()
|
city.cityStats.update()
|
||||||
|
|
||||||
for (unit in tile.getUnits().toList()) // toListed because we're modifying
|
for (unit in tile.getUnits().toList()) // toListed because we're modifying
|
||||||
if (!unit.civ.diplomacyFunctions.canPassThroughTiles(city.civ))
|
if (!unit.civ.diplomacyFunctions.canPassThroughTiles(city.civ))
|
||||||
unit.movement.teleportToClosestMoveableTile()
|
unit.movement.teleportToClosestMoveableTile()
|
||||||
|
|
||||||
city.civ.cache.updateViewableTiles()
|
|
||||||
|
|
||||||
tile.history.recordTakeOwnership(tile)
|
tile.history.recordTakeOwnership(tile)
|
||||||
}
|
}
|
||||||
@ -187,7 +186,8 @@ class CityExpansionManager : IsPartOfGameInfoSerialization {
|
|||||||
val location = addNewTileWithCulture()
|
val location = addNewTileWithCulture()
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
val locations = LocationAction(location, city.location)
|
val locations = LocationAction(location, city.location)
|
||||||
city.civ.addNotification("[" + city.name + "] has expanded its borders!", locations, NotificationCategory.Cities, NotificationIcon.Culture)
|
city.civ.addNotification("[${city.name}] has expanded its borders!", locations,
|
||||||
|
NotificationCategory.Cities, NotificationIcon.Culture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,6 @@ class CityInfoConquestFunctions(val city: City){
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
conqueringCiv.cache.updateViewableTiles() // Might see new tiles from this city
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -266,18 +265,16 @@ class CityInfoConquestFunctions(val city: City){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun moveToCiv(newCivInfo: Civilization) {
|
fun moveToCiv(newCiv: Civilization) {
|
||||||
|
val oldCiv = city.civ
|
||||||
city.apply {
|
city.apply {
|
||||||
val oldCiv = civ
|
|
||||||
|
|
||||||
|
|
||||||
// Remove/relocate palace for old Civ - need to do this BEFORE we move the cities between
|
// Remove/relocate palace for old Civ - need to do this BEFORE we move the cities between
|
||||||
// civs so the capitalCityIndicator recognizes the unique buildings of the conquered civ
|
// civs so the capitalCityIndicator recognizes the unique buildings of the conquered civ
|
||||||
if (oldCiv.getCapital() == this) oldCiv.moveCapitalToNextLargest()
|
if (oldCiv.getCapital() == this) oldCiv.moveCapitalToNextLargest()
|
||||||
|
|
||||||
oldCiv.cities = oldCiv.cities.toMutableList().apply { remove(city) }
|
oldCiv.cities = oldCiv.cities.toMutableList().apply { remove(city) }
|
||||||
newCivInfo.cities = newCivInfo.cities.toMutableList().apply { add(city) }
|
newCiv.cities = newCiv.cities.toMutableList().apply { add(city) }
|
||||||
civ = newCivInfo
|
civ = newCiv
|
||||||
hasJustBeenConquered = false
|
hasJustBeenConquered = false
|
||||||
turnAcquired = civ.gameInfo.turns
|
turnAcquired = civ.gameInfo.turns
|
||||||
previousOwner = oldCiv.civName
|
previousOwner = oldCiv.civName
|
||||||
@ -297,8 +294,8 @@ class CityInfoConquestFunctions(val city: City){
|
|||||||
// Place palace for newCiv if this is the only city they have
|
// Place palace for newCiv if this is the only city they have
|
||||||
// This needs to happen _before_ free buildings are added, as sometimes these should
|
// This needs to happen _before_ free buildings are added, as sometimes these should
|
||||||
// only be placed in the capital, and then there needs to be a capital.
|
// only be placed in the capital, and then there needs to be a capital.
|
||||||
if (newCivInfo.cities.size == 1) {
|
if (newCiv.cities.size == 1) {
|
||||||
newCivInfo.moveCapitalTo(this)
|
newCiv.moveCapitalTo(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add our free buildings to this city and add free buildings provided by the city to other cities
|
// Add our free buildings to this city and add free buildings provided by the city to other cities
|
||||||
@ -308,7 +305,7 @@ class CityInfoConquestFunctions(val city: City){
|
|||||||
|
|
||||||
// Transfer unique buildings
|
// Transfer unique buildings
|
||||||
for (building in cityConstructions.getBuiltBuildings()) {
|
for (building in cityConstructions.getBuiltBuildings()) {
|
||||||
val civEquivalentBuilding = newCivInfo.getEquivalentBuilding(building.name)
|
val civEquivalentBuilding = newCiv.getEquivalentBuilding(building.name)
|
||||||
if (building != civEquivalentBuilding) {
|
if (building != civEquivalentBuilding) {
|
||||||
cityConstructions.removeBuilding(building.name)
|
cityConstructions.removeBuilding(building.name)
|
||||||
cityConstructions.addBuilding(civEquivalentBuilding.name)
|
cityConstructions.addBuilding(civEquivalentBuilding.name)
|
||||||
@ -329,6 +326,8 @@ class CityInfoConquestFunctions(val city: City){
|
|||||||
tile.history.recordTakeOwnership(tile)
|
tile.history.recordTakeOwnership(tile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
newCiv.cache.updateOurTiles()
|
||||||
|
oldCiv.cache.updateOurTiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -138,8 +138,24 @@ class CivInfoTransientCache(val civInfo: Civilization) {
|
|||||||
civInfo.viewableInvisibleUnitsTiles = newViewableInvisibleTiles
|
civInfo.viewableInvisibleUnitsTiles = newViewableInvisibleTiles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var ourTilesAndNeighboringTiles: Set<Tile> = HashSet()
|
||||||
|
|
||||||
|
/** Our tiles update pretty infrequently - most 'viewable tile' changes are due to unit movements,
|
||||||
|
* which means we can store this separately and use it 'as is' so we don't need to find the neighboring tiles every time
|
||||||
|
* a unit moves */
|
||||||
|
fun updateOurTiles(){
|
||||||
|
val newOurTilesAndNeighboring = HashSet<Tile>()
|
||||||
|
val ownedTiles = civInfo.cities.asSequence().flatMap { it.getTiles() }
|
||||||
|
newOurTilesAndNeighboring.addAll(ownedTiles)
|
||||||
|
val neighboringUnownedTiles = ownedTiles.flatMap { tile -> tile.neighbors.filter { it.getOwner() != civInfo } }
|
||||||
|
newOurTilesAndNeighboring.addAll(neighboringUnownedTiles)
|
||||||
|
ourTilesAndNeighboringTiles = newOurTilesAndNeighboring
|
||||||
|
|
||||||
|
updateViewableTiles()
|
||||||
|
updateCivResources()
|
||||||
|
}
|
||||||
|
|
||||||
private fun setNewViewableTiles() {
|
private fun setNewViewableTiles() {
|
||||||
val newViewableTiles = HashSet<Tile>()
|
|
||||||
|
|
||||||
// while spectating all map is visible
|
// while spectating all map is visible
|
||||||
if (civInfo.isSpectator() || DebugUtils.VISIBLE_MAP) {
|
if (civInfo.isSpectator() || DebugUtils.VISIBLE_MAP) {
|
||||||
@ -149,13 +165,7 @@ class CivInfoTransientCache(val civInfo: Civilization) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are a LOT of tiles usually.
|
val newViewableTiles = HashSet<Tile>(ourTilesAndNeighboringTiles)
|
||||||
// And making large lists of them just as intermediaries before we shove them into the hashset is very space-inefficient.
|
|
||||||
// And so, sequences to the rescue!
|
|
||||||
val ownedTiles = civInfo.cities.asSequence().flatMap { it.getTiles() }
|
|
||||||
newViewableTiles.addAll(ownedTiles)
|
|
||||||
val neighboringUnownedTiles = ownedTiles.flatMap { tile -> tile.neighbors.filter { it.getOwner() != civInfo } }
|
|
||||||
newViewableTiles.addAll(neighboringUnownedTiles)
|
|
||||||
newViewableTiles.addAll(civInfo.units.getCivUnits().flatMap { unit -> unit.viewableTiles.asSequence().filter { it.getOwner() != civInfo } })
|
newViewableTiles.addAll(civInfo.units.getCivUnits().flatMap { unit -> unit.viewableTiles.asSequence().filter { it.getOwner() != civInfo } })
|
||||||
|
|
||||||
for (otherCiv in civInfo.getKnownCivs()) {
|
for (otherCiv in civInfo.getKnownCivs()) {
|
||||||
|
@ -105,8 +105,8 @@ class TradeLogic(val ourCivilization:Civilization, val otherCivilization: Civili
|
|||||||
unit.movement.teleportToClosestMoveableTile()
|
unit.movement.teleportToClosestMoveableTile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
to.cache.updateViewableTiles()
|
to.cache.updateOurTiles()
|
||||||
from.cache.updateViewableTiles()
|
from.cache.updateOurTiles()
|
||||||
|
|
||||||
// suggest an option to liberate the city
|
// suggest an option to liberate the city
|
||||||
if (to.isHuman()
|
if (to.isHuman()
|
||||||
|
Reference in New Issue
Block a user