Optimization to bulk team changes (reduces campaign core destruction lag)

This commit is contained in:
Anuken
2026-04-24 14:25:22 -04:00
parent ea730cd91e
commit 327464ba82
4 changed files with 92 additions and 17 deletions
+44 -9
View File
@@ -176,29 +176,58 @@ public class BlockIndexer{
}
}
//no longer part of the building list
data.buildings.remove(build);
data.buildingTypes.get(build.block, () -> new Seq<>(false)).remove(build);
{
int buildIndex = build.indexerBuildIndex & 0xffff;
//remove from building list based on cached index
if(buildIndex < data.buildings.size && data.buildings.get(buildIndex) == build){
data.buildings.remove(buildIndex);
//update index of the building placed in the new position
if(buildIndex < data.buildings.size) data.buildings.get(buildIndex).indexerBuildIndex = (short)buildIndex;
}else{
int index = data.buildings.indexOf(build);
if(index != -1){
data.buildings.remove(index);
//update index of the building placed in the new position
if(index < data.buildings.size) data.buildings.get(index).indexerBuildIndex = (short)index;
}
}
}
{
var targetTypes = data.buildingTypes.get(build.block, () -> new Seq<>(false));
int buildTypeIndex = build.indexerBuildTypeIndex & 0xffff;
//remove from building list based on cached index
if(buildTypeIndex < targetTypes.size && targetTypes.get(buildTypeIndex) == build){
targetTypes.remove(buildTypeIndex);
//update index of the building placed in the new position
if(buildTypeIndex < targetTypes.size) targetTypes.get(buildTypeIndex).indexerBuildTypeIndex = (short)buildTypeIndex;
}else{
int index = targetTypes.indexOf(build);
if(index != -1){
targetTypes.remove(index);
//update index of the building placed in the new position
if(index < targetTypes.size) targetTypes.get(index).indexerBuildTypeIndex = (short)index;
}
}
}
//update the unit cap when building is removed
data.unitCap -= tile.block().unitCapModifier;
//unregister building from building quadtree
if(data.buildingTree != null){
data.buildingTree.remove(build);
}
//remove indexed turret
if(data.turretTree != null && build.block.attacks){
data.turretTree.remove(build);
}
//unregister damaged buildings
//unregister damaged buildings if applicable
if(build.wasDamaged && damagedTiles[team.id] != null){
damagedTiles[team.id].remove(build);
}
//is no longer registered
build.wasDamaged = false;
}
}
@@ -563,9 +592,15 @@ public class BlockIndexer{
}
}
var targetTypes = data.buildingTypes.get(tile.block(), () -> new Seq<>(false));
//record in list of buildings
data.buildings.add(tile.build);
data.buildingTypes.get(tile.block(), () -> new Seq<>(false)).add(tile.build);
targetTypes.add(tile.build);
//save indices for fast lookup
tile.build.indexerBuildIndex = (short)(data.buildings.size - 1);
tile.build.indexerBuildTypeIndex = (short)(targetTypes.size - 1);
//update the unit cap when new tile is registered
data.unitCap += tile.block().unitCapModifier;
@@ -66,9 +66,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
transient Seq<Building> proximity = new Seq<>(true, 6, Building.class);
transient int cdump;
transient int rotation;
transient float payloadRotation;
transient String lastAccessed;
transient boolean wasDamaged; //used only by the indexer
transient float visualLiquid;
/** TODO Each bit corresponds to a team ID. Only 64 are supported. Does not work on servers. */
@@ -103,6 +101,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
private transient float sleepTime;
private transient boolean initialized;
//used only by the indexer
transient boolean wasDamaged;
transient short indexerBuildIndex, indexerBuildTypeIndex;
/** Sets this tile entity data to this and adds it if necessary. */
public Building init(Tile tile, Team team, boolean shouldAdd, int rotation){
if(!initialized){
@@ -1755,6 +1757,11 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
/** Changes this building's team in a safe manner. */
public void changeTeam(Team next){
changeTeam(next, true);
}
/** Changes this building's team in a safe manner. */
public void changeTeam(Team next, boolean updatePower){
if(this.team == next) return;
if(block.forceTeam != null) team = block.forceTeam;
@@ -1768,11 +1775,13 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
this.team = next;
if(power != null){
if(power != null && updatePower){
var oldGraph = power.graph;
for(int i = 0; i < power.links.size; i++){
var other = world.build(power.links.items[i]);
if(other != null && other.team != team && other.power != null){
//only reflow links that were connected to the old power graph; ones that have a new one were already covered.
if(other != null && other.team != team && other.power != null && other.power.graph == oldGraph){
power.links.removeIndex(i);
other.power.links.removeValue(pos());
+35 -3
View File
@@ -18,6 +18,7 @@ import mindustry.gen.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.blocks.environment.*;
import mindustry.world.blocks.power.*;
import static mindustry.Vars.*;
@@ -26,7 +27,9 @@ public class Tile implements Position, QuadTreeObject, Displayable{
private static final TilePreChangeEvent preChange = new TilePreChangeEvent();
private static final TileFloorChangeEvent floorChange = new TileFloorChangeEvent();
private static final TileOverlayChangeEvent overlayChange = new TileOverlayChangeEvent();;
private static final ObjectSet<Building> tileSet = new ObjectSet<>();
private static final IntSet staleGraphs = new IntSet();
/**
* Extra data for specific blocks. Only saved if Block#saveData is true.
@@ -801,10 +804,39 @@ public class Tile implements Position, QuadTreeObject, Displayable{
@Remote(called = Loc.server)
public static void setTeams(int[] positions, Team team){
if(positions == null) return;
staleGraphs.clear();
for(int pos : positions){
Tile tile = world.tile(pos);
if(tile != null && tile.build != null){
tile.build.changeTeam(team);
var build = world.build(pos);
if(build != null){
if(build.power != null){
staleGraphs.add(build.power.graph.getID());
}
build.changeTeam(team, false);
}
}
//update power graphs in a second pass
for(int pos : positions){
var build = world.build(pos);
if(build != null && build.power != null && staleGraphs.contains(build.power.graph.getID())){
for(int i = 0; i < build.power.links.size; i++){
var other = world.build(build.power.links.items[i]);
//only reflow links that were connected to the old power graph; ones that have a new one were already covered.
if(other != null && other.team != team && other.power != null && staleGraphs.contains(other.power.graph.getID())){
build.power.links.removeIndex(i);
other.power.links.removeValue(build.pos());
new PowerGraph().reflow(other);
i --;
}
}
new PowerGraph().reflow(build);
build.updatePowerGraph();
}
}
}
@@ -103,7 +103,6 @@ public class BuildPayload implements Payload{
@Override
public void set(float x, float y, float rotation){
build.set(x, y);
build.payloadRotation = rotation;
}
@Override