From 9c93d3a474d41dc6eb07af47e1fd1af3d4ac2142 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Tue, 6 Nov 2018 11:18:13 -0800 Subject: [PATCH] splice next into current if no backtrack, fixes #122, fixes #249 --- src/api/java/baritone/api/Settings.java | 10 +++ .../java/baritone/api/pathing/calc/IPath.java | 30 +++++++ .../baritone/behavior/PathingBehavior.java | 6 +- src/main/java/baritone/pathing/calc/Path.java | 27 +----- .../pathing/{calc => path}/CutoffPath.java | 13 ++- .../baritone/pathing/path/PathExecutor.java | 37 ++++++++ .../baritone/pathing/path/SplicedPath.java | 88 +++++++++++++++++++ 7 files changed, 180 insertions(+), 31 deletions(-) rename src/main/java/baritone/pathing/{calc => path}/CutoffPath.java (78%) create mode 100644 src/main/java/baritone/pathing/path/SplicedPath.java diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 8cf6a845..48385313 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -377,6 +377,16 @@ public class Settings { */ public Setting walkWhileBreaking = new Setting<>(true); + /** + * If we are more than 500 movements into the current path, discard the oldest segments, as they are no longer useful + */ + public Setting maxPathHistoryLength = new Setting<>(500); + + /** + * If the current path is too long, cut off this many movements from the beginning. + */ + public Setting pathHistoryCutoffAmount = new Setting<>(100); + /** * Rescan for the goal once every 5 ticks. * Set to 0 to disable. diff --git a/src/api/java/baritone/api/pathing/calc/IPath.java b/src/api/java/baritone/api/pathing/calc/IPath.java index fddc0232..f2309e0e 100644 --- a/src/api/java/baritone/api/pathing/calc/IPath.java +++ b/src/api/java/baritone/api/pathing/calc/IPath.java @@ -21,6 +21,7 @@ import baritone.api.Settings; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.movement.IMovement; import baritone.api.utils.BetterBlockPos; +import net.minecraft.util.math.BlockPos; import java.util.List; @@ -132,4 +133,33 @@ public interface IPath { default IPath staticCutoff(Goal destination) { return this; } + + + /** + * Performs a series of checks to ensure that the assembly of the path went as expected. + */ + default void sanityCheck() { + List path = positions(); + List movements = movements(); + if (!getSrc().equals(path.get(0))) { + throw new IllegalStateException("Start node does not equal first path element"); + } + if (!getDest().equals(path.get(path.size() - 1))) { + throw new IllegalStateException("End node does not equal last path element"); + } + if (path.size() != movements.size() + 1) { + throw new IllegalStateException("Size of path array is unexpected"); + } + for (int i = 0; i < path.size() - 1; i++) { + BlockPos src = path.get(i); + BlockPos dest = path.get(i + 1); + IMovement movement = movements.get(i); + if (!src.equals(movement.getSrc())) { + throw new IllegalStateException("Path source is not equal to the movement source"); + } + if (!dest.equals(movement.getDest())) { + throw new IllegalStateException("Path destination is not equal to the movement destination"); + } + } + } } diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index 77c9839d..53b771da 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -31,9 +31,9 @@ import baritone.api.utils.BetterBlockPos; import baritone.api.utils.interfaces.IGoalRenderPos; import baritone.pathing.calc.AStarPathFinder; import baritone.pathing.calc.AbstractNodeCostSearch; -import baritone.pathing.calc.CutoffPath; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; +import baritone.pathing.path.CutoffPath; import baritone.pathing.path.PathExecutor; import baritone.utils.BlockBreakHelper; import baritone.utils.Helper; @@ -156,6 +156,10 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, current.onTick(); return; } + current = current.trySplice(next); + if (next != null && current.getPath().getDest().equals(next.getPath().getDest())) { + next = null; + } synchronized (pathCalcLock) { if (isPathCalcInProgress) { // if we aren't calculating right now diff --git a/src/main/java/baritone/pathing/calc/Path.java b/src/main/java/baritone/pathing/calc/Path.java index adcd9b71..93f021c6 100644 --- a/src/main/java/baritone/pathing/calc/Path.java +++ b/src/main/java/baritone/pathing/calc/Path.java @@ -24,6 +24,7 @@ import baritone.api.pathing.movement.IMovement; import baritone.api.utils.BetterBlockPos; import baritone.pathing.movement.Movement; import baritone.pathing.movement.Moves; +import baritone.pathing.path.CutoffPath; import net.minecraft.client.Minecraft; import net.minecraft.util.math.BlockPos; import net.minecraft.world.chunk.EmptyChunk; @@ -104,32 +105,6 @@ class Path implements IPath { path.addAll(tempPath); } - /** - * Performs a series of checks to ensure that the assembly of the path went as expected. - */ - private void sanityCheck() { - if (!start.equals(path.get(0))) { - throw new IllegalStateException("Start node does not equal first path element"); - } - if (!end.equals(path.get(path.size() - 1))) { - throw new IllegalStateException("End node does not equal last path element"); - } - if (path.size() != movements.size() + 1) { - throw new IllegalStateException("Size of path array is unexpected"); - } - for (int i = 0; i < path.size() - 1; i++) { - BlockPos src = path.get(i); - BlockPos dest = path.get(i + 1); - Movement movement = movements.get(i); - if (!src.equals(movement.getSrc())) { - throw new IllegalStateException("Path source is not equal to the movement source"); - } - if (!dest.equals(movement.getDest())) { - throw new IllegalStateException("Path destination is not equal to the movement destination"); - } - } - } - private void assembleMovements() { if (path.isEmpty() || !movements.isEmpty()) { throw new IllegalStateException(); diff --git a/src/main/java/baritone/pathing/calc/CutoffPath.java b/src/main/java/baritone/pathing/path/CutoffPath.java similarity index 78% rename from src/main/java/baritone/pathing/calc/CutoffPath.java rename to src/main/java/baritone/pathing/path/CutoffPath.java index 53e0c560..9714d8ce 100644 --- a/src/main/java/baritone/pathing/calc/CutoffPath.java +++ b/src/main/java/baritone/pathing/path/CutoffPath.java @@ -15,7 +15,7 @@ * along with Baritone. If not, see . */ -package baritone.pathing.calc; +package baritone.pathing.path; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; @@ -35,11 +35,16 @@ public class CutoffPath implements IPath { private final Goal goal; - CutoffPath(IPath prev, int lastPositionToInclude) { - path = prev.positions().subList(0, lastPositionToInclude + 1); - movements = prev.movements().subList(0, lastPositionToInclude + 1); + public CutoffPath(IPath prev, int firstPositionToInclude, int lastPositionToInclude) { + path = prev.positions().subList(firstPositionToInclude, lastPositionToInclude + 1); + movements = prev.movements().subList(firstPositionToInclude, lastPositionToInclude); numNodes = prev.getNumNodesConsidered(); goal = prev.getGoal(); + sanityCheck(); + } + + public CutoffPath(IPath prev, int lastPositionToInclude) { + this(prev, 0, lastPositionToInclude); } @Override diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index e5f9c864..5cc472e6 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -454,6 +454,43 @@ public class PathExecutor implements IPathExecutor, Helper { return pathPosition; } + public PathExecutor trySplice(PathExecutor next) { + if (next == null) { + return cutIfTooLong(); + } + return SplicedPath.trySplice(path, next.path).map(path -> { + if (!path.getDest().equals(next.getPath().getDest())) { + throw new IllegalStateException(); + } + PathExecutor ret = new PathExecutor(path); + ret.pathPosition = pathPosition; + ret.currentMovementOriginalCostEstimate = currentMovementOriginalCostEstimate; + ret.costEstimateIndex = costEstimateIndex; + ret.ticksOnCurrent = ticksOnCurrent; + return ret; + }).orElse(cutIfTooLong()); + } + + private PathExecutor cutIfTooLong() { + if (pathPosition > Baritone.settings().maxPathHistoryLength.get()) { + int cutoffAmt = Baritone.settings().pathHistoryCutoffAmount.get(); + CutoffPath newPath = new CutoffPath(path, cutoffAmt, path.length() - 1); + if (!newPath.getDest().equals(path.getDest())) { + throw new IllegalStateException(); + } + logDebug("Discarding earliest segment movements, length cut from " + path.length() + " to " + newPath.length()); + PathExecutor ret = new PathExecutor(newPath); + ret.pathPosition = pathPosition - cutoffAmt; + ret.currentMovementOriginalCostEstimate = currentMovementOriginalCostEstimate; + if (costEstimateIndex != null) { + ret.costEstimateIndex = costEstimateIndex - cutoffAmt; + } + ret.ticksOnCurrent = ticksOnCurrent; + return ret; + } + return this; + } + @Override public IPath getPath() { return path; diff --git a/src/main/java/baritone/pathing/path/SplicedPath.java b/src/main/java/baritone/pathing/path/SplicedPath.java new file mode 100644 index 00000000..33b0b97f --- /dev/null +++ b/src/main/java/baritone/pathing/path/SplicedPath.java @@ -0,0 +1,88 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.pathing.path; + +import baritone.api.pathing.calc.IPath; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.movement.IMovement; +import baritone.api.utils.BetterBlockPos; + +import java.util.*; + +public class SplicedPath implements IPath { + private final List path; + + private final List movements; + + private final int numNodes; + + private final Goal goal; + + private SplicedPath(List path, List movements, int numNodesConsidered, Goal goal) { + this.path = path; + this.movements = movements; + this.numNodes = numNodesConsidered; + this.goal = goal; + sanityCheck(); + } + + @Override + public Goal getGoal() { + return goal; + } + + @Override + public List movements() { + return Collections.unmodifiableList(movements); + } + + @Override + public List positions() { + return Collections.unmodifiableList(path); + } + + @Override + public int getNumNodesConsidered() { + return numNodes; + } + + public static Optional trySplice(IPath first, IPath second) { + if (second == null || first == null) { + return Optional.empty(); + } + if (!Objects.equals(first.getGoal(), second.getGoal())) { + return Optional.empty(); + } + if (!first.getDest().equals(second.getSrc())) { + return Optional.empty(); + } + HashSet a = new HashSet<>(first.positions()); + for (int i = 1; i < second.length(); i++) { + if (a.contains(second.positions().get(i))) { + return Optional.empty(); + } + } + List positions = new ArrayList<>(); + List movements = new ArrayList<>(); + positions.addAll(first.positions()); + positions.addAll(second.positions().subList(1, second.length())); + movements.addAll(first.movements()); + movements.addAll(second.movements()); + return Optional.of(new SplicedPath(positions, movements, first.getNumNodesConsidered() + second.getNumNodesConsidered(), first.getGoal())); + } +}