From 85fdc2df34e2bd12acad7dde1abf1177c5b236de Mon Sep 17 00:00:00 2001 From: Leijurv Date: Mon, 13 Aug 2018 12:35:44 -0700 Subject: [PATCH] planning ahead --- .../bot/behavior/impl/PathingBehavior.java | 96 +++++++++++++++++-- .../java/baritone/bot/pathing/path/IPath.java | 7 +- .../bot/pathing/path/PathExecutor.java | 34 ++++--- 3 files changed, 114 insertions(+), 23 deletions(-) diff --git a/src/main/java/baritone/bot/behavior/impl/PathingBehavior.java b/src/main/java/baritone/bot/behavior/impl/PathingBehavior.java index 6bf61989..191f0169 100644 --- a/src/main/java/baritone/bot/behavior/impl/PathingBehavior.java +++ b/src/main/java/baritone/bot/behavior/impl/PathingBehavior.java @@ -49,16 +49,79 @@ public class PathingBehavior extends Behavior { private Goal goal; + private volatile boolean isPathCalcInProgress; + private final Object pathCalcLock = new Object(); + + private final Object pathPlanLock = new Object(); + @Override public void onTick(TickEvent event) { if (event.getType() == TickEvent.Type.OUT || current == null) { return; } - current.onTick(event); - if (current.failed() || current.finished()) { - current = null; - if (!goal.isInGoal(playerFeet())) - findPathInNewThread(playerFeet(), true); + boolean safe = current.onTick(event); + synchronized (pathPlanLock) { + if (current.failed() || current.finished()) { + current = null; + if (next != null && !next.getPath().positions().contains(playerFeet())) { + // if the current path failed, we may not actually be on the next one, so make sure + displayChatMessageRaw("Discarding next path as it does not contain current position"); + // for example if we had a nicely planned ahead path that starts where current ends + // that's all fine and good + // but if we fail in the middle of current + // we're nowhere close to our planned ahead path + // so need to discard it sadly. + next = null; + } + if (next != null) { + current = next; + next = null; + return; + } + if (goal.isInGoal(playerFeet())) { + return; + } + // at this point, current just ended, but we aren't in the goal and have no plan for the future + synchronized (pathCalcLock) { + if (isPathCalcInProgress) { + // if we aren't calculating right now + return; + } + findPathInNewThread(playerFeet(), true); + } + return; + } + // at this point, we know current is in progress + if (safe) { + // a movement just ended + if (next != null) { + if (next.getPath().positions().contains(playerFeet())) { + // jump directly onto the next path + current = next; + next = null; + return; + } + } + } + synchronized (pathCalcLock) { + if (isPathCalcInProgress) { + // if we aren't calculating right now + return; + } + if (next != null) { + // and we have no plan for what to do next + return; + } + if (goal.isInGoal(current.getPath().getDest())) { + // and this path dosen't get us all the way there + return; + } + if (current.getPath().ticksRemainingFrom(current.getPosition()) < 100) { + // and this path has 5 seconds or less left + displayChatMessageRaw("Path almost over; planning ahead"); + findPathInNewThread(current.getPath().getDest(), true); + } + } } } @@ -136,12 +199,30 @@ public class PathingBehavior extends Behavior { * @param talkAboutIt */ public void findPathInNewThread(final BlockPos start, final boolean talkAboutIt) { + synchronized (pathCalcLock) { + if (isPathCalcInProgress) { + throw new IllegalStateException("Already doing it"); + } + isPathCalcInProgress = true; + } new Thread(() -> { if (talkAboutIt) { displayChatMessageRaw("Starting to search for path from " + start + " to " + goal); } - findPath(start).map(PathExecutor::new).ifPresent(path -> current = path); + findPath(start).map(PathExecutor::new).ifPresent(path -> { + synchronized (pathPlanLock) { + if (current == null) { + current = path; + } else { + if (next == null) { + next = path; + } else { + throw new IllegalStateException("I have no idea what to do with this path"); + } + } + } + }); /* isThereAnythingInProgress = false; if (!currentPath.goal.isInGoal(currentPath.end)) { @@ -156,6 +237,9 @@ public class PathingBehavior extends Behavior { if (talkAboutIt && current != null && current.getPath() != null) { displayChatMessageRaw("Finished finding a path from " + start + " towards " + goal + ". " + current.getPath().getNumNodesConsidered() + " nodes considered"); } + synchronized (pathCalcLock) { + isPathCalcInProgress = false; + } }).start(); } diff --git a/src/main/java/baritone/bot/pathing/path/IPath.java b/src/main/java/baritone/bot/pathing/path/IPath.java index 0a8c3ca2..5c348b31 100644 --- a/src/main/java/baritone/bot/pathing/path/IPath.java +++ b/src/main/java/baritone/bot/pathing/path/IPath.java @@ -17,7 +17,6 @@ package baritone.bot.pathing.path; -import baritone.bot.pathing.movement.CalculationContext; import baritone.bot.pathing.movement.Movement; import baritone.bot.utils.Utils; import net.minecraft.util.Tuple; @@ -108,11 +107,11 @@ public interface IPath { return pos.get(pos.size() - 1); } - default double ticksRemaining(int pathPosition) { + default double ticksRemainingFrom(int pathPosition) { double sum = 0; - CalculationContext ctx = new CalculationContext(); + //this is fast because we aren't requesting recalculation, it's just cached for (int i = pathPosition; i < movements().size(); i++) { - sum += movements().get(i).getCost(ctx); + sum += movements().get(i).getCost(null); } return sum; } diff --git a/src/main/java/baritone/bot/pathing/path/PathExecutor.java b/src/main/java/baritone/bot/pathing/path/PathExecutor.java index 0e444599..429705ff 100644 --- a/src/main/java/baritone/bot/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/bot/pathing/path/PathExecutor.java @@ -18,12 +18,12 @@ package baritone.bot.pathing.path; import baritone.bot.Baritone; -import baritone.bot.behavior.Behavior; import baritone.bot.event.events.TickEvent; import baritone.bot.pathing.movement.ActionCosts; import baritone.bot.pathing.movement.Movement; import baritone.bot.pathing.movement.MovementState; import baritone.bot.utils.BlockStateInterface; +import baritone.bot.utils.Helper; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.init.Blocks; import net.minecraft.util.Tuple; @@ -41,7 +41,7 @@ import static baritone.bot.pathing.movement.MovementState.MovementStatus.*; * * @author leijurv */ -public class PathExecutor extends Behavior { +public class PathExecutor implements Helper { private static final double MAX_DIST_FROM_PATH = 2; private static final double MAX_TICKS_AWAY = 200; // ten seconds private final IPath path; @@ -61,15 +61,21 @@ public class PathExecutor extends Behavior { this.pathPosition = 0; } - @Override - public void onTick(TickEvent event) { + /** + * Tick this executor + * + * @param event + * @return True if a movement just finished (and the player is therefore in a "stable" state, like, + * not sneaking out over lava), false otherwise + */ + public boolean onTick(TickEvent event) { if (event.getType() == TickEvent.Type.OUT) { - return; + throw new IllegalStateException(); } if (pathPosition >= path.length()) { //stop bugging me, I'm done //TODO Baritone.INSTANCE.behaviors.remove(this) - return; + return true; } BlockPos whereShouldIBe = path.positions().get(pathPosition); EntityPlayerSP thePlayer = mc.player; @@ -77,7 +83,7 @@ public class PathExecutor extends Behavior { if (pathPosition == path.length() - 1) { displayChatMessageRaw("On last position, ending this path."); pathPosition++; - return; + return true; } if (!whereShouldIBe.equals(whereAmI)) { System.out.println("Should be at " + whereShouldIBe + " actually am at " + whereAmI); @@ -86,7 +92,7 @@ public class PathExecutor extends Behavior { if (whereAmI.equals(path.positions().get(i))) { displayChatMessageRaw("Skipping back " + (pathPosition - i) + " steps, to " + i); pathPosition = Math.max(i - 1, 0); // previous step might not actually be done - return; + return false; } } for (int i = pathPosition + 2; i < path.length(); i++) { //dont check pathPosition+1. the movement tells us when it's done (e.g. sneak placing) @@ -95,7 +101,7 @@ public class PathExecutor extends Behavior { displayChatMessageRaw("Skipping forward " + (i - pathPosition) + " steps, to " + i); } pathPosition = i - 1; - return; + return false; } } } @@ -111,7 +117,7 @@ public class PathExecutor extends Behavior { pathPosition = path.length() + 3; Baritone.INSTANCE.getInputOverrideHandler().clearAllKeys(); failed = true; - return; + return false; } } else { ticksAway = 0; @@ -194,7 +200,7 @@ public class PathExecutor extends Behavior { pathPosition = path.length() + 3; failed = true; Baritone.INSTANCE.getInputOverrideHandler().clearAllKeys(); - return; + return true; } if (costEstimateIndex == null || costEstimateIndex != pathPosition) { costEstimateIndex = pathPosition; @@ -206,7 +212,7 @@ public class PathExecutor extends Behavior { pathPosition = path.length() + 3; failed = true; Baritone.INSTANCE.getInputOverrideHandler().clearAllKeys(); - return; + return true; } if (movementStatus == SUCCESS) { System.out.println("Movement done, next path"); @@ -214,6 +220,7 @@ public class PathExecutor extends Behavior { ticksOnCurrent = 0; Baritone.INSTANCE.getInputOverrideHandler().clearAllKeys(); onTick(event); + return true; } else { ticksOnCurrent++; if (ticksOnCurrent > currentMovementInitialCostEstimate + 100) { @@ -226,9 +233,10 @@ public class PathExecutor extends Behavior { Baritone.INSTANCE.getInputOverrideHandler().clearAllKeys(); pathPosition = path.length() + 3; failed = true; - return; + return true; } } + return false; // movement is in progress } public int getPosition() {