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