From 327464ba82fca614671d86b1357c99f182783493 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 24 Apr 2026 14:25:22 -0400 Subject: [PATCH] Optimization to bulk team changes (reduces campaign core destruction lag) --- core/src/mindustry/ai/BlockIndexer.java | 53 +++++++++++++++---- .../mindustry/entities/comp/BuildingComp.java | 17 ++++-- core/src/mindustry/world/Tile.java | 38 +++++++++++-- .../world/blocks/payloads/BuildPayload.java | 1 - 4 files changed, 92 insertions(+), 17 deletions(-) diff --git a/core/src/mindustry/ai/BlockIndexer.java b/core/src/mindustry/ai/BlockIndexer.java index f5ff55e1e6..10fb1a6fd9 100644 --- a/core/src/mindustry/ai/BlockIndexer.java +++ b/core/src/mindustry/ai/BlockIndexer.java @@ -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; diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index b74ac37548..50ef3ce0b6 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -66,9 +66,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, transient Seq 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()); diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index e20f96290d..9292c93189 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -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 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(); } } } diff --git a/core/src/mindustry/world/blocks/payloads/BuildPayload.java b/core/src/mindustry/world/blocks/payloads/BuildPayload.java index 0b8b4a287e..9cf0c9e830 100644 --- a/core/src/mindustry/world/blocks/payloads/BuildPayload.java +++ b/core/src/mindustry/world/blocks/payloads/BuildPayload.java @@ -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