Potential pathfinder spurious error fix

This commit is contained in:
Anuken
2025-04-29 12:23:09 -04:00
parent e06229c5ac
commit b0cf1f7ef3

View File

@@ -107,42 +107,44 @@ public class ControlPathfinder implements Runnable{
//maps team -> pathCost -> flattened array of clusters in 2D
//(what about teams? different path costs?)
Cluster[][][] clusters;
int cwidth, cheight;
final Cluster[][][] clusters = new Cluster[256][][];
final int cwidth = Mathf.ceil((float)world.width() / clusterSize), cheight = Mathf.ceil((float)world.height() / clusterSize);
//temporarily used for resolving connections for intra-edges
IntSet usedEdges = new IntSet();
final IntSet usedEdges = new IntSet();
//tasks to run on pathfinding thread
TaskQueue queue = new TaskQueue();
final TaskQueue queue = new TaskQueue();
//individual requests based on unit - MAIN THREAD ONLY
ObjectMap<Unit, PathRequest> unitRequests = new ObjectMap<>();
final ObjectMap<Unit, PathRequest> unitRequests = new ObjectMap<>();
Seq<PathRequest> threadPathRequests = new Seq<>(false);
final Seq<PathRequest> threadPathRequests = new Seq<>(false);
//TODO: very dangerous usage;
//TODO - it is accessed from the main thread
//TODO - it is written to on the pathfinding thread
//maps position in world in (x + y * width format) | path type | team (bitpacked to long with FieldIndex.get) to a cache of flow fields
LongMap<FieldCache> fields = new LongMap<>();
final LongMap<FieldCache> fields = new LongMap<>();
//MAIN THREAD ONLY
Seq<FieldCache> fieldList = new Seq<>(false);
final Seq<FieldCache> fieldList = new Seq<>(false);
//these are for inner edge A* (temporary!)
IntFloatMap innerCosts = new IntFloatMap();
PathfindQueue innerFrontier = new PathfindQueue();
final IntFloatMap innerCosts = new IntFloatMap();
final PathfindQueue innerFrontier = new PathfindQueue();
//ONLY modify on pathfinding thread.
IntSet clustersToUpdate = new IntSet();
IntSet clustersToInnerUpdate = new IntSet();
final IntSet clustersToUpdate = new IntSet();
final IntSet clustersToInnerUpdate = new IntSet();
//PATHFINDING THREAD - requests that should be recomputed
ObjectSet<PathRequest> invalidRequests = new ObjectSet<>();
final ObjectSet<PathRequest> invalidRequests = new ObjectSet<>();
/** Current pathfinding thread */
@Nullable Thread thread;
/** If true, this pathfinder is no longer relevant (stopped) and its errors can be ignored. */
volatile boolean invalidated;
//path requests are per-unit
static class PathRequest{
final Unit unit;
@@ -211,51 +213,38 @@ public class ControlPathfinder implements Runnable{
LongSeq[][] portalConnections = new LongSeq[4][];
}
public ControlPathfinder(){
Events.on(ResetEvent.class, event -> stop());
static{
Events.on(ResetEvent.class, event -> controlPath.stop());
Events.on(WorldLoadEvent.class, event -> {
stop();
//TODO: can the pathfinding thread even see these?
unitRequests = new ObjectMap<>();
fields = new LongMap<>();
fieldList = new Seq<>(false);
clusters = new Cluster[256][][];
cwidth = Mathf.ceil((float)world.width() / clusterSize);
cheight = Mathf.ceil((float)world.height() / clusterSize);
start();
controlPath.stop();
//create a new pathfinder to avoid contaminating the new pathfinding state with the old thread, which may still be running
controlPath = new ControlPathfinder();
controlPath.start();
});
Events.on(TileChangeEvent.class, e -> {
updateTile(e.tile);
//TODO: recalculate affected flow fields? or just all of them? how to reflow?
controlPath.updateTile(e.tile);
});
//invalidate paths
Events.run(Trigger.update, () -> {
for(var req : unitRequests.values()){
for(var req : controlPath.unitRequests.values()){
//skipped N update -> drop it
if(req.lastUpdateId <= state.updateId - 10 || !req.unit.isAdded()){
req.invalidated = true;
//concurrent modification!
queue.post(() -> threadPathRequests.remove(req));
Core.app.post(() -> unitRequests.remove(req.unit));
controlPath.queue.post(() -> controlPath.threadPathRequests.remove(req));
Time.run(0f, () -> controlPath.unitRequests.remove(req.unit));
}
}
for(var field : fieldList){
for(var field : controlPath.fieldList){
//skipped N update -> drop it
if(field.lastUpdateId <= state.updateId - 30){
//make sure it's only modified on the main thread...? but what about calling get() on this thread??
queue.post(() -> fields.remove(field.mapKey));
Core.app.post(() -> fieldList.remove(field));
controlPath.queue.post(() -> controlPath.fields.remove(field.mapKey));
Time.run(0f, () -> controlPath.fieldList.remove(field));
}
}
});
@@ -268,11 +257,11 @@ public class ControlPathfinder implements Runnable{
Draw.draw(Layer.overlayUI, () -> {
Lines.stroke(1f);
if(clusters[team] != null && clusters[team][cost] != null){
for(int cx = 0; cx < cwidth; cx++){
for(int cy = 0; cy < cheight; cy++){
if(controlPath.clusters[team] != null && controlPath.clusters[team][cost] != null){
for(int cx = 0; cx < controlPath.cwidth; cx++){
for(int cy = 0; cy < controlPath.cheight; cy++){
var cluster = clusters[team][cost][cy * cwidth + cx];
var cluster = controlPath.clusters[team][cost][cy * controlPath.cwidth + cx];
if(cluster != null){
Lines.stroke(0.5f);
Draw.color(Color.gray);
@@ -290,7 +279,7 @@ public class ControlPathfinder implements Runnable{
int from = Point2.x(pos), to = Point2.y(pos);
float width = tilesize * (Math.abs(from - to) + 1), height = tilesize;
portalToVec(cluster, cx, cy, d, i, Tmp.v1);
controlPath.portalToVec(cluster, cx, cy, d, i, Tmp.v1);
Draw.color(Color.brown);
Lines.ellipse(30, Tmp.v1.x, Tmp.v1.y, width / 2f, height / 2f, d * 90f - 90f);
@@ -302,7 +291,7 @@ public class ControlPathfinder implements Runnable{
for(int coni = 0; coni < connections.size; coni ++){
long con = connections.items[coni];
portalToVec(cluster, cx, cy, IntraEdge.dir(con), IntraEdge.portal(con), Tmp.v2);
controlPath.portalToVec(cluster, cx, cy, IntraEdge.dir(con), IntraEdge.portal(con), Tmp.v2);
float
x1 = Tmp.v1.x, y1 = Tmp.v1.y,
@@ -319,10 +308,10 @@ public class ControlPathfinder implements Runnable{
}
}
for(var fields : fieldList){
for(var fields : controlPath.fieldList){
try{
for(var entry : fields.fields){
int cx = entry.key % cwidth, cy = entry.key / cwidth;
int cx = entry.key % controlPath.cwidth, cy = entry.key / controlPath.cwidth;
for(int y = 0; y < clusterSize; y++){
for(int x = 0; x < clusterSize; x++){
int value = entry.value[x + y * clusterSize];
@@ -402,6 +391,7 @@ public class ControlPathfinder implements Runnable{
thread.interrupt();
thread = null;
}
invalidated = true;
queue.clear();
}
@@ -1502,8 +1492,6 @@ public class ControlPathfinder implements Runnable{
while(true){
if(net.client()) return;
try{
if(state.isPlaying()){
queue.run();
@@ -1586,7 +1574,11 @@ public class ControlPathfinder implements Runnable{
return;
}
}catch(Throwable e){
Log.err(e);
if(!invalidated){
Log.err(e);
}else{
return;
}
}
}
}