Pathfinder bugfixes

This commit is contained in:
Anuken
2024-09-18 19:22:51 -04:00
parent e521a56712
commit ab39291626
5 changed files with 30 additions and 11 deletions

View File

@@ -170,6 +170,8 @@ public class Vars implements Loadable{
public static boolean confirmExit = true;
/** if true, UI is not drawn */
public static boolean disableUI;
/** if true, most autosaving is disabled. internal use only! */
public static boolean disableSave;
/** if true, game is set up in mobile mode, even on desktop. used for debugging */
public static boolean testMobile;
/** whether the game is running on a mobile device */

View File

@@ -124,7 +124,7 @@ public class ControlPathfinder implements Runnable{
//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) | type (bitpacked to long) 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<>();
//MAIN THREAD ONLY
Seq<FieldCache> fieldList = new Seq<>(false);
@@ -188,6 +188,7 @@ public class ControlPathfinder implements Runnable{
final IntQueue frontier = new IntQueue();
//maps cluster index to field weights; 0 means uninitialized
final IntMap<int[]> fields = new IntMap<>();
//packed (goalPos | costId | team) long key to use in the global fields map
final long mapKey;
//main thread only!
@@ -200,7 +201,7 @@ public class ControlPathfinder implements Runnable{
this.team = team;
this.goalPos = goalPos;
this.costId = costId;
this.mapKey = Pack.longInt(goalPos, costId);
this.mapKey = FieldIndex.get(goalPos, costId, team);
}
}
@@ -241,7 +242,7 @@ public class ControlPathfinder implements Runnable{
Events.run(Trigger.update, () -> {
for(var req : unitRequests.values()){
//skipped N update -> drop it
if(req.lastUpdateId <= state.updateId - 10){
if(req.lastUpdateId <= state.updateId - 10 || !req.unit.isAdded()){
req.invalidated = true;
//concurrent modification!
queue.post(() -> threadPathRequests.remove(req));
@@ -1024,10 +1025,12 @@ public class ControlPathfinder implements Runnable{
//no result found, bail out.
if(nodePath == null){
request.notFound = true;
//stop following the old path, it's not relevant now, it's just not possible to reach the destination anymore
request.oldCache = null;
return;
}
FieldCache cache = fields.get(Pack.longInt(goalPos, costId));
FieldCache cache = fields.get(FieldIndex.get(goalPos, costId, team));
//if true, extra values are added on the sides of existing field cells that face new cells.
boolean addingFrontier = true;
@@ -1143,7 +1146,7 @@ public class ControlPathfinder implements Runnable{
boolean any = false;
long fieldKey = Pack.longInt(destPos, costId);
long fieldKey = FieldIndex.get(destPos, costId, team);
//use existing request if it exists.
if(request != null && request.destination == destPos){
@@ -1152,13 +1155,14 @@ public class ControlPathfinder implements Runnable{
Tile tileOn = unit.tileOn(), initialTileOn = tileOn;
//TODO: should fields be accessible from this thread?
FieldCache fieldCache = fields.get(fieldKey);
if(fieldCache == null) fieldCache = request.oldCache;
if(fieldCache != null && tileOn != null){
FieldCache old = request.oldCache;
FieldCache targetCache = old != null ? old : fieldCache;
boolean requeue = old == null;
//nullify the old field to be GCed, as it cannot be relevant anymore (this path is complete)
if(fieldCache.frontier.isEmpty() && old != null){
if(fieldCache != request.oldCache && fieldCache.frontier.isEmpty() && old != null){
request.oldCache = null;
}
@@ -1449,7 +1453,7 @@ public class ControlPathfinder implements Runnable{
int index = cx + cy * cwidth;
for(var req : threadPathRequests){
long mapKey = Pack.longInt(req.destination, pathCost);
long mapKey = FieldIndex.get(req.destination, pathCost, team);
var field = fields.get(mapKey);
if((field != null && field.fields.containsKey(index)) || req.notFound){
invalidRequests.add(req);
@@ -1535,7 +1539,7 @@ public class ControlPathfinder implements Runnable{
continue;
}
long mapKey = Pack.longInt(request.destination, request.costId);
long mapKey = FieldIndex.get(request.destination, request.costId, request.team);
var field = fields.get(mapKey);
@@ -1543,7 +1547,7 @@ public class ControlPathfinder implements Runnable{
//it's only worth recalculating a path when the current frontier has finished; otherwise the unit will be following something incomplete.
if(field.frontier.isEmpty()){
//remove the field, to be recalculated next update one recalculatePath is processed
//remove the field, to be recalculated next update once recalculatePath is processed
fields.remove(field.mapKey);
Core.app.post(() -> fieldList.remove(field));
@@ -1551,6 +1555,10 @@ public class ControlPathfinder implements Runnable{
for(var otherRequest : threadPathRequests){
if(otherRequest.destination == request.destination){
otherRequest.oldCache = field;
if(otherRequest != request){
queue.post(() -> recalculatePath(otherRequest));
}
}
}
@@ -1584,6 +1592,15 @@ public class ControlPathfinder implements Runnable{
}
}
@Struct
static class FieldIndexStruct{
int pos;
@StructField(8)
int costId;
@StructField(8)
int team;
}
@Struct
static class IntraEdgeStruct{
@StructField(8)

View File

@@ -111,7 +111,7 @@ public class Saves{
if(state.isGame() && !state.gameOver && current != null && current.isAutosave()){
time += Time.delta;
if(time > Core.settings.getInt("saveinterval") * 60){
if(time > Core.settings.getInt("saveinterval") * 60 && !Vars.disableSave){
saving = true;
try{

View File

@@ -159,7 +159,7 @@ public class PausedDialog extends BaseDialog{
return;
}
if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || wasClient || state.gameOver){
if(control.saves.getCurrent() == null || !control.saves.getCurrent().isAutosave() || wasClient || state.gameOver || disableSave){
logic.reset();
return;
}