Pathfinder bugfixes
This commit is contained in:
Binary file not shown.
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user