diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index 42186b5b..3e8c3595 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -330,12 +330,16 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } } + public void secretCursedFunctionDoNotCall(IPath path) { + current = new PathExecutor(this, path); + } + /** * See issue #209 * * @return The starting {@link BlockPos} for a new path */ - public BlockPos pathStart() { // TODO move to a helper or util class + public BetterBlockPos pathStart() { // TODO move to a helper or util class BetterBlockPos feet = ctx.playerFeet(); if (!MovementHelper.canWalkOn(ctx, feet.down())) { if (ctx.player().onGround) { @@ -406,7 +410,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, failureTimeout = Baritone.settings().planAheadFailureTimeoutMS.get(); } CalculationContext context = new CalculationContext(baritone); // not safe to create on the other thread, it looks up a lot of stuff in minecraft - AbstractNodeCostSearch pathfinder = createPathfinder(start, goal, current == null ? null : current.getPath(), context); + AbstractNodeCostSearch pathfinder = createPathfinder(start, goal, current == null ? null : current.getPath(), context, true); if (!Objects.equals(pathfinder.getGoal(), goal)) { logDebug("Simplifying " + goal.getClass() + " to GoalXZ due to distance"); } @@ -480,9 +484,9 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, }); } - public static AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context) { + public static AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context, boolean allowSimplifyUnloaded) { Goal transformed = goal; - if (Baritone.settings().simplifyUnloadedYCoord.get() && goal instanceof IGoalRenderPos) { + if (Baritone.settings().simplifyUnloadedYCoord.get() && goal instanceof IGoalRenderPos && allowSimplifyUnloaded) { BlockPos pos = ((IGoalRenderPos) goal).getGoalPos(); if (context.world().getChunk(pos) instanceof EmptyChunk) { transformed = new GoalXZ(pos.getX(), pos.getZ()); diff --git a/src/main/java/baritone/cache/CachedWorld.java b/src/main/java/baritone/cache/CachedWorld.java index 31bcceab..e9caddfe 100644 --- a/src/main/java/baritone/cache/CachedWorld.java +++ b/src/main/java/baritone/cache/CachedWorld.java @@ -255,6 +255,10 @@ public final class CachedWorld implements ICachedWorld, Helper { }); } + public void tryLoadFromDisk(int regionX, int regionZ) { + getOrCreateRegion(regionX, regionZ); + } + /** * Returns the region ID based on the region coordinates. 0 will be * returned if the specified region coordinates are out of bounds. diff --git a/src/main/java/baritone/pathing/path/SplicedPath.java b/src/main/java/baritone/pathing/path/SplicedPath.java index 92610c86..f3263718 100644 --- a/src/main/java/baritone/pathing/path/SplicedPath.java +++ b/src/main/java/baritone/pathing/path/SplicedPath.java @@ -77,6 +77,7 @@ public class SplicedPath extends PathBase { for (int i = 0; i < first.length() - 1; i++) { // overlap in the very last element is fine (and required) so only go up to first.length() - 1 if (secondPos.contains(first.positions().get(i))) { firstPositionInSecond = i; + break; } } if (firstPositionInSecond != -1) { @@ -94,7 +95,7 @@ public class SplicedPath extends PathBase { List movements = new ArrayList<>(); positions.addAll(first.positions().subList(0, firstPositionInSecond + 1)); movements.addAll(first.movements().subList(0, firstPositionInSecond)); - + positions.addAll(second.positions().subList(positionInSecond + 1, second.length())); movements.addAll(second.movements().subList(positionInSecond, second.length() - 1)); return Optional.of(new SplicedPath(positions, movements, first.getNumNodesConsidered() + second.getNumNodesConsidered(), first.getGoal())); diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index 93e2742a..6b2c7448 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -32,6 +32,7 @@ import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.Moves; import baritone.process.CustomGoalProcess; +import baritone.utils.pathing.SegmentedCalculator; import net.minecraft.block.Block; import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.entity.Entity; @@ -202,6 +203,23 @@ public class ExampleBaritoneControl extends Behavior implements Helper { } return true; } + if (msg.equals("fullpath")) { + if (pathingBehavior.getGoal() == null) { + logDirect("No goal."); + } else { + logDirect("Started segmented calculator"); + SegmentedCalculator.calculateSegmentsThreaded(pathingBehavior.pathStart(), pathingBehavior.getGoal(), new CalculationContext(baritone), ipath -> { + logDirect("Found a path"); + logDirect("Ends at " + ipath.getDest()); + logDirect("Length " + ipath.length()); + logDirect("Estimated time " + ipath.ticksRemainingFrom(0)); + pathingBehavior.secretCursedFunctionDoNotCall(ipath); // it's okay when *I* do it + }, () -> { + logDirect("Path calculation failed, no path"); + }); + } + return true; + } if (msg.equals("repack") || msg.equals("rescan")) { ChunkProviderClient cli = (ChunkProviderClient) ctx.world().getChunkProvider(); int playerChunkX = ctx.playerFeet().getX() >> 4; diff --git a/src/main/java/baritone/utils/pathing/SegmentedCalculator.java b/src/main/java/baritone/utils/pathing/SegmentedCalculator.java index 75dd0282..d3f6703e 100644 --- a/src/main/java/baritone/utils/pathing/SegmentedCalculator.java +++ b/src/main/java/baritone/utils/pathing/SegmentedCalculator.java @@ -23,9 +23,11 @@ import baritone.api.pathing.goals.Goal; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.PathCalculationResult; import baritone.behavior.PathingBehavior; +import baritone.cache.CachedWorld; import baritone.pathing.calc.AbstractNodeCostSearch; import baritone.pathing.movement.CalculationContext; import baritone.pathing.path.SplicedPath; +import net.minecraft.util.EnumFacing; import java.util.Optional; import java.util.function.Consumer; @@ -52,8 +54,8 @@ public class SegmentedCalculator { PathCalculationResult result = segment(soFar); switch (result.getType()) { case SUCCESS_SEGMENT: + case SUCCESS_TO_GOAL: break; - case SUCCESS_TO_GOAL: // if we've gotten all the way to the goal, we're done case FAILURE: // if path calculation failed, we're done case EXCEPTION: // if path calculation threw an exception, we're done return soFar; @@ -62,13 +64,30 @@ public class SegmentedCalculator { } IPath segment = result.getPath().get(); // path calculation result type is SUCCESS_SEGMENT, so the path must be present IPath combined = soFar.map(previous -> (IPath) SplicedPath.trySplice(previous, segment, true).get()).orElse(segment); + loadAdjacent(combined.getDest().getX(), combined.getDest().getZ()); soFar = Optional.of(combined); + if (result.getType() == PathCalculationResult.Type.SUCCESS_TO_GOAL) { + return soFar; + } + } + } + + private void loadAdjacent(int blockX, int blockZ) { + BetterBlockPos bp = new BetterBlockPos(blockX, 64, blockZ); + CachedWorld cached = (CachedWorld) context.getBaritone().getPlayerContext().worldData().getCachedWorld(); + for (int i = 0; i < 4; i++) { + // pathing thread is not allowed to load new cached regions from disk + // it checks if every chunk is loaded before getting blocks from it + // so you see path segments ending at multiples of 512 (plus or minus one) on either x or z axis + // this loads every adjacent chunk to the segment end, so it can continue into the next cached region + BetterBlockPos toLoad = bp.offset(EnumFacing.byHorizontalIndex(i), 16); + cached.tryLoadFromDisk(toLoad.x >> 9, toLoad.z >> 9); } } private PathCalculationResult segment(Optional previous) { BetterBlockPos segmentStart = previous.map(IPath::getDest).orElse(start); // <-- e p i c - AbstractNodeCostSearch search = PathingBehavior.createPathfinder(segmentStart, goal, previous.orElse(null), context); + AbstractNodeCostSearch search = PathingBehavior.createPathfinder(segmentStart, goal, previous.orElse(null), context, false); return search.calculate(Baritone.settings().primaryTimeoutMS.get(), Baritone.settings().failureTimeoutMS.get()); // use normal time settings, not the plan ahead settings, so as to not overwhelm the computer }