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;
|
public static boolean confirmExit = true;
|
||||||
/** if true, UI is not drawn */
|
/** if true, UI is not drawn */
|
||||||
public static boolean disableUI;
|
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 */
|
/** if true, game is set up in mobile mode, even on desktop. used for debugging */
|
||||||
public static boolean testMobile;
|
public static boolean testMobile;
|
||||||
/** whether the game is running on a mobile device */
|
/** whether the game is running on a mobile device */
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ public class ControlPathfinder implements Runnable{
|
|||||||
//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) | 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<>();
|
LongMap<FieldCache> fields = new LongMap<>();
|
||||||
//MAIN THREAD ONLY
|
//MAIN THREAD ONLY
|
||||||
Seq<FieldCache> fieldList = new Seq<>(false);
|
Seq<FieldCache> fieldList = new Seq<>(false);
|
||||||
@@ -188,6 +188,7 @@ public class ControlPathfinder implements Runnable{
|
|||||||
final IntQueue frontier = new IntQueue();
|
final IntQueue frontier = new IntQueue();
|
||||||
//maps cluster index to field weights; 0 means uninitialized
|
//maps cluster index to field weights; 0 means uninitialized
|
||||||
final IntMap<int[]> fields = new IntMap<>();
|
final IntMap<int[]> fields = new IntMap<>();
|
||||||
|
//packed (goalPos | costId | team) long key to use in the global fields map
|
||||||
final long mapKey;
|
final long mapKey;
|
||||||
|
|
||||||
//main thread only!
|
//main thread only!
|
||||||
@@ -200,7 +201,7 @@ public class ControlPathfinder implements Runnable{
|
|||||||
this.team = team;
|
this.team = team;
|
||||||
this.goalPos = goalPos;
|
this.goalPos = goalPos;
|
||||||
this.costId = costId;
|
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, () -> {
|
Events.run(Trigger.update, () -> {
|
||||||
for(var req : unitRequests.values()){
|
for(var req : unitRequests.values()){
|
||||||
//skipped N update -> drop it
|
//skipped N update -> drop it
|
||||||
if(req.lastUpdateId <= state.updateId - 10){
|
if(req.lastUpdateId <= state.updateId - 10 || !req.unit.isAdded()){
|
||||||
req.invalidated = true;
|
req.invalidated = true;
|
||||||
//concurrent modification!
|
//concurrent modification!
|
||||||
queue.post(() -> threadPathRequests.remove(req));
|
queue.post(() -> threadPathRequests.remove(req));
|
||||||
@@ -1024,10 +1025,12 @@ public class ControlPathfinder implements Runnable{
|
|||||||
//no result found, bail out.
|
//no result found, bail out.
|
||||||
if(nodePath == null){
|
if(nodePath == null){
|
||||||
request.notFound = true;
|
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;
|
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.
|
//if true, extra values are added on the sides of existing field cells that face new cells.
|
||||||
boolean addingFrontier = true;
|
boolean addingFrontier = true;
|
||||||
|
|
||||||
@@ -1143,7 +1146,7 @@ public class ControlPathfinder implements Runnable{
|
|||||||
|
|
||||||
boolean any = false;
|
boolean any = false;
|
||||||
|
|
||||||
long fieldKey = Pack.longInt(destPos, costId);
|
long fieldKey = FieldIndex.get(destPos, costId, team);
|
||||||
|
|
||||||
//use existing request if it exists.
|
//use existing request if it exists.
|
||||||
if(request != null && request.destination == destPos){
|
if(request != null && request.destination == destPos){
|
||||||
@@ -1152,13 +1155,14 @@ public class ControlPathfinder implements Runnable{
|
|||||||
Tile tileOn = unit.tileOn(), initialTileOn = tileOn;
|
Tile tileOn = unit.tileOn(), initialTileOn = tileOn;
|
||||||
//TODO: should fields be accessible from this thread?
|
//TODO: should fields be accessible from this thread?
|
||||||
FieldCache fieldCache = fields.get(fieldKey);
|
FieldCache fieldCache = fields.get(fieldKey);
|
||||||
|
if(fieldCache == null) fieldCache = request.oldCache;
|
||||||
|
|
||||||
if(fieldCache != null && tileOn != null){
|
if(fieldCache != null && tileOn != null){
|
||||||
FieldCache old = request.oldCache;
|
FieldCache old = request.oldCache;
|
||||||
FieldCache targetCache = old != null ? old : fieldCache;
|
FieldCache targetCache = old != null ? old : fieldCache;
|
||||||
boolean requeue = old == null;
|
boolean requeue = old == null;
|
||||||
//nullify the old field to be GCed, as it cannot be relevant anymore (this path is complete)
|
//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;
|
request.oldCache = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1449,7 +1453,7 @@ public class ControlPathfinder implements Runnable{
|
|||||||
int index = cx + cy * cwidth;
|
int index = cx + cy * cwidth;
|
||||||
|
|
||||||
for(var req : threadPathRequests){
|
for(var req : threadPathRequests){
|
||||||
long mapKey = Pack.longInt(req.destination, pathCost);
|
long mapKey = FieldIndex.get(req.destination, pathCost, team);
|
||||||
var field = fields.get(mapKey);
|
var field = fields.get(mapKey);
|
||||||
if((field != null && field.fields.containsKey(index)) || req.notFound){
|
if((field != null && field.fields.containsKey(index)) || req.notFound){
|
||||||
invalidRequests.add(req);
|
invalidRequests.add(req);
|
||||||
@@ -1535,7 +1539,7 @@ public class ControlPathfinder implements Runnable{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
long mapKey = Pack.longInt(request.destination, request.costId);
|
long mapKey = FieldIndex.get(request.destination, request.costId, request.team);
|
||||||
|
|
||||||
var field = fields.get(mapKey);
|
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.
|
//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()){
|
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);
|
fields.remove(field.mapKey);
|
||||||
Core.app.post(() -> fieldList.remove(field));
|
Core.app.post(() -> fieldList.remove(field));
|
||||||
|
|
||||||
@@ -1551,6 +1555,10 @@ public class ControlPathfinder implements Runnable{
|
|||||||
for(var otherRequest : threadPathRequests){
|
for(var otherRequest : threadPathRequests){
|
||||||
if(otherRequest.destination == request.destination){
|
if(otherRequest.destination == request.destination){
|
||||||
otherRequest.oldCache = field;
|
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
|
@Struct
|
||||||
static class IntraEdgeStruct{
|
static class IntraEdgeStruct{
|
||||||
@StructField(8)
|
@StructField(8)
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class Saves{
|
|||||||
|
|
||||||
if(state.isGame() && !state.gameOver && current != null && current.isAutosave()){
|
if(state.isGame() && !state.gameOver && current != null && current.isAutosave()){
|
||||||
time += Time.delta;
|
time += Time.delta;
|
||||||
if(time > Core.settings.getInt("saveinterval") * 60){
|
if(time > Core.settings.getInt("saveinterval") * 60 && !Vars.disableSave){
|
||||||
saving = true;
|
saving = true;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ public class PausedDialog extends BaseDialog{
|
|||||||
return;
|
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();
|
logic.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user