diff --git a/src/api/java/baritone/api/utils/PathCalculationResult.java b/src/api/java/baritone/api/utils/PathCalculationResult.java index df009952..20aef9de 100644 --- a/src/api/java/baritone/api/utils/PathCalculationResult.java +++ b/src/api/java/baritone/api/utils/PathCalculationResult.java @@ -33,6 +33,9 @@ public class PathCalculationResult { public PathCalculationResult(Type type, IPath path) { this.path = path; this.type = type; + if (type == null) { + throw new IllegalArgumentException("come on"); + } } public final Optional getPath() { diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index f2017eef..42186b5b 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -40,10 +40,7 @@ import baritone.utils.PathRenderer; import net.minecraft.util.math.BlockPos; import net.minecraft.world.chunk.EmptyChunk; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Optional; +import java.util.*; import java.util.concurrent.LinkedBlockingQueue; import java.util.stream.Collectors; @@ -410,6 +407,9 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } 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); + if (!Objects.equals(pathfinder.getGoal(), goal)) { + logDebug("Simplifying " + goal.getClass() + " to GoalXZ due to distance"); + } inProgress = pathfinder; Baritone.getExecutor().execute(() -> { if (talkAboutIt) { @@ -480,12 +480,11 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, }); } - private AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context) { + public static AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context) { Goal transformed = goal; if (Baritone.settings().simplifyUnloadedYCoord.get() && goal instanceof IGoalRenderPos) { BlockPos pos = ((IGoalRenderPos) goal).getGoalPos(); if (context.world().getChunk(pos) instanceof EmptyChunk) { - logDebug("Simplifying " + goal.getClass() + " to GoalXZ due to distance"); transformed = new GoalXZ(pos.getX(), pos.getZ()); } } diff --git a/src/main/java/baritone/utils/pathing/SegmentedCalculator.java b/src/main/java/baritone/utils/pathing/SegmentedCalculator.java new file mode 100644 index 00000000..a0b8e27c --- /dev/null +++ b/src/main/java/baritone/utils/pathing/SegmentedCalculator.java @@ -0,0 +1,87 @@ +/* + * 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.utils.pathing; + +import baritone.Baritone; +import baritone.api.pathing.calc.IPath; +import baritone.api.pathing.goals.Goal; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.PathCalculationResult; +import baritone.behavior.PathingBehavior; +import baritone.pathing.calc.AbstractNodeCostSearch; +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.path.SplicedPath; + +import java.util.Optional; +import java.util.function.Consumer; + +/** + * Calculate and splice many path segments to reach a goal + * + * @author leijurv + */ +public class SegmentedCalculator { + private final BetterBlockPos start; + private final Goal goal; + private final CalculationContext context; + + private SegmentedCalculator(BetterBlockPos start, Goal goal, CalculationContext context) { + this.start = start; + this.goal = goal; + this.context = context; + } + + private Optional doCalc() { + Optional soFar = Optional.empty(); + while (true) { + PathCalculationResult result = segment(soFar); + switch (result.getType()) { + case SUCCESS_SEGMENT: + 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; + default: // CANCELLATION and null should not be possible, nothing else has access to this, so it can't have been canceled + throw new IllegalStateException(); + } + 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); + soFar = Optional.of(combined); + } + } + + 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); + 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 + } + + public static void calculateSegmentsThreaded(BetterBlockPos start, Goal goal, CalculationContext context, Consumer> onCompletion) { + Baritone.getExecutor().execute(() -> { + Optional result; + try { + result = new SegmentedCalculator(start, goal, context).doCalc(); + } catch (Exception ex) { + ex.printStackTrace(); + result = Optional.empty(); + } + onCompletion.accept(result); + }); + } +}