diff --git a/src/api/java/baritone/api/process/IFollowProcess.java b/src/api/java/baritone/api/process/IFollowProcess.java index cb9ecde6..ef869da4 100644 --- a/src/api/java/baritone/api/process/IFollowProcess.java +++ b/src/api/java/baritone/api/process/IFollowProcess.java @@ -19,6 +19,9 @@ package baritone.api.process; import net.minecraft.entity.Entity; +import java.util.List; +import java.util.function.Predicate; + /** * @author Brady * @since 9/23/2018 @@ -26,16 +29,18 @@ import net.minecraft.entity.Entity; public interface IFollowProcess extends IBaritoneProcess { /** - * Set the follow target to the specified entity; + * Set the follow target to any entities matching this predicate * - * @param entity The entity to follow + * @param filter the predicate */ - void follow(Entity entity); + void follow(Predicate filter); /** - * @return The entity that is currently being followed + * @return The entities that are currently being followed. null if not currently following, empty if nothing matches the predicate */ - Entity following(); + List following(); + + Predicate currentFilter(); /** * Cancels the follow behavior, this will clear the current follow target. diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index 51550995..c9c95c87 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -55,6 +55,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, private boolean safeToCancel; private boolean pauseRequestedLastTick; + private boolean cancelRequested; private boolean calcFailedLastTick; private volatile boolean isPathCalcInProgress; @@ -91,18 +92,22 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, baritone.getPathingControlManager().cancelEverything(); return; } + baritone.getPathingControlManager().preTick(); tickPath(); dispatchEvents(); } private void tickPath() { - baritone.getPathingControlManager().doTheThingWithTheStuff(); if (pauseRequestedLastTick && safeToCancel) { pauseRequestedLastTick = false; baritone.getInputOverrideHandler().clearAllKeys(); BlockBreakHelper.stopBreakingBlock(); return; } + if (cancelRequested) { + cancelRequested = false; + baritone.getInputOverrideHandler().clearAllKeys(); + } if (current == null) { return; } @@ -273,6 +278,17 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, return calcFailedLastTick; } + public void softCancelIfSafe() { + if (!isSafeToCancel()) { + return; + } + current = null; + next = null; + cancelRequested = true; + AbstractNodeCostSearch.getCurrentlyRunning().ifPresent(AbstractNodeCostSearch::cancel); + // do everything BUT clear keys + } + // just cancel the current path public void secretInternalSegmentCancel() { queuePathEvent(PathEvent.CANCELED); diff --git a/src/main/java/baritone/process/FollowProcess.java b/src/main/java/baritone/process/FollowProcess.java index f662fa1d..41a59808 100644 --- a/src/main/java/baritone/process/FollowProcess.java +++ b/src/main/java/baritone/process/FollowProcess.java @@ -18,6 +18,8 @@ package baritone.process; import baritone.Baritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalComposite; import baritone.api.pathing.goals.GoalNear; import baritone.api.pathing.goals.GoalXZ; import baritone.api.process.IFollowProcess; @@ -27,6 +29,12 @@ import baritone.utils.BaritoneProcessHelper; import net.minecraft.entity.Entity; import net.minecraft.util.math.BlockPos; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * Follow an entity * @@ -34,7 +42,8 @@ import net.minecraft.util.math.BlockPos; */ public final class FollowProcess extends BaritoneProcessHelper implements IFollowProcess { - private Entity following; + private Predicate filter; + private List cache; public FollowProcess(Baritone baritone) { super(baritone, 1); @@ -42,39 +51,76 @@ public final class FollowProcess extends BaritoneProcessHelper implements IFollo @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + scanWorld(); + Goal goal = new GoalComposite(cache.stream().map(this::towards).toArray(Goal[]::new)); + return new PathingCommand(goal, PathingCommandType.REVALIDATE_GOAL_AND_PATH); + } + + private Goal towards(Entity following) { // lol this is trashy but it works BlockPos pos; if (Baritone.settings().followOffsetDistance.get() == 0) { - pos = following.getPosition(); + pos = new BlockPos(following); } else { GoalXZ g = GoalXZ.fromDirection(following.getPositionVector(), Baritone.settings().followOffsetDirection.get(), Baritone.settings().followOffsetDistance.get()); pos = new BlockPos(g.getX(), following.posY, g.getZ()); } - return new PathingCommand(new GoalNear(pos, Baritone.settings().followRadius.get()), PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH); + return new GoalNear(pos, Baritone.settings().followRadius.get()); + } + + + private boolean followable(Entity entity) { + if (entity == null) { + return false; + } + if (entity.isDead) { + return false; + } + if (entity.equals(player())) { + return false; + } + if (!world().loadedEntityList.contains(entity) && !world().playerEntities.contains(entity)) { + return false; + } + return true; + } + + private void scanWorld() { + cache = Stream.of(world().loadedEntityList, world().playerEntities).flatMap(List::stream).filter(this::followable).filter(this.filter).distinct().collect(Collectors.toCollection(ArrayList::new)); } @Override public boolean isActive() { - return following != null; + if (filter == null) { + return false; + } + scanWorld(); + return !cache.isEmpty(); } @Override public void onLostControl() { - following = null; + filter = null; + cache = null; } @Override public String displayName() { - return "Follow " + following; + return "Follow " + cache; } @Override - public void follow(Entity entity) { - this.following = entity; + public void follow(Predicate filter) { + this.filter = filter; } @Override - public Entity following() { - return this.following; + public List following() { + return cache; + } + + @Override + public Predicate currentFilter() { + return filter; } } diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index 1db47fc8..0c39f2fb 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -241,6 +241,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } public void addNearby() { + knownOreLocations.addAll(droppedItemsScan(mining, world())); BlockPos playerFeet = playerFeet(); int searchDist = 4;//why four? idk for (int x = playerFeet.getX() - searchDist; x <= playerFeet.getX() + searchDist; x++) { @@ -260,6 +261,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro List dropped = droppedItemsScan(mining, world); List locs = locs2 .stream() + .distinct() // remove any that are within loaded chunks that aren't actually what we want .filter(pos -> world.getChunk(pos) instanceof EmptyChunk || mining.contains(BlockStateInterface.get(pos).getBlock()) || dropped.contains(pos)) diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index e908d1c4..f4defbfd 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -262,6 +262,11 @@ public class ExampleBaritoneControl extends Behavior implements Helper { }); return true; } + if (msg.startsWith("followplayers")) { + baritone.getFollowProcess().follow(EntityPlayer.class::isInstance); // O P P A + logDirect("Following any players"); + return true; + } if (msg.startsWith("follow")) { String name = msg.substring(6).trim(); Optional toFollow = Optional.empty(); @@ -279,7 +284,8 @@ public class ExampleBaritoneControl extends Behavior implements Helper { logDirect("Not found"); return true; } - baritone.getFollowProcess().follow(toFollow.get()); + Entity effectivelyFinal = toFollow.get(); + baritone.getFollowProcess().follow(x -> effectivelyFinal.equals(x)); logDirect("Following " + toFollow.get()); return true; } diff --git a/src/main/java/baritone/utils/PathingControlManager.java b/src/main/java/baritone/utils/PathingControlManager.java index 5d1b0303..9ea193a1 100644 --- a/src/main/java/baritone/utils/PathingControlManager.java +++ b/src/main/java/baritone/utils/PathingControlManager.java @@ -18,10 +18,13 @@ package baritone.utils; import baritone.Baritone; +import baritone.api.event.events.TickEvent; +import baritone.api.event.listener.AbstractGameEventListener; import baritone.api.pathing.goals.Goal; import baritone.api.process.IBaritoneProcess; import baritone.api.process.PathingCommand; import baritone.behavior.PathingBehavior; +import baritone.pathing.calc.AbstractNodeCostSearch; import baritone.pathing.path.PathExecutor; import net.minecraft.util.math.BlockPos; @@ -36,10 +39,20 @@ public class PathingControlManager { private final HashSet processes; // unGh private IBaritoneProcess inControlLastTick; private IBaritoneProcess inControlThisTick; + private PathingCommand command; public PathingControlManager(Baritone baritone) { this.baritone = baritone; this.processes = new HashSet<>(); + baritone.registerEventListener(new AbstractGameEventListener() { // needs to be after all behavior ticks + @Override + public void onTick(TickEvent event) { + if (event.getType() == TickEvent.Type.OUT) { + return; + } + postTick(); + } + }); } public void registerProcess(IBaritoneProcess process) { @@ -61,38 +74,35 @@ public class PathingControlManager { return inControlThisTick; } - public void doTheThingWithTheStuff() { + public void preTick() { inControlLastTick = inControlThisTick; - PathingCommand cmd = doTheStuff(); - if (cmd == null) { + command = doTheStuff(); + if (command == null) { return; } PathingBehavior p = baritone.getPathingBehavior(); - switch (cmd.commandType) { + switch (command.commandType) { case REQUEST_PAUSE: p.requestPause(); break; case CANCEL_AND_SET_GOAL: - p.secretInternalSetGoal(cmd.goal); + p.secretInternalSetGoal(command.goal); p.cancelSegmentIfSafe(); break; case FORCE_REVALIDATE_GOAL_AND_PATH: - p.secretInternalSetGoalAndPath(cmd.goal); - if (cmd.goal == null || forceRevalidate(cmd.goal) || revalidateGoal(cmd.goal)) { - // pwnage - p.cancelSegmentIfSafe(); + if (!p.isPathing() && !AbstractNodeCostSearch.getCurrentlyRunning().isPresent()) { + p.secretInternalSetGoalAndPath(command.goal); } break; case REVALIDATE_GOAL_AND_PATH: - p.secretInternalSetGoalAndPath(cmd.goal); - if (Baritone.settings().cancelOnGoalInvalidation.get() && (cmd.goal == null || revalidateGoal(cmd.goal))) { - p.cancelSegmentIfSafe(); + if (!p.isPathing() && !AbstractNodeCostSearch.getCurrentlyRunning().isPresent()) { + p.secretInternalSetGoalAndPath(command.goal); } break; case SET_GOAL_AND_PATH: // now this i can do - if (cmd.goal != null) { - baritone.getPathingBehavior().secretInternalSetGoalAndPath(cmd.goal); + if (command.goal != null) { + baritone.getPathingBehavior().secretInternalSetGoalAndPath(command.goal); } break; default: @@ -100,6 +110,33 @@ public class PathingControlManager { } } + public void postTick() { + // if we did this in pretick, it would suck + // we use the time between ticks as calculation time + // therefore, we only cancel and recalculate after the tick for the current path has executed + // "it would suck" means it would actually execute a path every other tick + if (command == null) { + return; + } + PathingBehavior p = baritone.getPathingBehavior(); + switch (command.commandType) { + case FORCE_REVALIDATE_GOAL_AND_PATH: + if (command.goal == null || forceRevalidate(command.goal) || revalidateGoal(command.goal)) { + // pwnage + p.softCancelIfSafe(); + } + p.secretInternalSetGoalAndPath(command.goal); + break; + case REVALIDATE_GOAL_AND_PATH: + if (Baritone.settings().cancelOnGoalInvalidation.get() && (command.goal == null || revalidateGoal(command.goal))) { + p.softCancelIfSafe(); + } + p.secretInternalSetGoalAndPath(command.goal); + break; + default: + } + } + public boolean forceRevalidate(Goal newGoal) { PathExecutor current = baritone.getPathingBehavior().getCurrent(); if (current != null) {