This commit is contained in:
Anuken
2023-11-10 01:57:04 -05:00
parent 358a9ca98b
commit 69e2f6b93b
+148 -96
View File
@@ -65,6 +65,12 @@ public class HierarchyPathFinder implements Runnable{
ObjectMap<Unit, PathRequest> unitRequests = new ObjectMap<>(); ObjectMap<Unit, PathRequest> unitRequests = new ObjectMap<>();
//maps position in world in (x + y * width format) to a cache of flow fields //maps position in world in (x + y * width format) to a cache of flow fields
IntMap<FieldCache> requests = new IntMap<>(); IntMap<FieldCache> requests = new IntMap<>();
//these are for inner edge A*
IntFloatMap innerCosts = new IntFloatMap();
PathfindQueue innerFrontier = new PathfindQueue();
/** Current pathfinding thread */ /** Current pathfinding thread */
@Nullable Thread thread; @Nullable Thread thread;
@@ -80,10 +86,16 @@ public class HierarchyPathFinder implements Runnable{
IntIntMap cameFrom = new IntIntMap(); IntIntMap cameFrom = new IntIntMap();
//frontier for A* //frontier for A*
PathfindQueue frontier = new PathfindQueue(); PathfindQueue frontier = new PathfindQueue();
public PathRequest(int destination){
this.destination = destination;
}
} }
static class FieldCache{ static class FieldCache{
int destination; PathCost cost;
int team;
int goalPos;
//frontier for flow fields //frontier for flow fields
IntQueue frontier = new IntQueue(); IntQueue frontier = new IntQueue();
//maps cluster index to field weights; 0 means uninitialized //maps cluster index to field weights; 0 means uninitialized
@@ -91,6 +103,13 @@ public class HierarchyPathFinder implements Runnable{
//TODO: node map for merging //TODO: node map for merging
//TODO: how to extend flowfields? //TODO: how to extend flowfields?
public FieldCache(PathCost cost, int team, int goalPos){
this.cost = cost;
this.team = team;
this.goalPos = goalPos;
}
} }
public HierarchyPathFinder(){ public HierarchyPathFinder(){
@@ -228,30 +247,6 @@ public class HierarchyPathFinder implements Runnable{
queue.clear(); queue.clear();
} }
@Override
public void run(){
while(true){
if(net.client()) return;
try{
if(state.isPlaying()){
queue.run();
//TODO: update everything
}
try{
Thread.sleep(updateInterval);
}catch(InterruptedException e){
//stop looping when interrupted externally
return;
}
}catch(Throwable e){
e.printStackTrace();
}
}
}
Vec2 nodeToVec(int current, Vec2 out){ Vec2 nodeToVec(int current, Vec2 out){
portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), out); portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), out);
return out; return out;
@@ -451,6 +446,9 @@ public class HierarchyPathFinder implements Runnable{
/** @return -1 if no path was found */ /** @return -1 if no path was found */
float innerAstar(int team, PathCost cost, int minX, int minY, int maxX, int maxY, int startPos, int goalPos, int goalX1, int goalY1, int goalX2, int goalY2){ float innerAstar(int team, PathCost cost, int minX, int minY, int maxX, int maxY, int startPos, int goalPos, int goalX1, int goalY1, int goalX2, int goalY2){
var frontier = innerFrontier;
var costs = innerCosts;
frontier.clear(); frontier.clear();
costs.clear(); costs.clear();
@@ -702,78 +700,15 @@ public class HierarchyPathFinder implements Runnable{
} }
} }
public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){ private void updateFields(FieldCache cache, long nsToRun){
int costId = 0; var frontier = cache.frontier;
var fields = cache.fields;
var goalPos = cache.goalPos;
var pcost = cache.cost;
var team = cache.team;
int node = findClosestNode(unit.team.id, costId, unit.tileX(), unit.tileY()); long start = Time.nanos();
int dest = findClosestNode(unit.team.id, costId, World.toTile(destination.x), World.toTile(destination.y)); int counter = 0;
fields = new IntMap<>();
PathCost pcost = ControlPathfinder.costGround;
int team = Team.sharded.id;
int goalPos = (World.toTile(destination.x) + World.toTile(destination.y) * wwidth);
IntQueue frontier = new IntQueue();
frontier.addFirst(goalPos);
Tile tileOn = unit.tileOn();
var result = clusterAstar(costId, node, dest);
if(result != null && tileOn != null){
int fsize = clusterSize * clusterSize;
int cx = unit.tileX() / clusterSize, cy = unit.tileY() / clusterSize;
fields.put(cx + cy * cwidth, new int[fsize]);
for(int i = -1; i < result.size; i++){
int
current = i == -1 ? node : result.items[i],
cluster = NodeIndex.cluster(current),
dir = NodeIndex.dir(current),
dx = Geometry.d4[dir].x,
dy = Geometry.d4[dir].y,
ox = cluster % cwidth + dx,
oy = cluster / cwidth + dy;
//store current cluster in the path list
if(!fields.containsKey(cluster)){
fields.put(cluster, new int[fsize]);
}
//store directionals TODO out of bounds
for(Point2 p : Geometry.d4){
int other = cluster + p.x + p.y * cwidth;
if(!fields.containsKey(other)){
fields.put(other, new int[fsize]);
}
}
//store directional/flipped version of cluster
if(ox >= 0 && oy >= 0 && ox < cwidth && oy < cheight){
int other = ox + oy * cwidth;
if(!fields.containsKey(other)){
fields.put(other, new int[fsize]);
}
//store directionals again
for(Point2 p : Geometry.d4){
int other2 = other + p.x + p.y * cwidth;
if(!fields.containsKey(other2)){
fields.put(other2, new int[fsize]);
}
}
}
}
for(int i = -1; i < result.size - 1; i++){
int current = i == -1 ? node : result.items[i], next = result.items[i + 1];
portalToVec(0, NodeIndex.cluster(current), NodeIndex.dir(current), NodeIndex.portal(current), Tmp.v1);
portalToVec(0, NodeIndex.cluster(next), NodeIndex.dir(next), NodeIndex.portal(next), Tmp.v2);
line(Tmp.v1, Tmp.v2, Color.orange);
}
//actually do the flow field part //actually do the flow field part
//TODO spread this out across many frames //TODO spread this out across many frames
@@ -817,9 +752,97 @@ public class HierarchyPathFinder implements Runnable{
} }
} }
} }
//every N iterations, check the time spent - this prevents extra calls to nano time, which itself is slow
if(nsToRun >= 0 && (counter++) >= 200){
counter = 0;
if(Time.timeSinceNanos(start) >= nsToRun){
return;
}
}
}
} }
public void createPathRequest(Unit unit, int goalX, int goalY){
int costId = 0;
PathCost pcost = ControlPathfinder.costGround;
int team = unit.team.id;
int goalPos = (goalX + goalY * wwidth);
int node = findClosestNode(team, costId, unit.tileX(), unit.tileY());
int dest = findClosestNode(team, costId, goalX, goalY);
//TODO: not new?
PathRequest request = new PathRequest(dest);
var nodePath = clusterAstar(request, costId, node, dest);
//TODO: how to reuse
FieldCache cache = this.requests.get(goalPos, () -> new FieldCache(pcost, team, goalPos));
if(cache.frontier.isEmpty()){
cache.frontier.addFirst(goalPos);
}
if(nodePath != null){
int fsize = clusterSize * clusterSize;
int cx = unit.tileX() / clusterSize, cy = unit.tileY() / clusterSize;
var fields = cache.fields;
fields.put(cx + cy * cwidth, new int[fsize]);
for(int i = -1; i < nodePath.size; i++){
int
current = i == -1 ? node : nodePath.items[i],
cluster = NodeIndex.cluster(current),
dir = NodeIndex.dir(current),
dx = Geometry.d4[dir].x,
dy = Geometry.d4[dir].y,
ox = cluster % cwidth + dx,
oy = cluster / cwidth + dy;
//store current cluster in the path list
if(!fields.containsKey(cluster)){
fields.put(cluster, new int[fsize]);
}
//store directionals TODO out of bounds
for(Point2 p : Geometry.d4){
int other = cluster + p.x + p.y * cwidth;
if(!fields.containsKey(other)){
fields.put(other, new int[fsize]);
}
}
//store directional/flipped version of cluster
if(ox >= 0 && oy >= 0 && ox < cwidth && oy < cheight){
int other = ox + oy * cwidth;
if(!fields.containsKey(other)){
fields.put(other, new int[fsize]);
}
//store directionals again
for(Point2 p : Geometry.d4){
int other2 = other + p.x + p.y * cwidth;
if(!fields.containsKey(other2)){
fields.put(other2, new int[fsize]);
}
}
}
}
}
}
public boolean getPathPosition(Unit unit, int pathId, Vec2 destination, Vec2 out, boolean[] noResultFound){
int costId = 0;
Tile tileOn = unit.tileOn();
if(tileOn != null){
int value = getCost(fields, tileOn.x, tileOn.y); int value = getCost(fields, tileOn.x, tileOn.y);
Tile current = null; Tile current = null;
@@ -887,6 +910,35 @@ public class HierarchyPathFinder implements Runnable{
return cost.getCost(team, pathfinder.tiles[tilePos]); return cost.getCost(team, pathfinder.tiles[tilePos]);
} }
@Override
public void run(){
while(true){
if(net.client()) return;
try{
if(state.isPlaying()){
queue.run();
//TODO: update everything else too
//each update time (not total!) no longer than maxUpdate
for(FieldCache cache : requests.values()){
updateFields(cache, maxUpdate);
}
}
try{
Thread.sleep(updateInterval);
}catch(InterruptedException e){
//stop looping when interrupted externally
return;
}
}catch(Throwable e){
e.printStackTrace();
}
}
}
static class Cluster{ static class Cluster{
IntSeq[] portals = new IntSeq[4]; IntSeq[] portals = new IntSeq[4];
//maps rotation + index of portal to list of IntraEdge objects //maps rotation + index of portal to list of IntraEdge objects