diff --git a/core/src/mindustry/ai/Pathfinder.java b/core/src/mindustry/ai/Pathfinder.java index c4ca3a2f90..83a145e243 100644 --- a/core/src/mindustry/ai/Pathfinder.java +++ b/core/src/mindustry/ai/Pathfinder.java @@ -19,7 +19,7 @@ import mindustry.world.meta.*; import static mindustry.Vars.*; public class Pathfinder implements Runnable{ - private static final long maxUpdate = Time.millisToNanos(7); + private static final long maxUpdate = Time.millisToNanos(8); private static final int updateFPS = 60; private static final int updateInterval = 1000 / updateFPS; @@ -219,8 +219,6 @@ public class Pathfinder implements Runnable{ public void updateTile(Tile tile){ if(net.client()) return; - int x = tile.x, y = tile.y; - tile.getLinkedTiles(t -> { int pos = t.array(); if(pos < tiles.length){ @@ -238,9 +236,10 @@ public class Pathfinder implements Runnable{ } } + //mark every flow field as dirty, so it updates when it's done queue.post(() -> { for(Flowfield data : threadList){ - updateTargets(data, x, y); + data.dirty = true; } }); } @@ -257,6 +256,13 @@ public class Pathfinder implements Runnable{ //each update time (not total!) no longer than maxUpdate for(Flowfield data : threadList){ + + //if it's dirty and there is nothing to update, begin updating once more + if(data.dirty && data.frontier.size == 0){ + updateTargets(data); + data.dirty = false; + } + updateFrontier(data, maxUpdate); } } @@ -315,7 +321,8 @@ public class Pathfinder implements Runnable{ } } - int[] values = path.weights; + //use complete weights if possible; these contain a complete flow field that is not being updated + int[] values = path.hasComplete ? path.completeWeights : path.weights; int apos = tile.array(); int value = values[apos]; @@ -341,32 +348,6 @@ public class Pathfinder implements Runnable{ return current; } - /** - * Clears the frontier, increments the search and sets up all flow sources. - * This only occurs for active teams. - */ - private void updateTargets(Flowfield path, int x, int y){ - int packed = world.packArray(x, y); - - if(packed > path.weights.length) return; - - if(path.weights[packed] == 0){ - //this was a previous target - path.frontier.clear(); - }else if(!path.frontier.isEmpty()){ - //skip if this path is processing - return; - } - - //update cost of the tile TODO maybe only update the cost when it's not passable - path.weights[packed] = path.cost.getCost(path.team.id, tiles[packed]); - - //clear frontier to prevent contamination - path.frontier.clear(); - - updateTargets(path); - } - /** Increments the search and sets up flow sources. Does not change the frontier. */ private void updateTargets(Flowfield path){ @@ -421,6 +402,7 @@ public class Pathfinder implements Runnable{ /** Update the frontier for a path. Pathfinding thread only. */ private void updateFrontier(Flowfield path, long nsToRun){ + boolean hadAny = path.frontier.size > 0; long start = Time.nanos(); int counter = 0; @@ -462,6 +444,12 @@ public class Pathfinder implements Runnable{ } } } + + //there WERE some things in the frontier, but now they are gone, so the path is done; copy over latest data + if(hadAny && path.frontier.size == 0){ + System.arraycopy(path.weights, 0, path.completeWeights, 0, path.weights.length); + path.hasComplete = true; + } } public static class EnemyCoreField extends Flowfield{ @@ -505,11 +493,18 @@ public class Pathfinder implements Runnable{ protected Team team = Team.derelict; /** Function for calculating path cost. Set before using. */ protected PathCost cost = costTypes.get(costGround); + /** If true, this flow field needs updating. This flag is only set to false once the flow field finishes and the weights are copied over. */ + protected boolean dirty = false; + /** Whether there are valid weights in the complete array. */ + protected volatile boolean hasComplete; /** costs of getting to a specific tile */ public int[] weights; /** search IDs of each position - the highest, most recent search is prioritized and overwritten */ public int[] searches; + /** the last "complete" weights of this tilemap. */ + public int[] completeWeights; + /** search frontier, these are Pos objects */ IntQueue frontier = new IntQueue(); /** all target positions; these positions have a cost of 0, and must be synchronized on! */ @@ -524,6 +519,7 @@ public class Pathfinder implements Runnable{ void setup(int length){ this.weights = new int[length]; this.searches = new int[length]; + this.completeWeights = new int[length]; this.frontier.ensureCapacity((length) / 4); this.initialized = true; } diff --git a/core/src/mindustry/ai/types/BuilderAI.java b/core/src/mindustry/ai/types/BuilderAI.java index eebb6ef850..202128563c 100644 --- a/core/src/mindustry/ai/types/BuilderAI.java +++ b/core/src/mindustry/ai/types/BuilderAI.java @@ -119,7 +119,7 @@ public class BuilderAI extends AIController{ } //follow someone and help them build - if(timer.get(timerTarget2, 60f)){ + if(timer.get(timerTarget2, 20f)){ found = false; Units.nearby(unit.team, unit.x, unit.y, buildRadius, u -> { diff --git a/core/src/mindustry/logic/LAccess.java b/core/src/mindustry/logic/LAccess.java index f5fcf7a477..2c724d7c41 100644 --- a/core/src/mindustry/logic/LAccess.java +++ b/core/src/mindustry/logic/LAccess.java @@ -60,7 +60,7 @@ public enum LAccess{ all = values(), senseable = Seq.select(all, t -> t.params.length <= 1).toArray(LAccess.class), controls = Seq.select(all, t -> t.params.length > 0).toArray(LAccess.class), - settable = {x, y, rotation, team, flag, health, totalPower, payloadType}; //TODO + settable = {x, y, rotation, team, flag, health, totalPower, payloadType}; LAccess(String... params){ this.params = params; diff --git a/gradle.properties b/gradle.properties index ef35141584..87a75103b8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=a96ce3b267 +archash=cc9c60d49b