diff --git a/src/main/java/baritone/bot/Settings.java b/src/main/java/baritone/bot/Settings.java index 9e68df8b..7a22abc2 100644 --- a/src/main/java/baritone/bot/Settings.java +++ b/src/main/java/baritone/bot/Settings.java @@ -35,17 +35,22 @@ public class Settings { public Setting chuckCaching = new Setting<>(false); public Setting allowWaterBucketFall = new Setting<>(true); public Setting planningTickLookAhead = new Setting<>(150); - public Setting renderPath = new Setting<>(true); public Setting chatDebug = new Setting<>(true); public Setting chatControl = new Setting<>(true); // probably false in impact + public Setting renderPath = new Setting<>(true); public Setting fadePath = new Setting<>(false); // give this a better name in the UI, like "better path fps" idk + public Setting pathTimeoutMS = new Setting<>(4000L); public Setting slowPath = new Setting<>(false); + public Setting slowPathTimeDelayMS = new Setting<>(100L); + public Setting slowPathTimeoutMS = new Setting<>(40000L); public Setting> acceptableThrowAwayItems = new Setting<>(Arrays.asList( Item.getItemFromBlock(Blocks.DIRT), Item.getItemFromBlock(Blocks.COBBLESTONE), Item.getItemFromBlock(Blocks.NETHERRACK) )); public Setting renderGoal = new Setting<>(true); + public Setting pathingMaxChunkBorderFetch = new Setting<>(50); + public Setting backtrackCostFavoringCoefficient = new Setting<>(0.9); public final Map> byName; public final List> allSettings; @@ -63,8 +68,8 @@ public class Settings { this.klass = (Class) value.getClass(); } - public final T get() { - return value; + public final K get() { + return (K) value; } public final String getName() { diff --git a/src/main/java/baritone/bot/behavior/impl/PathingBehavior.java b/src/main/java/baritone/bot/behavior/impl/PathingBehavior.java index 00153053..5be89412 100644 --- a/src/main/java/baritone/bot/behavior/impl/PathingBehavior.java +++ b/src/main/java/baritone/bot/behavior/impl/PathingBehavior.java @@ -93,7 +93,7 @@ public class PathingBehavior extends Behavior { // if we aren't calculating right now return; } - findPathInNewThread(playerFeet(), true); + findPathInNewThread(playerFeet(), true, Optional.empty()); } return; } @@ -126,7 +126,7 @@ public class PathingBehavior extends Behavior { if (ticksRemainingInSegment().get() < Baritone.settings().planningTickLookAhead.get()) { // and this path has 5 seconds or less left displayChatMessageRaw("Path almost over. Planning ahead..."); - findPathInNewThread(current.getPath().getDest(), false); + findPathInNewThread(current.getPath().getDest(), false, Optional.of(current.getPath())); } } } @@ -160,11 +160,17 @@ public class PathingBehavior extends Behavior { } public void path() { - synchronized (pathCalcLock) { - if (isPathCalcInProgress) { + synchronized (pathPlanLock) { + if (current != null) { + displayChatMessageRaw("Currently executing a path. Please cancel it first."); return; } - findPathInNewThread(playerFeet(), true); + synchronized (pathCalcLock) { + if (isPathCalcInProgress) { + return; + } + findPathInNewThread(playerFeet(), true, Optional.empty()); + } } } @@ -174,7 +180,7 @@ public class PathingBehavior extends Behavior { * @param start * @param talkAboutIt */ - private void findPathInNewThread(final BlockPos start, final boolean talkAboutIt) { + private void findPathInNewThread(final BlockPos start, final boolean talkAboutIt, final Optional previous) { synchronized (pathCalcLock) { if (isPathCalcInProgress) { throw new IllegalStateException("Already doing it"); @@ -186,7 +192,7 @@ public class PathingBehavior extends Behavior { displayChatMessageRaw("Starting to search for path from " + start + " to " + goal); } - findPath(start).map(IPath::cutoffAtLoadedChunks).map(PathExecutor::new).ifPresent(path -> { + findPath(start, previous).map(IPath::cutoffAtLoadedChunks).map(PathExecutor::new).ifPresent(path -> { synchronized (pathPlanLock) { if (current == null) { current = path; @@ -219,13 +225,13 @@ public class PathingBehavior extends Behavior { * @param start * @return */ - private Optional findPath(BlockPos start) { + private Optional findPath(BlockPos start, Optional previous) { if (goal == null) { displayChatMessageRaw("no goal"); return Optional.empty(); } try { - IPathFinder pf = new AStarPathFinder(start, goal); + IPathFinder pf = new AStarPathFinder(start, goal, previous.map(IPath::positions)); return pf.calculate(); } catch (Exception e) { e.printStackTrace(); @@ -251,6 +257,7 @@ public class PathingBehavior extends Behavior { PathExecutor current = this.current; // this should prevent most race conditions? PathExecutor next = this.next; // like, now it's not possible for current!=null to be true, then suddenly false because of another thread // TODO is this enough, or do we need to acquire a lock here? + // TODO benchmark synchronized in render loop // Render the current path, if there is one if (current != null && current.getPath() != null) { diff --git a/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java index 694b05a3..9d308162 100644 --- a/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/bot/pathing/calc/AStarPathFinder.java @@ -52,6 +52,8 @@ import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.world.chunk.EmptyChunk; +import java.util.Collection; +import java.util.HashSet; import java.util.Optional; import java.util.Random; @@ -62,8 +64,11 @@ import java.util.Random; */ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { - public AStarPathFinder(BlockPos start, Goal goal) { + private final Optional> favoredPositions; + + public AStarPathFinder(BlockPos start, Goal goal, Optional> favoredPositions) { super(start, goal); + this.favoredPositions = favoredPositions.map(HashSet::new); // <-- okay this is epic } @Override @@ -72,26 +77,30 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { startNode.cost = 0; startNode.combinedCost = startNode.estimatedCostToGoal; IOpenSet openSet = new BinaryHeapOpenSet(); - startNode.isOpen = true; openSet.insert(startNode); + startNode.isOpen = true; bestSoFar = new PathNode[COEFFICIENTS.length];//keep track of the best node by the metric of (estimatedCostToGoal + cost / COEFFICIENTS[i]) double[] bestHeuristicSoFar = new double[COEFFICIENTS.length]; for (int i = 0; i < bestHeuristicSoFar.length; i++) { bestHeuristicSoFar[i] = Double.MAX_VALUE; } + CalculationContext calcContext = new CalculationContext(); + HashSet favored = favoredPositions.orElse(null); currentlyRunning = this; long startTime = System.currentTimeMillis(); boolean slowPath = Baritone.settings().slowPath.get(); - long timeoutTime = startTime + (slowPath ? 40000 : 4000); + long timeoutTime = startTime + (slowPath ? Baritone.settings().slowPathTimeoutMS : Baritone.settings().pathTimeoutMS).get(); long lastPrintout = 0; int numNodes = 0; - CalculationContext calcContext = new CalculationContext(); int numEmptyChunk = 0; + boolean favoring = favoredPositions.isPresent(); // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior boolean cache = Baritone.settings().chuckCaching.get(); - while (!openSet.isEmpty() && numEmptyChunk < 50 && System.currentTimeMillis() < timeoutTime) { + int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.get(); + double favorCoeff = Baritone.settings().backtrackCostFavoringCoefficient.get(); + while (!openSet.isEmpty() && numEmptyChunk < pathingMaxChunkBorderFetch && System.currentTimeMillis() < timeoutTime) { if (slowPath) { try { - Thread.sleep(100); + Thread.sleep(Baritone.settings().slowPathTimeDelayMS.get()); } catch (InterruptedException ex) { } } @@ -109,7 +118,7 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { return Optional.of(new Path(startNode, currentNode, numNodes)); } //long constructStart = System.nanoTime(); - Movement[] possibleMovements = getConnectedPositions(currentNodePos, calcContext);//movement that we could take that start at myPos, in random order + Movement[] possibleMovements = getConnectedPositions(currentNodePos, calcContext);//movement that we could take that start at currentNodePos, in random order shuffle(possibleMovements); //long constructEnd = System.nanoTime(); //System.out.println(constructEnd - constructStart); @@ -117,16 +126,16 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { if (movementToGetToNeighbor == null) { continue; } - + BetterBlockPos dest = (BetterBlockPos) movementToGetToNeighbor.getDest(); boolean isPositionCached = false; if (cache) { if (CachedWorldProvider.INSTANCE.getCurrentWorld() != null) { - if (CachedWorldProvider.INSTANCE.getCurrentWorld().getBlockType(movementToGetToNeighbor.getDest()) != null) { + if (CachedWorldProvider.INSTANCE.getCurrentWorld().getBlockType(dest) != null) { isPositionCached = true; } } } - if (!isPositionCached && Minecraft.getMinecraft().world.getChunk(movementToGetToNeighbor.getDest()) instanceof EmptyChunk) { + if (!isPositionCached && Minecraft.getMinecraft().world.getChunk(dest) instanceof EmptyChunk) { numEmptyChunk++; continue; } @@ -141,7 +150,10 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { if (actionCost <= 0) { throw new IllegalStateException(movementToGetToNeighbor.getClass() + " " + movementToGetToNeighbor + " calculated implausible cost " + actionCost); } - PathNode neighbor = getNodeAtPosition((BetterBlockPos) movementToGetToNeighbor.getDest()); + if (favoring && favored.contains(dest)) { + actionCost *= favorCoeff; + } + PathNode neighbor = getNodeAtPosition(dest); double tentativeCost = currentNode.cost + actionCost; if (tentativeCost < neighbor.cost) { if (tentativeCost < 0) { diff --git a/src/main/java/baritone/bot/pathing/path/IPath.java b/src/main/java/baritone/bot/pathing/path/IPath.java index 95eba5b1..d764b12a 100644 --- a/src/main/java/baritone/bot/pathing/path/IPath.java +++ b/src/main/java/baritone/bot/pathing/path/IPath.java @@ -20,6 +20,7 @@ package baritone.bot.pathing.path; import baritone.bot.pathing.movement.Movement; import baritone.bot.utils.Helper; import baritone.bot.utils.Utils; +import baritone.bot.utils.pathing.BetterBlockPos; import net.minecraft.client.Minecraft; import net.minecraft.util.Tuple; import net.minecraft.util.math.BlockPos; @@ -44,7 +45,7 @@ public interface IPath extends Helper { * All positions along the way. * Should begin with the same as getSrc and end with the same as getDest */ - List positions(); + List positions(); /** * Number of positions in this path @@ -62,7 +63,7 @@ public interface IPath extends Helper { * @return */ default Movement subsequentMovement(BlockPos currentPosition) { - List pos = positions(); + List pos = positions(); List movements = movements(); for (int i = 0; i < pos.size(); i++) { if (currentPosition.equals(pos.get(i))) { @@ -98,15 +99,15 @@ public interface IPath extends Helper { /** * Where does this path start */ - default BlockPos getSrc() { + default BetterBlockPos getSrc() { return positions().get(0); } /** * Where does this path end */ - default BlockPos getDest() { - List pos = positions(); + default BetterBlockPos getDest() { + List pos = positions(); return pos.get(pos.size() - 1); }