From 80a8957b11c06b20a344b1128de956d997661bb5 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Thu, 19 Sep 2024 17:32:27 -0500 Subject: [PATCH 01/26] Interpolated Looking implementation Replaced old, in many cases not working "smoothlook" with "interpolatedLook", which forces global player rotation in both axis to follow a "path" from source looking position(rotation) to target, removing jittering when, for instance, mining blocks; it WILL hinder baritone's default mining speed, but the main purpose is to make actions seem slightly more humanly possible, time of the interpolation is controlled by interpolatedLookLength, in ticks. --- src/api/java/baritone/api/Settings.java | 38 +++- .../baritone/api/utils/RotationUtils.java | 71 +++++++ src/api/java/baritone/api/utils/VecUtils.java | 5 + .../java/baritone/behavior/LookBehavior.java | 184 ++++++++++++++---- .../java/baritone/utils/BaritoneMath.java | 5 + 5 files changed, 257 insertions(+), 46 deletions(-) diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 3f45f7ac5..155da14c3 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -774,20 +774,40 @@ public final class Settings { */ public final Setting elytraFreeLook = new Setting<>(true); - /** - * Forces the client-sided yaw rotation to an average of the last {@link #smoothLookTicks} of server-sided rotations. + ///** + // * Forces the client-sided yaw rotation to an average of the last {@link #smoothLookTicks} of server-sided rotations. + // */ + //public final Setting smoothLook = new Setting<>(false); + // + ///** + // * Same as {@link #smoothLook} but for elytra flying. + // */ + //public final Setting elytraSmoothLook = new Setting<>(false); + // + ///** + // * The number of ticks to average across for {@link #smoothLook}; + // */ + //public final Setting smoothLookTicks = new Setting<>(5); + + // Old implementation settings... + + /** + * Forces global player rotation in both axis to follow a "path" from source looking position(rotation) to target, removing jittering when, for instance, mining blocks. + * Uses {@link #interpolatedLookLength} to determine the amount of ticks it takes to complete the path + *

+ * TODO: It might be better to use some kind of angular speed instead of a fixed duration for all angles. + *

+ * it WILL hinder mining speed, but the main goal here is that neither the client nor the server sees the default jittery-ness of baritone */ - public final Setting smoothLook = new Setting<>(false); + public final Setting interpolatedLook = new Setting<>(false); /** - * Same as {@link #smoothLook} but for elytra flying. + * Controls time it takes to complete an {@link #interpolatedLook} "cycle". + *

+ * In ticks. */ - public final Setting elytraSmoothLook = new Setting<>(false); + public final Setting interpolatedLookLength = new Setting<>(10); - /** - * The number of ticks to average across for {@link #smoothLook}; - */ - public final Setting smoothLookTicks = new Setting<>(5); /** * When true, the player will remain with its existing look direction as often as possible. diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index 2c2ee9147..6c95d1712 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -19,6 +19,7 @@ import baritone.api.BaritoneAPI; import baritone.api.IBaritone; +import baritone.api.utils.VecUtils; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -265,6 +266,76 @@ public static Optional reachableCenter(IPlayerContext ctx, BlockPos po return reachableOffset(ctx, pos, VecUtils.calculateBlockCenter(ctx.world(), pos), blockReachDistance, wouldSneak); } + /** + * Arc interpolator, creates a radius with a center as near as possible from "origin" which touches + * both A and B. + *

+ * It's an alternative to normal lerp, where if A is in front of origin and B opposite or + * near opposite to the former, the line would pass straight through origin + * + * @param A Location to return at t = 0 + * @param B Location to return at t = 1 + * @param Origin Location the center should approximate + * @param t Interpolator value + * @return Range of locations forming an arc outwards from Origin, controlled by t + */ + public static Vec3 alerp(Vec3 A, Vec3 B, Vec3 Origin, double t) { + Vec3 midpoint = VecUtils.vDivide(A.add(B), new Vec3(2, 2, 2)); + + Vec3 normal = ((A.subtract(Origin)).cross(B.subtract(Origin))).normalize(); + + Vec3 AB = (B.subtract(A)).normalize(); + + Vec3 toCenter = (AB.cross(normal)).normalize(); + + Vec3 originToMid = midpoint.subtract(Origin); + double projectionLength = originToMid.dot(toCenter); + + Vec3 center = midpoint.subtract(new Vec3(projectionLength, projectionLength, projectionLength).multiply(toCenter)); + + double radius = A.distanceTo(center); + + Vec3 vecToA = A.subtract(center); + Vec3 vecToB = B.subtract(center); + + double angleA = Mth.atan2(vecToA.dot(toCenter), vecToA.dot(AB)); + double angleB = Mth.atan2(vecToB.dot(toCenter), vecToB.dot(AB)); + + // Ensure we're using the shorter arc + if (Mth.abs((float) (angleB - angleA)) > Mth.PI) { + if (angleA < angleB) { + angleA += 2 * Mth.PI; + } else { + angleB += 2 * Mth.PI; + } + } + + // Interpolate the angle + double angle = (1 - t) * angleA + t * angleB; + + // Calculate the interpolated point + Vec3 asdainside = (new Vec3(Mth.cos((float) angle), Mth.cos((float) angle), Mth.cos((float) angle)).multiply(AB)).add(new Vec3(Mth.sin((float) angle), Mth.sin((float) angle), Mth.sin((float) angle)).multiply(toCenter)); + + return center.add((new Vec3(radius, radius, radius)).multiply(asdainside)); + } + /* + Using Slerp is also possible, but might be slightly more prone to doing a neck break, and IDK how to implement it lol + btw it uses quaternions, so doing a translation back and forth might be more expensive than just using the position to rotation function already implemented here. + I might be wrong in a lot of things though, I come from c++, and java is not exactly different, but it's also not exactly the same; I literally have 0 java + expertise, and I'm just rawdogging this without tutorials... + */ + + /* + didn't realize that in lookBehavior values in the Target struct and the current lookpos of the player were already angles with no trace of the original position until + after I wrote this function; at least if anything happens it should be un-pair proof (if somehow the distance between A, B and the origin is different it will still work as intended) + */ + + /* + also funny story, I created a whole library of vector operations in VecUtils and used them for this function until I realized that the Vec3 library already had + most of them, so I undid all that work except for vDivide, which was NOT on Vec3.class, although I do think my implementation did make it all a bit more understandable + but that's very subjective. + */ + @Deprecated public static Optional reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance) { return reachable(entity, pos, blockReachDistance, false); diff --git a/src/api/java/baritone/api/utils/VecUtils.java b/src/api/java/baritone/api/utils/VecUtils.java index 7037afcfc..7c58f3224 100644 --- a/src/api/java/baritone/api/utils/VecUtils.java +++ b/src/api/java/baritone/api/utils/VecUtils.java @@ -19,6 +19,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseFireBlock; @@ -124,4 +125,8 @@ public static double entityDistanceToCenter(Entity entity, BlockPos pos) { public static double entityFlatDistanceToCenter(Entity entity, BlockPos pos) { return distanceToCenter(pos, entity.position().x, pos.getY() + 0.5, entity.position().z); } + + public static Vec3 vDivide (Vec3 A, Vec3 B) { + return new Vec3(A.x / B.x, A.y / B.y, A.z / B.z); + } } diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 98e12aeff..4be2c25ef 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -25,7 +25,11 @@ import baritone.api.event.events.*; import baritone.api.utils.IPlayerContext; import baritone.api.utils.Rotation; +import baritone.api.utils.RotationUtils; +import baritone.api.utils.VecUtils; import baritone.behavior.look.ForkableRandom; +import baritone.utils.BaritoneMath; +import net.minecraft.world.phys.Vec3; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import java.util.ArrayDeque; @@ -53,8 +57,16 @@ public final class LookBehavior extends Behavior implements ILookBehavior { private final AimProcessor processor; - private final Deque smoothYawBuffer; - private final Deque smoothPitchBuffer; + /** + * the interpolation's current stage, to be used by {@link #onPlayerUpdate(PlayerUpdateEvent)} whenever {@link Settings#interpolatedLook} is true. + */ + private static int stage = 0; + private static Vec3 Source; + private static Vec3 Destiny; + private static Vec3 Center; + + //private final Deque smoothYawBuffer; + //private final Deque smoothPitchBuffer; public LookBehavior(Baritone baritone) { super(baritone); @@ -87,49 +99,146 @@ public void onPlayerUpdate(PlayerUpdateEvent event) { return; } - switch (event.getState()) { - case PRE: { - if (this.target.mode == Target.Mode.NONE) { - // Just return for PRE, we still want to set target to null on POST - return; - } + if (Baritone.settings().interpolatedLook.value = false) { + switch (event.getState()) { + //PRE: onPlayerUpdate was called before rotation data was sent to the server + case PRE: { + if (this.target.mode == Target.Mode.NONE) { + // Just return for PRE, we still want to set target to null on POST + return; + } - this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); - final Rotation actual = this.processor.peekRotation(this.target.rotation); - ctx.player().setYRot(actual.getYaw()); - ctx.player().setXRot(actual.getPitch()); - break; + this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); + final Rotation actual = this.processor.peekRotation(this.target.rotation); + ctx.player().setYRot(actual.getYaw()); + ctx.player().setXRot(actual.getPitch()); + break; + } + //POST: onPlayerUpdate was called after rotation data was sent to the server + case POST: { + // Reset the player's rotations back to their original values + if (this.prevRotation != null) { + if (this.target.mode == Target.Mode.SERVER) { + ctx.player().setYRot(this.prevRotation.getYaw()); + ctx.player().setXRot(this.prevRotation.getPitch()); + }// else if (ctx.player().isFallFlying() && Baritone.settings().elytraSmoothLook.value) { + // ctx.player().setYRot((float) this.smoothYawBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getYaw())); + // if (ctx.player().isFallFlying()) { + // ctx.player().setXRot((float) this.smoothPitchBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getPitch())); + // } + //} + /** TODO: reimplement elytra and falling */ + //ctx.player().xRotO = prevRotation.getPitch(); + //ctx.player().yRotO = prevRotation.getYaw(); + this.prevRotation = null; + } + // The target is done being used for this game tick, so it can be invalidated + this.target = null; + break; + } + default: + break; } - case POST: { - // Reset the player's rotations back to their original values - if (this.prevRotation != null) { - this.smoothYawBuffer.addLast(this.target.rotation.getYaw()); - while (this.smoothYawBuffer.size() > Baritone.settings().smoothLookTicks.value) { - this.smoothYawBuffer.removeFirst(); + }else { + // interpolatedLook == true + switch (event.getState()) { + case PRE: { + if(this.target.mode == Target.Mode.NONE) { + // same security case as above + return; } - this.smoothPitchBuffer.addLast(this.target.rotation.getPitch()); - while (this.smoothPitchBuffer.size() > Baritone.settings().smoothLookTicks.value) { - this.smoothPitchBuffer.removeFirst(); + + this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); + final Rotation actual = this.processor.peekRotation(this.target.rotation); + + if (LookBehavior.stage == 0) { + LookBehavior.Center = ctx.playerHead(); + LookBehavior.Source = (RotationUtils.calcLookDirectionFromRotation(this.prevRotation)).add(LookBehavior.Center); + LookBehavior.Destiny = (RotationUtils.calcLookDirectionFromRotation(actual)).add(LookBehavior.Center); + + RotationUtils.alerp(LookBehavior.Source, LookBehavior.Destiny, LookBehavior.Center, BaritoneMath.normalize(LookBehavior.stage, Baritone.settings().interpolatedLookLength.value)); //debug, CO&PA + LookBehavior.stage ++; + /* + At this point, the camera rotation should still be the same as before, be it that Baritone was traveling or just exited from another mining phase. + */ + break; + + } else if (0 < LookBehavior.stage && LookBehavior.stage < Baritone.settings().interpolatedLookLength.value) { + final Rotation InterpD = RotationUtils.calcRotationFromVec3d(LookBehavior.Center, RotationUtils.alerp(LookBehavior.Source, LookBehavior.Destiny, LookBehavior.Center, BaritoneMath.normalize(LookBehavior.stage, Baritone.settings().interpolatedLookLength.value)), new Rotation(0, 0)); // love:slash:hate relationship with relative vectors + ctx.player().setYRot(InterpD.getYaw()); + ctx.player().setXRot(InterpD.getPitch()); + LookBehavior.stage ++; + /* + here we enter the main "loop", based on the look stage the player should be looking at any point between the path projected by alerp; at the moment of writing this I'm just assuming that Baritone does a check + to see if the player is looking at the block before starting to do the mining action, and is not some timer-hard-coded value. + */ + break; + + } else if (LookBehavior.stage == Baritone.settings().interpolatedLookLength.value) { + final Rotation InterpD = RotationUtils.calcRotationFromVec3d(LookBehavior.Center, RotationUtils.alerp(LookBehavior.Source, LookBehavior.Destiny, LookBehavior.Center, BaritoneMath.normalize(LookBehavior.stage, Baritone.settings().interpolatedLookLength.value)), new Rotation(0, 0)); // love:slash:hate relationship with relative vectors + ctx.player().setYRot(InterpD.getYaw()); + ctx.player().setXRot(InterpD.getPitch()); + LookBehavior.stage = 0; + LookBehavior.Source = null; + LookBehavior.Destiny = null; + /* + we've reached the final part of the "loop", after setting rotations we do some cleanup to be able to enter the next mining phase. + Center in theory shouldn't need to be nullified, but let's see... + */ + break; + + } else { + // wtf + LookBehavior.stage = 0; + LookBehavior.Source = null; + LookBehavior.Destiny = null; + // I hate code once debug everywhere + break; + // I really don't know how to debug this edge case } - if (this.target.mode == Target.Mode.SERVER) { - ctx.player().setYRot(this.prevRotation.getYaw()); - ctx.player().setXRot(this.prevRotation.getPitch()); - } else if (ctx.player().isFallFlying() ? Baritone.settings().elytraSmoothLook.value : Baritone.settings().smoothLook.value) { - ctx.player().setYRot((float) this.smoothYawBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getYaw())); - if (ctx.player().isFallFlying()) { - ctx.player().setXRot((float) this.smoothPitchBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getPitch())); + } + case POST: { + // Same as before, I really do wish that both PRE and POST are both done before frame generation, but that would be just not to trigger epilepsy + if (this.prevRotation != null) { + if (this.target.mode == Target.Mode.SERVER) { + ctx.player().setYRot(this.prevRotation.getYaw()); + ctx.player().setXRot(this.prevRotation.getPitch()); + }// else if (ctx.player().isFallFlying() && Baritone.settings().elytraSmoothLook.value) { + // ctx.player().setYRot((float) this.smoothYawBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getYaw())); + // if (ctx.player().isFallFlying()) { + // ctx.player().setXRot((float) this.smoothPitchBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getPitch())); + // } + //} + /** TODO: reimplement elytra and falling, again, for interpolatedLook */ + //ctx.player().xRotO = prevRotation.getPitch(); + //ctx.player().yRotO = prevRotation.getYaw(); + if (LookBehavior.stage == Baritone.settings().interpolatedLookLength.value) { + this.prevRotation = null; } } - //ctx.player().xRotO = prevRotation.getPitch(); - //ctx.player().yRotO = prevRotation.getYaw(); - this.prevRotation = null; + // The target is done being used for this game tick, so it can be invalidated + this.target = null; + break; } - // The target is done being used for this game tick, so it can be invalidated - this.target = null; - break; + default: + break; } - default: - break; + /* + in paper(not the server software) this should work, but imma add a failed attempts counter... + */ + + //ATT1: at 10:40PM, 9/18/24 = Compile successful, executing... Stuck looking at 135.1, 0,1 + + /* + I think I had this problem before when testing alerp in a grapher, sometimes the compilers(ig?) decide that a couple of formulas are wrong because f me, in some others it does work perfectly. + I will re-explore all the solutions per grapher. + */ + /* + After an embarrassingly long amount of time, I just realized I converted the angle to a vector before doing an alerp, and I need to add that vector to the player head position, otherwise the two points are near (0,0,0). + Realized this while debugging, and at the same time I saw that the Static modifier is too powerful(not), it is kept on memory after exiting one server and entering another (including embedded, local or internal server). + */ + + //ATT2: at 3:05PM, 9/19/24 = Compile successful, executing... works. } } @@ -149,6 +258,7 @@ public void onSendPacket(PacketEvent event) { public void onWorldEvent(WorldEvent event) { this.serverRotation = null; this.target = null; + LookBehavior.stage = 0; } public void pig() { diff --git a/src/main/java/baritone/utils/BaritoneMath.java b/src/main/java/baritone/utils/BaritoneMath.java index be546f248..b67b52437 100644 --- a/src/main/java/baritone/utils/BaritoneMath.java +++ b/src/main/java/baritone/utils/BaritoneMath.java @@ -34,4 +34,9 @@ public static int fastFloor(final double v) { public static int fastCeil(final double v) { return FLOOR_DOUBLE_I - (int) (FLOOR_DOUBLE_D - v); } + + public static double normalize(double value, double maxValue) { + if (maxValue == 0) return 0; + return value/maxValue; + } } From 663b3bfdef26c6aa99cb52966bdf5e7f7523fe57 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 00:05:30 -0500 Subject: [PATCH 02/26] Preserve default look behavior --- src/api/java/baritone/api/Settings.java | 30 +++++++++---------- .../java/baritone/behavior/LookBehavior.java | 27 ++++++++++------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 155da14c3..b56e3b532 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -774,22 +774,20 @@ public final class Settings { */ public final Setting elytraFreeLook = new Setting<>(true); - ///** - // * Forces the client-sided yaw rotation to an average of the last {@link #smoothLookTicks} of server-sided rotations. - // */ - //public final Setting smoothLook = new Setting<>(false); - // - ///** - // * Same as {@link #smoothLook} but for elytra flying. - // */ - //public final Setting elytraSmoothLook = new Setting<>(false); - // - ///** - // * The number of ticks to average across for {@link #smoothLook}; - // */ - //public final Setting smoothLookTicks = new Setting<>(5); - - // Old implementation settings... + /** + * Forces the client-sided yaw rotation to an average of the last {@link #smoothLookTicks} of server-sided rotations. + */ + public final Setting smoothLook = new Setting<>(false); + + /** + * Same as {@link #smoothLook} but for elytra flying. + */ + public final Setting elytraSmoothLook = new Setting<>(false); + + /** + * The number of ticks to average across for {@link #smoothLook}; + */ + public final Setting smoothLookTicks = new Setting<>(5); /** * Forces global player rotation in both axis to follow a "path" from source looking position(rotation) to target, removing jittering when, for instance, mining blocks. diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 4be2c25ef..9d2a03c69 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -65,8 +65,8 @@ public final class LookBehavior extends Behavior implements ILookBehavior { private static Vec3 Destiny; private static Vec3 Center; - //private final Deque smoothYawBuffer; - //private final Deque smoothPitchBuffer; + private final Deque smoothYawBuffer; + private final Deque smoothPitchBuffer; public LookBehavior(Baritone baritone) { super(baritone); @@ -99,7 +99,7 @@ public void onPlayerUpdate(PlayerUpdateEvent event) { return; } - if (Baritone.settings().interpolatedLook.value = false) { + if (!Baritone.settings().interpolatedLook.value) { switch (event.getState()) { //PRE: onPlayerUpdate was called before rotation data was sent to the server case PRE: { @@ -118,16 +118,23 @@ public void onPlayerUpdate(PlayerUpdateEvent event) { case POST: { // Reset the player's rotations back to their original values if (this.prevRotation != null) { + this.smoothYawBuffer.addLast(this.target.rotation.getYaw()); + while (this.smoothYawBuffer.size() > Baritone.settings().smoothLookTicks.value) { + this.smoothYawBuffer.removeFirst(); + } + this.smoothPitchBuffer.addLast(this.target.rotation.getPitch()); + while (this.smoothPitchBuffer.size() > Baritone.settings().smoothLookTicks.value) { + this.smoothPitchBuffer.removeFirst(); + } if (this.target.mode == Target.Mode.SERVER) { ctx.player().setYRot(this.prevRotation.getYaw()); ctx.player().setXRot(this.prevRotation.getPitch()); - }// else if (ctx.player().isFallFlying() && Baritone.settings().elytraSmoothLook.value) { - // ctx.player().setYRot((float) this.smoothYawBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getYaw())); - // if (ctx.player().isFallFlying()) { - // ctx.player().setXRot((float) this.smoothPitchBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getPitch())); - // } - //} - /** TODO: reimplement elytra and falling */ + } else if (ctx.player().isFallFlying() ? Baritone.settings().elytraSmoothLook.value : Baritone.settings().smoothLook.value) { + ctx.player().setYRot((float) this.smoothYawBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getYaw())); + if (ctx.player().isFallFlying()) { + ctx.player().setXRot((float) this.smoothPitchBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getPitch())); + } + } //ctx.player().xRotO = prevRotation.getPitch(); //ctx.player().yRotO = prevRotation.getYaw(); this.prevRotation = null; From 2ff5c93a2a7c5b0f196bef702b80c03f9b49b309 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 00:53:48 -0500 Subject: [PATCH 03/26] Clean up interpolation style --- src/api/java/baritone/api/Settings.java | 15 +- .../baritone/api/utils/RotationUtils.java | 98 ++---- src/api/java/baritone/api/utils/VecUtils.java | 5 - .../java/baritone/behavior/LookBehavior.java | 310 +++++++++--------- .../java/baritone/utils/BaritoneMath.java | 6 +- 5 files changed, 196 insertions(+), 238 deletions(-) diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index b56e3b532..a0c6ce069 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -775,7 +775,8 @@ public final class Settings { public final Setting elytraFreeLook = new Setting<>(true); /** - * Forces the client-sided yaw rotation to an average of the last {@link #smoothLookTicks} of server-sided rotations. + * Forces the client-sided yaw rotation to an average of the last + * {@link #smoothLookTicks} server-sided rotations. */ public final Setting smoothLook = new Setting<>(false); @@ -790,19 +791,15 @@ public final class Settings { public final Setting smoothLookTicks = new Setting<>(5); /** - * Forces global player rotation in both axis to follow a "path" from source looking position(rotation) to target, removing jittering when, for instance, mining blocks. - * Uses {@link #interpolatedLookLength} to determine the amount of ticks it takes to complete the path + * Interpolates player rotation from the current look direction to the target direction. *

- * TODO: It might be better to use some kind of angular speed instead of a fixed duration for all angles. - *

- * it WILL hinder mining speed, but the main goal here is that neither the client nor the server sees the default jittery-ness of baritone + * This uses {@link #interpolatedLookLength} to determine how many ticks one + * interpolation cycle takes. */ public final Setting interpolatedLook = new Setting<>(false); /** - * Controls time it takes to complete an {@link #interpolatedLook} "cycle". - *

- * In ticks. + * Time, in ticks, to complete one {@link #interpolatedLook} cycle. */ public final Setting interpolatedLookLength = new Setting<>(10); diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index 6c95d1712..b110fdc57 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -19,7 +19,7 @@ import baritone.api.BaritoneAPI; import baritone.api.IBaritone; -import baritone.api.utils.VecUtils; +import java.util.Optional; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -33,8 +33,6 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import java.util.Optional; - /** * @author Brady * @since 9/25/2018 @@ -267,74 +265,50 @@ public static Optional reachableCenter(IPlayerContext ctx, BlockPos po } /** - * Arc interpolator, creates a radius with a center as near as possible from "origin" which touches - * both A and B. - *

- * It's an alternative to normal lerp, where if A is in front of origin and B opposite or - * near opposite to the former, the line would pass straight through origin - * - * @param A Location to return at t = 0 - * @param B Location to return at t = 1 - * @param Origin Location the center should approximate - * @param t Interpolator value - * @return Range of locations forming an arc outwards from Origin, controlled by t + * Interpolates along an arc from {@code start} to {@code end} around {@code origin}. */ - public static Vec3 alerp(Vec3 A, Vec3 B, Vec3 Origin, double t) { - Vec3 midpoint = VecUtils.vDivide(A.add(B), new Vec3(2, 2, 2)); - - Vec3 normal = ((A.subtract(Origin)).cross(B.subtract(Origin))).normalize(); - - Vec3 AB = (B.subtract(A)).normalize(); - - Vec3 toCenter = (AB.cross(normal)).normalize(); - - Vec3 originToMid = midpoint.subtract(Origin); + public static Vec3 alerp(Vec3 start, Vec3 end, Vec3 origin, double t) { + Vec3 midpoint = start.add(end).scale(0.5); + Vec3 normal = start.subtract(origin).cross(end.subtract(origin)).normalize(); + Vec3 pathDirection = end.subtract(start).normalize(); + Vec3 toCenter = pathDirection.cross(normal).normalize(); + Vec3 originToMid = midpoint.subtract(origin); double projectionLength = originToMid.dot(toCenter); - - Vec3 center = midpoint.subtract(new Vec3(projectionLength, projectionLength, projectionLength).multiply(toCenter)); - - double radius = A.distanceTo(center); - - Vec3 vecToA = A.subtract(center); - Vec3 vecToB = B.subtract(center); - - double angleA = Mth.atan2(vecToA.dot(toCenter), vecToA.dot(AB)); - double angleB = Mth.atan2(vecToB.dot(toCenter), vecToB.dot(AB)); + Vec3 center = midpoint.subtract( + new Vec3(projectionLength, projectionLength, projectionLength).multiply(toCenter)); + double radius = start.distanceTo(center); + Vec3 centerToStart = start.subtract(center); + Vec3 centerToEnd = end.subtract(center); + double angleStart = Mth.atan2( + centerToStart.dot(toCenter), + centerToStart.dot(pathDirection)); + double angleEnd = Mth.atan2( + centerToEnd.dot(toCenter), + centerToEnd.dot(pathDirection)); // Ensure we're using the shorter arc - if (Mth.abs((float) (angleB - angleA)) > Mth.PI) { - if (angleA < angleB) { - angleA += 2 * Mth.PI; + if (Mth.abs((float) (angleEnd - angleStart)) > Mth.PI) { + if (angleStart < angleEnd) { + angleStart += 2 * Mth.PI; } else { - angleB += 2 * Mth.PI; + angleEnd += 2 * Mth.PI; } } - // Interpolate the angle - double angle = (1 - t) * angleA + t * angleB; - - // Calculate the interpolated point - Vec3 asdainside = (new Vec3(Mth.cos((float) angle), Mth.cos((float) angle), Mth.cos((float) angle)).multiply(AB)).add(new Vec3(Mth.sin((float) angle), Mth.sin((float) angle), Mth.sin((float) angle)).multiply(toCenter)); - - return center.add((new Vec3(radius, radius, radius)).multiply(asdainside)); + double angle = (1 - t) * angleStart + t * angleEnd; + Vec3 arcDirection = new Vec3( + Mth.cos((float) angle), + Mth.cos((float) angle), + Mth.cos((float) angle)) + .multiply(pathDirection) + .add(new Vec3( + Mth.sin((float) angle), + Mth.sin((float) angle), + Mth.sin((float) angle)) + .multiply(toCenter)); + + return center.add(new Vec3(radius, radius, radius).multiply(arcDirection)); } - /* - Using Slerp is also possible, but might be slightly more prone to doing a neck break, and IDK how to implement it lol - btw it uses quaternions, so doing a translation back and forth might be more expensive than just using the position to rotation function already implemented here. - I might be wrong in a lot of things though, I come from c++, and java is not exactly different, but it's also not exactly the same; I literally have 0 java - expertise, and I'm just rawdogging this without tutorials... - */ - - /* - didn't realize that in lookBehavior values in the Target struct and the current lookpos of the player were already angles with no trace of the original position until - after I wrote this function; at least if anything happens it should be un-pair proof (if somehow the distance between A, B and the origin is different it will still work as intended) - */ - - /* - also funny story, I created a whole library of vector operations in VecUtils and used them for this function until I realized that the Vec3 library already had - most of them, so I undid all that work except for vDivide, which was NOT on Vec3.class, although I do think my implementation did make it all a bit more understandable - but that's very subjective. - */ @Deprecated public static Optional reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance) { diff --git a/src/api/java/baritone/api/utils/VecUtils.java b/src/api/java/baritone/api/utils/VecUtils.java index 7c58f3224..7037afcfc 100644 --- a/src/api/java/baritone/api/utils/VecUtils.java +++ b/src/api/java/baritone/api/utils/VecUtils.java @@ -19,7 +19,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseFireBlock; @@ -125,8 +124,4 @@ public static double entityDistanceToCenter(Entity entity, BlockPos pos) { public static double entityFlatDistanceToCenter(Entity entity, BlockPos pos) { return distanceToCenter(pos, entity.position().x, pos.getY() + 0.5, entity.position().z); } - - public static Vec3 vDivide (Vec3 A, Vec3 B) { - return new Vec3(A.x / B.x, A.y / B.y, A.z / B.z); - } } diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 9d2a03c69..25eee641f 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -22,19 +22,21 @@ import baritone.api.behavior.ILookBehavior; import baritone.api.behavior.look.IAimProcessor; import baritone.api.behavior.look.ITickableAimProcessor; -import baritone.api.event.events.*; +import baritone.api.event.events.PacketEvent; +import baritone.api.event.events.PlayerUpdateEvent; +import baritone.api.event.events.RotationMoveEvent; +import baritone.api.event.events.TickEvent; +import baritone.api.event.events.WorldEvent; import baritone.api.utils.IPlayerContext; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; -import baritone.api.utils.VecUtils; import baritone.behavior.look.ForkableRandom; import baritone.utils.BaritoneMath; -import net.minecraft.world.phys.Vec3; -import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; - import java.util.ArrayDeque; import java.util.Deque; import java.util.Optional; +import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; +import net.minecraft.world.phys.Vec3; public final class LookBehavior extends Behavior implements ILookBehavior { @@ -44,7 +46,8 @@ public final class LookBehavior extends Behavior implements ILookBehavior { private Target target; /** - * The rotation known to the server. Returned by {@link #getEffectiveRotation()} for use in {@link IPlayerContext}. + * The rotation known to the server. Returned by {@link #getEffectiveRotation()} for use + * in {@link IPlayerContext}. */ private Rotation serverRotation; @@ -57,16 +60,12 @@ public final class LookBehavior extends Behavior implements ILookBehavior { private final AimProcessor processor; - /** - * the interpolation's current stage, to be used by {@link #onPlayerUpdate(PlayerUpdateEvent)} whenever {@link Settings#interpolatedLook} is true. - */ - private static int stage = 0; - private static Vec3 Source; - private static Vec3 Destiny; - private static Vec3 Center; - private final Deque smoothYawBuffer; private final Deque smoothPitchBuffer; + private int interpolationStage; + private Vec3 interpolationSource; + private Vec3 interpolationTarget; + private Vec3 interpolationCenter; public LookBehavior(Baritone baritone) { super(baritone); @@ -99,154 +98,136 @@ public void onPlayerUpdate(PlayerUpdateEvent event) { return; } - if (!Baritone.settings().interpolatedLook.value) { - switch (event.getState()) { - //PRE: onPlayerUpdate was called before rotation data was sent to the server - case PRE: { - if (this.target.mode == Target.Mode.NONE) { - // Just return for PRE, we still want to set target to null on POST - return; - } + if (Baritone.settings().interpolatedLook.value) { + this.updateInterpolatedTarget(event); + return; + } - this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); - final Rotation actual = this.processor.peekRotation(this.target.rotation); - ctx.player().setYRot(actual.getYaw()); - ctx.player().setXRot(actual.getPitch()); - break; + switch (event.getState()) { + case PRE: { + if (this.target.mode == Target.Mode.NONE) { + // Just return for PRE, we still want to set target to null on POST + return; } - //POST: onPlayerUpdate was called after rotation data was sent to the server - case POST: { - // Reset the player's rotations back to their original values - if (this.prevRotation != null) { - this.smoothYawBuffer.addLast(this.target.rotation.getYaw()); - while (this.smoothYawBuffer.size() > Baritone.settings().smoothLookTicks.value) { - this.smoothYawBuffer.removeFirst(); - } - this.smoothPitchBuffer.addLast(this.target.rotation.getPitch()); - while (this.smoothPitchBuffer.size() > Baritone.settings().smoothLookTicks.value) { - this.smoothPitchBuffer.removeFirst(); - } - if (this.target.mode == Target.Mode.SERVER) { - ctx.player().setYRot(this.prevRotation.getYaw()); - ctx.player().setXRot(this.prevRotation.getPitch()); - } else if (ctx.player().isFallFlying() ? Baritone.settings().elytraSmoothLook.value : Baritone.settings().smoothLook.value) { - ctx.player().setYRot((float) this.smoothYawBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getYaw())); - if (ctx.player().isFallFlying()) { - ctx.player().setXRot((float) this.smoothPitchBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getPitch())); - } + + this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); + final Rotation actual = this.processor.peekRotation(this.target.rotation); + ctx.player().setYRot(actual.getYaw()); + ctx.player().setXRot(actual.getPitch()); + break; + } + case POST: { + if (this.prevRotation != null) { + final int smoothLookTicks = Baritone.settings().smoothLookTicks.value; + this.smoothYawBuffer.addLast(this.target.rotation.getYaw()); + while (this.smoothYawBuffer.size() > smoothLookTicks) { + this.smoothYawBuffer.removeFirst(); + } + this.smoothPitchBuffer.addLast(this.target.rotation.getPitch()); + while (this.smoothPitchBuffer.size() > smoothLookTicks) { + this.smoothPitchBuffer.removeFirst(); + } + if (this.target.mode == Target.Mode.SERVER) { + ctx.player().setYRot(this.prevRotation.getYaw()); + ctx.player().setXRot(this.prevRotation.getPitch()); + } else if (ctx.player().isFallFlying() + ? Baritone.settings().elytraSmoothLook.value + : Baritone.settings().smoothLook.value) { + ctx.player().setYRot((float) this.smoothYawBuffer.stream() + .mapToDouble(d -> d) + .average() + .orElse(this.prevRotation.getYaw())); + if (ctx.player().isFallFlying()) { + ctx.player().setXRot((float) this.smoothPitchBuffer.stream() + .mapToDouble(d -> d) + .average() + .orElse(this.prevRotation.getPitch())); } - //ctx.player().xRotO = prevRotation.getPitch(); - //ctx.player().yRotO = prevRotation.getYaw(); - this.prevRotation = null; } - // The target is done being used for this game tick, so it can be invalidated - this.target = null; - break; + //ctx.player().xRotO = prevRotation.getPitch(); + //ctx.player().yRotO = prevRotation.getYaw(); + this.prevRotation = null; } - default: - break; + // The target is done being used for this game tick, so it can be invalidated + this.target = null; + break; } - }else { - // interpolatedLook == true - switch (event.getState()) { - case PRE: { - if(this.target.mode == Target.Mode.NONE) { - // same security case as above - return; - } + default: + break; + } + } - this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); - final Rotation actual = this.processor.peekRotation(this.target.rotation); - - if (LookBehavior.stage == 0) { - LookBehavior.Center = ctx.playerHead(); - LookBehavior.Source = (RotationUtils.calcLookDirectionFromRotation(this.prevRotation)).add(LookBehavior.Center); - LookBehavior.Destiny = (RotationUtils.calcLookDirectionFromRotation(actual)).add(LookBehavior.Center); - - RotationUtils.alerp(LookBehavior.Source, LookBehavior.Destiny, LookBehavior.Center, BaritoneMath.normalize(LookBehavior.stage, Baritone.settings().interpolatedLookLength.value)); //debug, CO&PA - LookBehavior.stage ++; - /* - At this point, the camera rotation should still be the same as before, be it that Baritone was traveling or just exited from another mining phase. - */ - break; - - } else if (0 < LookBehavior.stage && LookBehavior.stage < Baritone.settings().interpolatedLookLength.value) { - final Rotation InterpD = RotationUtils.calcRotationFromVec3d(LookBehavior.Center, RotationUtils.alerp(LookBehavior.Source, LookBehavior.Destiny, LookBehavior.Center, BaritoneMath.normalize(LookBehavior.stage, Baritone.settings().interpolatedLookLength.value)), new Rotation(0, 0)); // love:slash:hate relationship with relative vectors - ctx.player().setYRot(InterpD.getYaw()); - ctx.player().setXRot(InterpD.getPitch()); - LookBehavior.stage ++; - /* - here we enter the main "loop", based on the look stage the player should be looking at any point between the path projected by alerp; at the moment of writing this I'm just assuming that Baritone does a check - to see if the player is looking at the block before starting to do the mining action, and is not some timer-hard-coded value. - */ - break; - - } else if (LookBehavior.stage == Baritone.settings().interpolatedLookLength.value) { - final Rotation InterpD = RotationUtils.calcRotationFromVec3d(LookBehavior.Center, RotationUtils.alerp(LookBehavior.Source, LookBehavior.Destiny, LookBehavior.Center, BaritoneMath.normalize(LookBehavior.stage, Baritone.settings().interpolatedLookLength.value)), new Rotation(0, 0)); // love:slash:hate relationship with relative vectors - ctx.player().setYRot(InterpD.getYaw()); - ctx.player().setXRot(InterpD.getPitch()); - LookBehavior.stage = 0; - LookBehavior.Source = null; - LookBehavior.Destiny = null; - /* - we've reached the final part of the "loop", after setting rotations we do some cleanup to be able to enter the next mining phase. - Center in theory shouldn't need to be nullified, but let's see... - */ - break; - - } else { - // wtf - LookBehavior.stage = 0; - LookBehavior.Source = null; - LookBehavior.Destiny = null; - // I hate code once debug everywhere - break; - // I really don't know how to debug this edge case - } + private void updateInterpolatedTarget(PlayerUpdateEvent event) { + switch (event.getState()) { + case PRE: { + if (this.target.mode == Target.Mode.NONE) { + return; } - case POST: { - // Same as before, I really do wish that both PRE and POST are both done before frame generation, but that would be just not to trigger epilepsy - if (this.prevRotation != null) { - if (this.target.mode == Target.Mode.SERVER) { - ctx.player().setYRot(this.prevRotation.getYaw()); - ctx.player().setXRot(this.prevRotation.getPitch()); - }// else if (ctx.player().isFallFlying() && Baritone.settings().elytraSmoothLook.value) { - // ctx.player().setYRot((float) this.smoothYawBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getYaw())); - // if (ctx.player().isFallFlying()) { - // ctx.player().setXRot((float) this.smoothPitchBuffer.stream().mapToDouble(d -> d).average().orElse(this.prevRotation.getPitch())); - // } - //} - /** TODO: reimplement elytra and falling, again, for interpolatedLook */ - //ctx.player().xRotO = prevRotation.getPitch(); - //ctx.player().yRotO = prevRotation.getYaw(); - if (LookBehavior.stage == Baritone.settings().interpolatedLookLength.value) { - this.prevRotation = null; - } - } - // The target is done being used for this game tick, so it can be invalidated - this.target = null; - break; + + this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); + final Rotation actual = this.processor.peekRotation(this.target.rotation); + this.updateInterpolation(actual); + break; + } + case POST: { + if (this.prevRotation != null && this.target.mode == Target.Mode.SERVER) { + ctx.player().setYRot(this.prevRotation.getYaw()); + ctx.player().setXRot(this.prevRotation.getPitch()); } - default: - break; + this.prevRotation = null; + this.target = null; + break; } - /* - in paper(not the server software) this should work, but imma add a failed attempts counter... - */ + default: + break; + } + } - //ATT1: at 10:40PM, 9/18/24 = Compile successful, executing... Stuck looking at 135.1, 0,1 + private void updateInterpolation(Rotation actual) { + final int interpolationLength = Baritone.settings().interpolatedLookLength.value; - /* - I think I had this problem before when testing alerp in a grapher, sometimes the compilers(ig?) decide that a couple of formulas are wrong because f me, in some others it does work perfectly. - I will re-explore all the solutions per grapher. - */ - /* - After an embarrassingly long amount of time, I just realized I converted the angle to a vector before doing an alerp, and I need to add that vector to the player head position, otherwise the two points are near (0,0,0). - Realized this while debugging, and at the same time I saw that the Static modifier is too powerful(not), it is kept on memory after exiting one server and entering another (including embedded, local or internal server). - */ + if (this.interpolationStage == 0) { + this.interpolationCenter = ctx.playerHead(); + this.interpolationSource = RotationUtils.calcLookDirectionFromRotation(this.prevRotation) + .add(this.interpolationCenter); + this.interpolationTarget = RotationUtils.calcLookDirectionFromRotation(actual) + .add(this.interpolationCenter); + this.interpolationStage++; + return; + } + + if (this.interpolationStage < interpolationLength) { + this.applyInterpolatedRotation(interpolationLength); + this.interpolationStage++; + return; + } - //ATT2: at 3:05PM, 9/19/24 = Compile successful, executing... works. + if (this.interpolationStage == interpolationLength) { + this.applyInterpolatedRotation(interpolationLength); } + this.resetInterpolation(); + } + + private void applyInterpolatedRotation(int interpolationLength) { + final Vec3 interpolatedLook = RotationUtils.alerp( + this.interpolationSource, + this.interpolationTarget, + this.interpolationCenter, + BaritoneMath.normalize(this.interpolationStage, interpolationLength)); + final Rotation interpolatedRotation = RotationUtils.calcRotationFromVec3d( + this.interpolationCenter, + interpolatedLook, + new Rotation(0, 0)); + + ctx.player().setYRot(interpolatedRotation.getYaw()); + ctx.player().setXRot(interpolatedRotation.getPitch()); + } + + private void resetInterpolation() { + this.interpolationStage = 0; + this.interpolationSource = null; + this.interpolationTarget = null; + this.interpolationCenter = null; } @Override @@ -256,7 +237,8 @@ public void onSendPacket(PacketEvent event) { } final ServerboundMovePlayerPacket packet = (ServerboundMovePlayerPacket) event.getPacket(); - if (packet instanceof ServerboundMovePlayerPacket.Rot || packet instanceof ServerboundMovePlayerPacket.PosRot) { + if (packet instanceof ServerboundMovePlayerPacket.Rot + || packet instanceof ServerboundMovePlayerPacket.PosRot) { this.serverRotation = new Rotation(packet.getYRot(0.0f), packet.getXRot(0.0f)); } } @@ -265,7 +247,7 @@ public void onSendPacket(PacketEvent event) { public void onWorldEvent(WorldEvent event) { this.serverRotation = null; this.target = null; - LookBehavior.stage = 0; + this.resetInterpolation(); } public void pig() { @@ -331,8 +313,8 @@ public final Rotation peekRotation(final Rotation rotation) { float desiredYaw = rotation.getYaw(); float desiredPitch = rotation.getPitch(); - // In other words, the target doesn't care about the pitch, so it used playerRotations().getPitch() - // and it's safe to adjust it to a normal level + // In other words, the target doesn't care about the pitch, so it used + // playerRotations().getPitch() and it's safe to adjust it to a normal level if (desiredPitch == prev.getPitch()) { desiredPitch = nudgeToLevel(desiredPitch); } @@ -349,8 +331,10 @@ public final Rotation peekRotation(final Rotation rotation) { @Override public final void tick() { // randomLooking - this.randomYawOffset = (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value; - this.randomPitchOffset = (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value; + this.randomYawOffset = + (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value; + this.randomPitchOffset = + (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value; // randomLooking113 double random = this.rand.nextDouble() - 0.5; @@ -395,7 +379,9 @@ protected Rotation getPrevRotation() { protected abstract Rotation getPrevRotation(); /** - * Nudges the player's pitch to a regular level. (Between {@code -20} and {@code 10}, increments are by {@code 1}) + * Nudges the player's pitch to a regular level. + * + *

Between {@code -20} and {@code 10}, increments are by {@code 1}. */ private float nudgeToLevel(float pitch) { if (pitch < -20) { @@ -419,8 +405,10 @@ private double angleToMouse(float angleDelta) { private float mouseToAngle(double mouseDelta) { // casting float literals to double gets us the precise values used by mc - final double f = ctx.minecraft().options.sensitivity().get() * (double) 0.6f + (double) 0.2f; - return (float) (mouseDelta * f * f * f * 8.0d) * 0.15f; // yes, one double and one float scaling factor + final double f = ctx.minecraft().options.sensitivity().get() + * (double) 0.6f + + (double) 0.2f; + return (float) (mouseDelta * f * f * f * 8.0d) * 0.15f; } } @@ -459,9 +447,11 @@ static Mode resolve(IPlayerContext ctx, boolean blockInteract) { // always need to set angles while flying return settings.elytraFreeLook.value ? SERVER : CLIENT; } else if (settings.freeLook.value) { - // Regardless of if antiCheatCompatibility is enabled, if a blockInteract is requested then the player - // rotation needs to be set somehow, otherwise Baritone will halt since objectMouseOver() will just be - // whatever the player is mousing over visually. Let's just settle for setting it silently. + // Regardless of if antiCheatCompatibility is enabled, if a + // blockInteract is requested then the player rotation needs to be + // set somehow, otherwise Baritone will halt since objectMouseOver() + // will just be whatever the player is mousing over visually. Let's + // just settle for setting it silently. if (blockInteract) { return blockFreeLook ? SERVER : CLIENT; } diff --git a/src/main/java/baritone/utils/BaritoneMath.java b/src/main/java/baritone/utils/BaritoneMath.java index b67b52437..aa00b5c47 100644 --- a/src/main/java/baritone/utils/BaritoneMath.java +++ b/src/main/java/baritone/utils/BaritoneMath.java @@ -36,7 +36,9 @@ public static int fastCeil(final double v) { } public static double normalize(double value, double maxValue) { - if (maxValue == 0) return 0; - return value/maxValue; + if (maxValue == 0) { + return 0; + } + return value / maxValue; } } From 9a42997fe33d50c20f17b7e07c7654727cfa9172 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 09:39:31 -0500 Subject: [PATCH 04/26] Sample interpolated rotation arcs --- .../java/baritone/behavior/LookBehavior.java | 107 +++++++++++------- 1 file changed, 67 insertions(+), 40 deletions(-) diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 25eee641f..c04b50880 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -62,10 +62,9 @@ public final class LookBehavior extends Behavior implements ILookBehavior { private final Deque smoothYawBuffer; private final Deque smoothPitchBuffer; - private int interpolationStage; - private Vec3 interpolationSource; - private Vec3 interpolationTarget; - private Vec3 interpolationCenter; + private RotationArc interpolationArc; + private Rotation previousArcSample; + private Rotation currentArcSample; public LookBehavior(Baritone baritone) { super(baritone); @@ -165,8 +164,9 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { } this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); + final Rotation start = ctx.playerRotations(); final Rotation actual = this.processor.peekRotation(this.target.rotation); - this.updateInterpolation(actual); + this.updateInterpolation(start, actual); break; } case POST: { @@ -183,51 +183,31 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { } } - private void updateInterpolation(Rotation actual) { + private void updateInterpolation(Rotation start, Rotation actual) { final int interpolationLength = Baritone.settings().interpolatedLookLength.value; - if (this.interpolationStage == 0) { - this.interpolationCenter = ctx.playerHead(); - this.interpolationSource = RotationUtils.calcLookDirectionFromRotation(this.prevRotation) - .add(this.interpolationCenter); - this.interpolationTarget = RotationUtils.calcLookDirectionFromRotation(actual) - .add(this.interpolationCenter); - this.interpolationStage++; - return; - } - - if (this.interpolationStage < interpolationLength) { - this.applyInterpolatedRotation(interpolationLength); - this.interpolationStage++; - return; + if (this.interpolationArc == null) { + this.interpolationArc = new RotationArc(start, actual, interpolationLength); } - if (this.interpolationStage == interpolationLength) { - this.applyInterpolatedRotation(interpolationLength); + this.previousArcSample = this.interpolationArc.getCurrentRotation(); + this.currentArcSample = this.interpolationArc.advance(); + this.applyRotation(this.currentArcSample); + this.serverRotation = this.currentArcSample; + if (this.interpolationArc.isComplete()) { + this.interpolationArc = null; } - this.resetInterpolation(); } - private void applyInterpolatedRotation(int interpolationLength) { - final Vec3 interpolatedLook = RotationUtils.alerp( - this.interpolationSource, - this.interpolationTarget, - this.interpolationCenter, - BaritoneMath.normalize(this.interpolationStage, interpolationLength)); - final Rotation interpolatedRotation = RotationUtils.calcRotationFromVec3d( - this.interpolationCenter, - interpolatedLook, - new Rotation(0, 0)); - - ctx.player().setYRot(interpolatedRotation.getYaw()); - ctx.player().setXRot(interpolatedRotation.getPitch()); + private void applyRotation(Rotation rotation) { + ctx.player().setYRot(rotation.getYaw()); + ctx.player().setXRot(rotation.getPitch()); } private void resetInterpolation() { - this.interpolationStage = 0; - this.interpolationSource = null; - this.interpolationTarget = null; - this.interpolationCenter = null; + this.interpolationArc = null; + this.previousArcSample = null; + this.currentArcSample = null; } @Override @@ -463,4 +443,51 @@ static Mode resolve(IPlayerContext ctx, boolean blockInteract) { } } } + + private static final class RotationArc { + + private final Rotation startRotation; + private final Rotation targetRotation; + private final int length; + private int stage; + + private RotationArc(Rotation startRotation, Rotation targetRotation, int length) { + this.startRotation = startRotation; + this.targetRotation = targetRotation; + this.length = Math.max(1, length); + } + + private Rotation getCurrentRotation() { + return this.arcAt(BaritoneMath.normalize(this.stage, this.length)); + } + + private Rotation advance() { + if (this.stage < this.length) { + this.stage++; + } + return this.getCurrentRotation(); + } + + private boolean isComplete() { + return this.stage >= this.length; + } + + private Rotation arcAt(double t) { + if (t <= 0) { + return this.startRotation; + } + if (t >= 1) { + return this.targetRotation; + } + final Vec3 source = RotationUtils.calcLookDirectionFromRotation(this.startRotation); + final Vec3 target = RotationUtils.calcLookDirectionFromRotation(this.targetRotation); + if (source.distanceToSqr(target) < 1.0E-8) { + return this.targetRotation; + } + return RotationUtils.calcRotationFromVec3d( + Vec3.ZERO, + RotationUtils.alerp(source, target, Vec3.ZERO, t), + new Rotation(0, 0)); + } + } } From 2290f6cacd675fefb957f9e4c70fabe65a409c35 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 09:40:51 -0500 Subject: [PATCH 05/26] Track tick arc samples for rendering --- src/main/java/baritone/behavior/LookBehavior.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index c04b50880..634f5005c 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -170,7 +170,11 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { break; } case POST: { - if (this.prevRotation != null && this.target.mode == Target.Mode.SERVER) { + if (this.prevRotation != null && this.previousArcSample != null && this.currentArcSample != null) { + this.applyVisualArc(); + this.previousArcSample = null; + this.currentArcSample = null; + } else if (this.prevRotation != null && this.target.mode == Target.Mode.SERVER) { ctx.player().setYRot(this.prevRotation.getYaw()); ctx.player().setXRot(this.prevRotation.getPitch()); } @@ -204,6 +208,12 @@ private void applyRotation(Rotation rotation) { ctx.player().setXRot(rotation.getPitch()); } + private void applyVisualArc() { + ctx.player().yRotO = this.previousArcSample.getYaw(); + ctx.player().xRotO = this.previousArcSample.getPitch(); + this.applyRotation(this.currentArcSample); + } + private void resetInterpolation() { this.interpolationArc = null; this.previousArcSample = null; From f2892f59e740c0eb551b82eff31f90ea988201b6 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 09:45:48 -0500 Subject: [PATCH 06/26] Move rotation arc into rotation utilities --- .../baritone/api/utils/RotationUtils.java | 47 ++++++++++++++++++ .../java/baritone/behavior/LookBehavior.java | 49 +------------------ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index b110fdc57..d5bacad85 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -310,6 +310,53 @@ public static Vec3 alerp(Vec3 start, Vec3 end, Vec3 origin, double t) { return center.add(new Vec3(radius, radius, radius).multiply(arcDirection)); } + public static final class RotationArc { + + private final Rotation startRotation; + private final Rotation targetRotation; + private final int length; + private int stage; + + public RotationArc(Rotation startRotation, Rotation targetRotation, int length) { + this.startRotation = startRotation; + this.targetRotation = targetRotation; + this.length = Math.max(1, length); + } + + public Rotation getCurrentRotation() { + return this.arcAt(this.stage / (double) this.length); + } + + public Rotation advance() { + if (this.stage < this.length) { + this.stage++; + } + return this.getCurrentRotation(); + } + + public boolean isComplete() { + return this.stage >= this.length; + } + + public Rotation arcAt(double t) { + if (t <= 0) { + return this.startRotation; + } + if (t >= 1) { + return this.targetRotation; + } + final Vec3 source = calcLookDirectionFromRotation(this.startRotation); + final Vec3 target = calcLookDirectionFromRotation(this.targetRotation); + if (source.distanceToSqr(target) < 1.0E-8) { + return this.targetRotation; + } + return calcRotationFromVec3d( + Vec3.ZERO, + alerp(source, target, Vec3.ZERO, t), + new Rotation(0, 0)); + } + } + @Deprecated public static Optional reachable(LocalPlayer entity, BlockPos pos, double blockReachDistance) { return reachable(entity, pos, blockReachDistance, false); diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 634f5005c..6afa0df4e 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -30,13 +30,12 @@ import baritone.api.utils.IPlayerContext; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; +import baritone.api.utils.RotationUtils.RotationArc; import baritone.behavior.look.ForkableRandom; -import baritone.utils.BaritoneMath; import java.util.ArrayDeque; import java.util.Deque; import java.util.Optional; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; -import net.minecraft.world.phys.Vec3; public final class LookBehavior extends Behavior implements ILookBehavior { @@ -454,50 +453,4 @@ static Mode resolve(IPlayerContext ctx, boolean blockInteract) { } } - private static final class RotationArc { - - private final Rotation startRotation; - private final Rotation targetRotation; - private final int length; - private int stage; - - private RotationArc(Rotation startRotation, Rotation targetRotation, int length) { - this.startRotation = startRotation; - this.targetRotation = targetRotation; - this.length = Math.max(1, length); - } - - private Rotation getCurrentRotation() { - return this.arcAt(BaritoneMath.normalize(this.stage, this.length)); - } - - private Rotation advance() { - if (this.stage < this.length) { - this.stage++; - } - return this.getCurrentRotation(); - } - - private boolean isComplete() { - return this.stage >= this.length; - } - - private Rotation arcAt(double t) { - if (t <= 0) { - return this.startRotation; - } - if (t >= 1) { - return this.targetRotation; - } - final Vec3 source = RotationUtils.calcLookDirectionFromRotation(this.startRotation); - final Vec3 target = RotationUtils.calcLookDirectionFromRotation(this.targetRotation); - if (source.distanceToSqr(target) < 1.0E-8) { - return this.targetRotation; - } - return RotationUtils.calcRotationFromVec3d( - Vec3.ZERO, - RotationUtils.alerp(source, target, Vec3.ZERO, t), - new Rotation(0, 0)); - } - } } From fd6d57c9a6507425fea2ea4dadb7b3ae82ee8c5f Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 09:58:24 -0500 Subject: [PATCH 07/26] Harden rotation arc interpolation --- .../baritone/api/utils/RotationUtils.java | 106 +++++++++++++----- .../baritone/utils/RotationUtilsTest.java | 63 +++++++++++ 2 files changed, 143 insertions(+), 26 deletions(-) create mode 100644 src/test/java/baritone/utils/RotationUtilsTest.java diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index d5bacad85..a7b841d8d 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -51,6 +51,9 @@ public final class RotationUtils { public static final double RAD_TO_DEG = 180.0 / Math.PI; public static final float RAD_TO_DEG_F = (float) RAD_TO_DEG; + private static final double ARC_EPSILON = 1.0E-8; + private static final double TWO_PI = Math.PI * 2.0; + /** * Offsets from the root block position to the center of each side. */ @@ -268,46 +271,97 @@ public static Optional reachableCenter(IPlayerContext ctx, BlockPos po * Interpolates along an arc from {@code start} to {@code end} around {@code origin}. */ public static Vec3 alerp(Vec3 start, Vec3 end, Vec3 origin, double t) { + if (t <= 0) { + return start; + } + if (t >= 1) { + return end; + } + if (start.distanceToSqr(end) < ARC_EPSILON) { + return start; + } + + Vec3 startOffset = start.subtract(origin); + Vec3 endOffset = end.subtract(origin); + if (startOffset.lengthSqr() < ARC_EPSILON || endOffset.lengthSqr() < ARC_EPSILON) { + return lerp(start, end, t); + } + + Vec3 normal = startOffset.cross(endOffset); + if (normal.lengthSqr() < ARC_EPSILON) { + return colinearAlerp(start, end, origin, t, startOffset, endOffset); + } + Vec3 midpoint = start.add(end).scale(0.5); - Vec3 normal = start.subtract(origin).cross(end.subtract(origin)).normalize(); + normal = normal.normalize(); Vec3 pathDirection = end.subtract(start).normalize(); Vec3 toCenter = pathDirection.cross(normal).normalize(); Vec3 originToMid = midpoint.subtract(origin); double projectionLength = originToMid.dot(toCenter); - Vec3 center = midpoint.subtract( - new Vec3(projectionLength, projectionLength, projectionLength).multiply(toCenter)); + Vec3 center = midpoint.subtract(toCenter.scale(projectionLength)); double radius = start.distanceTo(center); + if (radius < ARC_EPSILON) { + return lerp(start, end, t); + } Vec3 centerToStart = start.subtract(center); Vec3 centerToEnd = end.subtract(center); - double angleStart = Mth.atan2( + double angleStart = Math.atan2( centerToStart.dot(toCenter), centerToStart.dot(pathDirection)); - double angleEnd = Mth.atan2( + double angleEnd = Math.atan2( centerToEnd.dot(toCenter), centerToEnd.dot(pathDirection)); + double angle = angleStart + wrapRadians(angleEnd - angleStart) * t; + Vec3 arcDirection = pathDirection.scale(Math.cos(angle)).add(toCenter.scale(Math.sin(angle))); - // Ensure we're using the shorter arc - if (Mth.abs((float) (angleEnd - angleStart)) > Mth.PI) { - if (angleStart < angleEnd) { - angleStart += 2 * Mth.PI; - } else { - angleEnd += 2 * Mth.PI; - } + return center.add(arcDirection.scale(radius)); + } + + private static Vec3 lerp(Vec3 start, Vec3 end, double t) { + return start.add(end.subtract(start).scale(t)); + } + + private static Vec3 colinearAlerp( + Vec3 start, + Vec3 end, + Vec3 origin, + double t, + Vec3 startOffset, + Vec3 endOffset) { + if (startOffset.dot(endOffset) >= 0) { + return lerp(start, end, t); } - double angle = (1 - t) * angleStart + t * angleEnd; - Vec3 arcDirection = new Vec3( - Mth.cos((float) angle), - Mth.cos((float) angle), - Mth.cos((float) angle)) - .multiply(pathDirection) - .add(new Vec3( - Mth.sin((float) angle), - Mth.sin((float) angle), - Mth.sin((float) angle)) - .multiply(toCenter)); - - return center.add(new Vec3(radius, radius, radius).multiply(arcDirection)); + Vec3 startDirection = startOffset.normalize(); + Vec3 perpendicular = perpendicular(startDirection); + double startRadius = startOffset.length(); + double endRadius = endOffset.length(); + double radius = startRadius + (endRadius - startRadius) * t; + double angle = Math.PI * t; + Vec3 arcDirection = startDirection.scale(Math.cos(angle)).add(perpendicular.scale(Math.sin(angle))); + + return origin.add(arcDirection.scale(radius)); + } + + private static Vec3 perpendicular(Vec3 vector) { + Vec3 axis = Math.abs(vector.x) < Math.abs(vector.y) + ? new Vec3(1, 0, 0) + : new Vec3(0, 1, 0); + Vec3 perpendicular = vector.cross(axis); + if (perpendicular.lengthSqr() < ARC_EPSILON) { + perpendicular = vector.cross(new Vec3(0, 0, 1)); + } + return perpendicular.normalize(); + } + + private static double wrapRadians(double angle) { + double wrapped = angle % TWO_PI; + if (wrapped <= -Math.PI) { + wrapped += TWO_PI; + } else if (wrapped > Math.PI) { + wrapped -= TWO_PI; + } + return wrapped; } public static final class RotationArc { @@ -353,7 +407,7 @@ public Rotation arcAt(double t) { return calcRotationFromVec3d( Vec3.ZERO, alerp(source, target, Vec3.ZERO, t), - new Rotation(0, 0)); + this.startRotation); } } diff --git a/src/test/java/baritone/utils/RotationUtilsTest.java b/src/test/java/baritone/utils/RotationUtilsTest.java new file mode 100644 index 000000000..5852d9ba1 --- /dev/null +++ b/src/test/java/baritone/utils/RotationUtilsTest.java @@ -0,0 +1,63 @@ +/* + * 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; + +import baritone.api.utils.RotationUtils; +import net.minecraft.world.phys.Vec3; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class RotationUtilsTest { + + private static final double EPSILON = 1.0E-10; + + @Test + public void testAlerpIdenticalPoints() { + Vec3 point = new Vec3(1, 2, 3); + + assertVecEquals(point, RotationUtils.alerp(point, point, Vec3.ZERO, 0.5)); + } + + @Test + public void testAlerpZeroVectorFallsBackToLine() { + assertVecEquals( + new Vec3(0.5, 0, 0), + RotationUtils.alerp(Vec3.ZERO, new Vec3(2, 0, 0), Vec3.ZERO, 0.25)); + } + + @Test + public void testAlerpSameRayFallsBackToLine() { + assertVecEquals( + new Vec3(1.5, 0, 0), + RotationUtils.alerp(new Vec3(1, 0, 0), new Vec3(2, 0, 0), Vec3.ZERO, 0.5)); + } + + @Test + public void testAlerpOppositeRayUsesStableHalfCircle() { + assertVecEquals( + new Vec3(0, 0, 1), + RotationUtils.alerp(new Vec3(1, 0, 0), new Vec3(-1, 0, 0), Vec3.ZERO, 0.5)); + } + + private static void assertVecEquals(Vec3 expected, Vec3 actual) { + assertEquals(expected.x, actual.x, EPSILON); + assertEquals(expected.y, actual.y, EPSILON); + assertEquals(expected.z, actual.z, EPSILON); + } +} From e001227c7e7c61ced1a7dc67af39fda0d50bdc6a Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 10:18:13 -0500 Subject: [PATCH 08/26] Prepare per-frame visual arc sampling --- .../baritone/api/utils/RotationUtils.java | 8 ++++ .../baritone/launch/mixins/MixinEntity.java | 40 +++++++++++++++++++ .../java/baritone/behavior/LookBehavior.java | 31 ++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index a7b841d8d..47a6dd1f1 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -364,6 +364,10 @@ private static double wrapRadians(double angle) { return wrapped; } + private static double clamp(double value, double min, double max) { + return Math.max(min, Math.min(max, value)); + } + public static final class RotationArc { private final Rotation startRotation; @@ -381,6 +385,10 @@ public Rotation getCurrentRotation() { return this.arcAt(this.stage / (double) this.length); } + public Rotation getCurrentRotation(float partialTicks) { + return this.arcAt((this.stage - 1 + clamp(partialTicks, 0, 1)) / (double) this.length); + } + public Rotation advance() { if (this.stage < this.length) { this.stage++; diff --git a/src/launch/java/baritone/launch/mixins/MixinEntity.java b/src/launch/java/baritone/launch/mixins/MixinEntity.java index 748095351..1d48da7d3 100644 --- a/src/launch/java/baritone/launch/mixins/MixinEntity.java +++ b/src/launch/java/baritone/launch/mixins/MixinEntity.java @@ -18,7 +18,10 @@ package baritone.launch.mixins; import baritone.api.BaritoneAPI; +import baritone.api.IBaritone; import baritone.api.event.events.RotationMoveEvent; +import baritone.api.utils.Rotation; +import baritone.behavior.LookBehavior; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.Entity; import org.spongepowered.asm.mixin.Mixin; @@ -27,6 +30,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(Entity.class) public class MixinEntity { @@ -66,4 +70,40 @@ private void moveRelativeReturn(CallbackInfo info) { this.motionUpdateRotationEvent = null; } } + + @Inject( + method = "getViewYRot", + at = @At("HEAD"), + cancellable = true + ) + private void getViewYRot(float partialTicks, CallbackInfoReturnable cir) { + Rotation visualRotation = this.getVisualRotation(partialTicks); + if (visualRotation != null) { + cir.setReturnValue(visualRotation.getYaw()); + } + } + + @Inject( + method = "getViewXRot", + at = @At("HEAD"), + cancellable = true + ) + private void getViewXRot(float partialTicks, CallbackInfoReturnable cir) { + Rotation visualRotation = this.getVisualRotation(partialTicks); + if (visualRotation != null) { + cir.setReturnValue(visualRotation.getPitch()); + } + } + + @Unique + private Rotation getVisualRotation(float partialTicks) { + if (!LocalPlayer.class.isInstance(this)) { + return null; + } + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); + if (baritone == null) { + return null; + } + return ((LookBehavior) baritone.getLookBehavior()).getVisualRotation(partialTicks); + } } diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 6afa0df4e..bbf7015b1 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -27,6 +27,7 @@ import baritone.api.event.events.RotationMoveEvent; import baritone.api.event.events.TickEvent; import baritone.api.event.events.WorldEvent; +import baritone.api.event.events.type.EventState; import baritone.api.utils.IPlayerContext; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; @@ -62,8 +63,12 @@ public final class LookBehavior extends Behavior implements ILookBehavior { private final Deque smoothYawBuffer; private final Deque smoothPitchBuffer; private RotationArc interpolationArc; + private RotationArc pendingRenderArc; + private RotationArc renderArc; private Rotation previousArcSample; private Rotation currentArcSample; + private float cachedVisualPartialTicks = Float.NaN; + private Rotation cachedVisualRotation; public LookBehavior(Baritone baritone) { super(baritone); @@ -93,6 +98,11 @@ public void onTick(TickEvent event) { public void onPlayerUpdate(PlayerUpdateEvent event) { if (this.target == null) { + if (event.getState() == EventState.PRE) { + this.renderArc = null; + this.cachedVisualPartialTicks = Float.NaN; + this.cachedVisualRotation = null; + } return; } @@ -159,6 +169,7 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { switch (event.getState()) { case PRE: { if (this.target.mode == Target.Mode.NONE) { + this.pendingRenderArc = null; return; } @@ -195,6 +206,7 @@ private void updateInterpolation(Rotation start, Rotation actual) { this.previousArcSample = this.interpolationArc.getCurrentRotation(); this.currentArcSample = this.interpolationArc.advance(); + this.pendingRenderArc = this.interpolationArc; this.applyRotation(this.currentArcSample); this.serverRotation = this.currentArcSample; if (this.interpolationArc.isComplete()) { @@ -208,15 +220,34 @@ private void applyRotation(Rotation rotation) { } private void applyVisualArc() { + this.renderArc = this.pendingRenderArc; + this.pendingRenderArc = null; + this.cachedVisualPartialTicks = Float.NaN; + this.cachedVisualRotation = null; ctx.player().yRotO = this.previousArcSample.getYaw(); ctx.player().xRotO = this.previousArcSample.getPitch(); this.applyRotation(this.currentArcSample); } + public Rotation getVisualRotation(float partialTicks) { + if (this.renderArc == null) { + return null; + } + if (this.cachedVisualRotation == null || this.cachedVisualPartialTicks != partialTicks) { + this.cachedVisualPartialTicks = partialTicks; + this.cachedVisualRotation = this.renderArc.getCurrentRotation(partialTicks); + } + return this.cachedVisualRotation; + } + private void resetInterpolation() { this.interpolationArc = null; + this.pendingRenderArc = null; + this.renderArc = null; this.previousArcSample = null; this.currentArcSample = null; + this.cachedVisualPartialTicks = Float.NaN; + this.cachedVisualRotation = null; } @Override From d97311b5f7e9fedfd9ad0bd443c0e6c98fade399 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 10:26:31 -0500 Subject: [PATCH 09/26] Hook local player camera rotation --- .../mixins/MixinClientPlayerEntity.java | 36 +++++++++++++++++ .../baritone/launch/mixins/MixinEntity.java | 40 ------------------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java b/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java index 24e807f62..9a5b0d847 100644 --- a/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java +++ b/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java @@ -22,15 +22,18 @@ import baritone.api.event.events.PlayerUpdateEvent; import baritone.api.event.events.SprintStateEvent; import baritone.api.event.events.type.EventState; +import baritone.api.utils.Rotation; import baritone.behavior.LookBehavior; import net.minecraft.client.KeyMapping; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.player.Abilities; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; /** * @author Brady @@ -54,6 +57,30 @@ private void onPreUpdate(CallbackInfo ci) { } } + @Inject( + method = "getViewYRot", + at = @At("HEAD"), + cancellable = true + ) + private void getViewYRot(float partialTicks, CallbackInfoReturnable cir) { + Rotation visualRotation = this.getVisualRotation(partialTicks); + if (visualRotation != null) { + cir.setReturnValue(visualRotation.getYaw()); + } + } + + @Inject( + method = "getViewXRot", + at = @At("HEAD"), + cancellable = true + ) + private void getViewXRot(float partialTicks, CallbackInfoReturnable cir) { + Rotation visualRotation = this.getVisualRotation(partialTicks); + if (visualRotation != null) { + cir.setReturnValue(visualRotation.getPitch()); + } + } + @Redirect( method = "aiStep", at = @At( @@ -120,4 +147,13 @@ private boolean tryToStartFallFlying(final LocalPlayer instance) { } return instance.tryToStartFallFlying(); } + + @Unique + private Rotation getVisualRotation(float partialTicks) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); + if (baritone == null) { + return null; + } + return ((LookBehavior) baritone.getLookBehavior()).getVisualRotation(partialTicks); + } } diff --git a/src/launch/java/baritone/launch/mixins/MixinEntity.java b/src/launch/java/baritone/launch/mixins/MixinEntity.java index 1d48da7d3..748095351 100644 --- a/src/launch/java/baritone/launch/mixins/MixinEntity.java +++ b/src/launch/java/baritone/launch/mixins/MixinEntity.java @@ -18,10 +18,7 @@ package baritone.launch.mixins; import baritone.api.BaritoneAPI; -import baritone.api.IBaritone; import baritone.api.event.events.RotationMoveEvent; -import baritone.api.utils.Rotation; -import baritone.behavior.LookBehavior; import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.Entity; import org.spongepowered.asm.mixin.Mixin; @@ -30,7 +27,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(Entity.class) public class MixinEntity { @@ -70,40 +66,4 @@ private void moveRelativeReturn(CallbackInfo info) { this.motionUpdateRotationEvent = null; } } - - @Inject( - method = "getViewYRot", - at = @At("HEAD"), - cancellable = true - ) - private void getViewYRot(float partialTicks, CallbackInfoReturnable cir) { - Rotation visualRotation = this.getVisualRotation(partialTicks); - if (visualRotation != null) { - cir.setReturnValue(visualRotation.getYaw()); - } - } - - @Inject( - method = "getViewXRot", - at = @At("HEAD"), - cancellable = true - ) - private void getViewXRot(float partialTicks, CallbackInfoReturnable cir) { - Rotation visualRotation = this.getVisualRotation(partialTicks); - if (visualRotation != null) { - cir.setReturnValue(visualRotation.getPitch()); - } - } - - @Unique - private Rotation getVisualRotation(float partialTicks) { - if (!LocalPlayer.class.isInstance(this)) { - return null; - } - IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); - if (baritone == null) { - return null; - } - return ((LookBehavior) baritone.getLookBehavior()).getVisualRotation(partialTicks); - } } From b2c4001d0181815ec22ee1732c10a7f2b76f34ae Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 10:42:58 -0500 Subject: [PATCH 10/26] Scale arc duration by angular speed --- src/api/java/baritone/api/Settings.java | 7 ++--- .../baritone/api/utils/RotationUtils.java | 26 +++++++++++++++++++ .../java/baritone/behavior/LookBehavior.java | 4 +-- .../baritone/utils/RotationUtilsTest.java | 24 +++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index a0c6ce069..052653e07 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -793,13 +793,14 @@ public final class Settings { /** * Interpolates player rotation from the current look direction to the target direction. *

- * This uses {@link #interpolatedLookLength} to determine how many ticks one - * interpolation cycle takes. + * This uses {@link #interpolatedLookLength} as a maximum angular speed, so larger + * turns take more ticks than smaller turns. */ public final Setting interpolatedLook = new Setting<>(false); /** - * Time, in ticks, to complete one {@link #interpolatedLook} cycle. + * Maximum angular speed, in degrees per tick, for {@link #interpolatedLook}. + * The historical name is retained for config compatibility. */ public final Setting interpolatedLookLength = new Setting<>(10); diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index 47a6dd1f1..e38125877 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -381,6 +381,16 @@ public RotationArc(Rotation startRotation, Rotation targetRotation, int length) this.length = Math.max(1, length); } + public static RotationArc fromAngularSpeed( + Rotation startRotation, + Rotation targetRotation, + double degreesPerTick) { + return new RotationArc( + startRotation, + targetRotation, + ticksForAngularSpeed(startRotation, targetRotation, degreesPerTick)); + } + public Rotation getCurrentRotation() { return this.arcAt(this.stage / (double) this.length); } @@ -417,6 +427,22 @@ public Rotation arcAt(double t) { alerp(source, target, Vec3.ZERO, t), this.startRotation); } + + private static int ticksForAngularSpeed(Rotation startRotation, Rotation targetRotation, double degreesPerTick) { + if (degreesPerTick <= ARC_EPSILON) { + return 1; + } + + Vec3 source = calcLookDirectionFromRotation(startRotation); + Vec3 target = calcLookDirectionFromRotation(targetRotation); + double dot = clamp(source.dot(target), -1, 1); + double angularDistance = Math.toDegrees(Math.acos(dot)); + + if (angularDistance < ARC_EPSILON) { + return 1; + } + return Math.max(1, (int) Math.ceil(angularDistance / degreesPerTick)); + } } @Deprecated diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index bbf7015b1..f5fd6c92b 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -198,10 +198,10 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { } private void updateInterpolation(Rotation start, Rotation actual) { - final int interpolationLength = Baritone.settings().interpolatedLookLength.value; + final int interpolationSpeed = Baritone.settings().interpolatedLookLength.value; if (this.interpolationArc == null) { - this.interpolationArc = new RotationArc(start, actual, interpolationLength); + this.interpolationArc = RotationArc.fromAngularSpeed(start, actual, interpolationSpeed); } this.previousArcSample = this.interpolationArc.getCurrentRotation(); diff --git a/src/test/java/baritone/utils/RotationUtilsTest.java b/src/test/java/baritone/utils/RotationUtilsTest.java index 5852d9ba1..f385014b8 100644 --- a/src/test/java/baritone/utils/RotationUtilsTest.java +++ b/src/test/java/baritone/utils/RotationUtilsTest.java @@ -17,7 +17,9 @@ package baritone.utils; +import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; +import baritone.api.utils.RotationUtils.RotationArc; import net.minecraft.world.phys.Vec3; import org.junit.Test; @@ -55,9 +57,31 @@ public void testAlerpOppositeRayUsesStableHalfCircle() { RotationUtils.alerp(new Vec3(1, 0, 0), new Vec3(-1, 0, 0), Vec3.ZERO, 0.5)); } + @Test + public void testRotationArcUsesAngularSpeed() { + assertEquals( + 1, + countTicks(RotationArc.fromAngularSpeed(new Rotation(0, 0), new Rotation(10, 0), 30))); + assertEquals( + 3, + countTicks(RotationArc.fromAngularSpeed(new Rotation(0, 0), new Rotation(90, 0), 30))); + assertEquals( + 4, + countTicks(RotationArc.fromAngularSpeed(new Rotation(0, 0), new Rotation(91, 0), 30))); + } + private static void assertVecEquals(Vec3 expected, Vec3 actual) { assertEquals(expected.x, actual.x, EPSILON); assertEquals(expected.y, actual.y, EPSILON); assertEquals(expected.z, actual.z, EPSILON); } + + private static int countTicks(RotationArc arc) { + int ticks = 0; + while (!arc.isComplete()) { + arc.advance(); + ticks++; + } + return ticks; + } } From 4e4ebbeacf3a4e896bdffff0d8efe61468cb55b3 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 10:53:39 -0500 Subject: [PATCH 11/26] Restart arcs when player origin moves --- .../java/baritone/behavior/LookBehavior.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index f5fd6c92b..80fc1e86f 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -37,9 +37,12 @@ import java.util.Deque; import java.util.Optional; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; +import net.minecraft.world.phys.Vec3; public final class LookBehavior extends Behavior implements ILookBehavior { + private static final double ORIGIN_EPSILON = 1.0E-8; + /** * The current look target, may be {@code null}. */ @@ -63,6 +66,7 @@ public final class LookBehavior extends Behavior implements ILookBehavior { private final Deque smoothYawBuffer; private final Deque smoothPitchBuffer; private RotationArc interpolationArc; + private Vec3 interpolationOrigin; private RotationArc pendingRenderArc; private RotationArc renderArc; private Rotation previousArcSample; @@ -176,7 +180,7 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); final Rotation start = ctx.playerRotations(); final Rotation actual = this.processor.peekRotation(this.target.rotation); - this.updateInterpolation(start, actual); + this.updateInterpolation(start, actual, ctx.playerHead()); break; } case POST: { @@ -197,11 +201,17 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { } } - private void updateInterpolation(Rotation start, Rotation actual) { + private void updateInterpolation(Rotation start, Rotation actual, Vec3 origin) { final int interpolationSpeed = Baritone.settings().interpolatedLookLength.value; + if (this.interpolationArc != null && this.interpolationOriginMoved(origin)) { + start = this.interpolationArc.getCurrentRotation(); + this.interpolationArc = null; + } + if (this.interpolationArc == null) { this.interpolationArc = RotationArc.fromAngularSpeed(start, actual, interpolationSpeed); + this.interpolationOrigin = origin; } this.previousArcSample = this.interpolationArc.getCurrentRotation(); @@ -211,9 +221,15 @@ private void updateInterpolation(Rotation start, Rotation actual) { this.serverRotation = this.currentArcSample; if (this.interpolationArc.isComplete()) { this.interpolationArc = null; + this.interpolationOrigin = null; } } + private boolean interpolationOriginMoved(Vec3 origin) { + return this.interpolationOrigin != null + && this.interpolationOrigin.distanceToSqr(origin) > ORIGIN_EPSILON; + } + private void applyRotation(Rotation rotation) { ctx.player().setYRot(rotation.getYaw()); ctx.player().setXRot(rotation.getPitch()); @@ -242,6 +258,7 @@ public Rotation getVisualRotation(float partialTicks) { private void resetInterpolation() { this.interpolationArc = null; + this.interpolationOrigin = null; this.pendingRenderArc = null; this.renderArc = null; this.previousArcSample = null; From 2951eb9e9c6a0e4625c12adc15f0cf4d73e71849 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 11:00:26 -0500 Subject: [PATCH 12/26] Hold rotation while mining target blocks --- .../baritone/api/behavior/ILookBehavior.java | 12 +++++++++ .../api/utils/IInputOverrideHandler.java | 5 ++++ .../java/baritone/behavior/LookBehavior.java | 17 +++++++++---- .../baritone/pathing/movement/Movement.java | 13 +++++++--- .../pathing/movement/MovementState.java | 11 ++++++++ .../java/baritone/process/BuilderProcess.java | 9 +++++-- .../java/baritone/process/MineProcess.java | 9 +++++-- .../java/baritone/utils/BlockBreakHelper.java | 25 ++++++++++++++++--- .../baritone/utils/InputOverrideHandler.java | 6 +++++ 9 files changed, 91 insertions(+), 16 deletions(-) diff --git a/src/api/java/baritone/api/behavior/ILookBehavior.java b/src/api/java/baritone/api/behavior/ILookBehavior.java index d78e7f8b3..43f99709e 100644 --- a/src/api/java/baritone/api/behavior/ILookBehavior.java +++ b/src/api/java/baritone/api/behavior/ILookBehavior.java @@ -39,6 +39,18 @@ public interface ILookBehavior extends IBehavior { */ void updateTarget(Rotation rotation, boolean blockInteract); + /** + * Updates the current {@link ILookBehavior} target and optionally restarts any active interpolation. + * + * @param rotation The target rotations + * @param blockInteract Whether the target rotations are needed for a block interaction + * @param restartInterpolation Whether active interpolation should restart from its current sample + * @see #updateTarget(Rotation, boolean) + */ + default void updateTarget(Rotation rotation, boolean blockInteract, boolean restartInterpolation) { + updateTarget(rotation, blockInteract); + } + /** * The aim processor instance for this {@link ILookBehavior}, which is responsible for applying additional, * deterministic transformations to the target rotation set by {@link #updateTarget}. diff --git a/src/api/java/baritone/api/utils/IInputOverrideHandler.java b/src/api/java/baritone/api/utils/IInputOverrideHandler.java index 3cfb74385..9bd4aa04d 100644 --- a/src/api/java/baritone/api/utils/IInputOverrideHandler.java +++ b/src/api/java/baritone/api/utils/IInputOverrideHandler.java @@ -19,6 +19,7 @@ import baritone.api.behavior.IBehavior; import baritone.api.utils.input.Input; +import net.minecraft.core.BlockPos; /** * @author Brady @@ -31,4 +32,8 @@ public interface IInputOverrideHandler extends IBehavior { void setInputForceState(Input input, boolean forced); void clearAllKeys(); + + default boolean isBreakingBlock(BlockPos pos) { + return false; + } } diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 80fc1e86f..952a003fa 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -83,7 +83,12 @@ public LookBehavior(Baritone baritone) { @Override public void updateTarget(Rotation rotation, boolean blockInteract) { - this.target = new Target(rotation, Target.Mode.resolve(ctx, blockInteract)); + this.updateTarget(rotation, blockInteract, false); + } + + @Override + public void updateTarget(Rotation rotation, boolean blockInteract, boolean restartInterpolation) { + this.target = new Target(rotation, Target.Mode.resolve(ctx, blockInteract), restartInterpolation); } @Override @@ -180,7 +185,7 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); final Rotation start = ctx.playerRotations(); final Rotation actual = this.processor.peekRotation(this.target.rotation); - this.updateInterpolation(start, actual, ctx.playerHead()); + this.updateInterpolation(start, actual, ctx.playerHead(), this.target.restartInterpolation); break; } case POST: { @@ -201,10 +206,10 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { } } - private void updateInterpolation(Rotation start, Rotation actual, Vec3 origin) { + private void updateInterpolation(Rotation start, Rotation actual, Vec3 origin, boolean restartArc) { final int interpolationSpeed = Baritone.settings().interpolatedLookLength.value; - if (this.interpolationArc != null && this.interpolationOriginMoved(origin)) { + if (this.interpolationArc != null && (restartArc || this.interpolationOriginMoved(origin))) { start = this.interpolationArc.getCurrentRotation(); this.interpolationArc = null; } @@ -453,10 +458,12 @@ private static class Target { public final Rotation rotation; public final Mode mode; + public final boolean restartInterpolation; - public Target(Rotation rotation, Mode mode) { + public Target(Rotation rotation, Mode mode, boolean restartInterpolation) { this.rotation = rotation; this.mode = mode; + this.restartInterpolation = restartInterpolation; } enum Mode { diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index 739c8ee89..011f2c0b0 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -132,10 +132,12 @@ public MovementStatus update() { } // If the movement target has to force the new rotations, or we aren't using silent move, then force the rotations - currentState.getTarget().getRotation().ifPresent(rotation -> + MovementState.MovementTarget target = currentState.getTarget(); + target.getRotation().ifPresent(rotation -> baritone.getLookBehavior().updateTarget( rotation, - currentState.getTarget().hasToForceRotations())); + target.hasToForceRotations(), + target.shouldRestartInterpolation())); baritone.getInputOverrideHandler().clearAllKeys(); currentState.getInputStates().forEach((input, forced) -> { baritone.getInputOverrideHandler().setInputForceState(input, forced); @@ -162,11 +164,16 @@ protected boolean prepared(MovementState state) { if (!MovementHelper.canWalkThrough(ctx, blockPos)) { // can't break air, so don't try somethingInTheWay = true; MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, blockPos)); + if (baritone.getInputOverrideHandler().isBreakingBlock(blockPos)) { + state.setTarget(new MovementState.MovementTarget(ctx.playerRotations(), true, true)); + state.setInput(Input.CLICK_LEFT, true); + return false; + } Optional reachable = RotationUtils.reachable(ctx, blockPos, ctx.playerController().getBlockReachDistance()); if (reachable.isPresent()) { Rotation rotTowardsBlock = reachable.get(); state.setTarget(new MovementState.MovementTarget(rotTowardsBlock, true)); - if (ctx.isLookingAt(blockPos) || ctx.playerRotations().isReallyCloseTo(rotTowardsBlock)) { + if (ctx.isLookingAt(blockPos)) { state.setInput(Input.CLICK_LEFT, true); } return false; diff --git a/src/main/java/baritone/pathing/movement/MovementState.java b/src/main/java/baritone/pathing/movement/MovementState.java index 73539698a..2f3483f3f 100644 --- a/src/main/java/baritone/pathing/movement/MovementState.java +++ b/src/main/java/baritone/pathing/movement/MovementState.java @@ -72,13 +72,20 @@ public static class MovementTarget { */ private boolean forceRotations; + private boolean restartInterpolation; + public MovementTarget() { this(null, false); } public MovementTarget(Rotation rotation, boolean forceRotations) { + this(rotation, forceRotations, false); + } + + public MovementTarget(Rotation rotation, boolean forceRotations, boolean restartInterpolation) { this.rotation = rotation; this.forceRotations = forceRotations; + this.restartInterpolation = restartInterpolation; } public final Optional getRotation() { @@ -88,5 +95,9 @@ public final Optional getRotation() { public boolean hasToForceRotations() { return this.forceRotations; } + + public boolean shouldRestartInterpolation() { + return this.restartInterpolation; + } } } diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index b99d41e0a..466a26211 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -550,7 +550,6 @@ public int lengthZ() { // only change look direction if it's safe (don't want to fuck up an in progress parkour for example Rotation rot = toBreak.get().getB(); BetterBlockPos pos = toBreak.get().getA(); - baritone.getLookBehavior().updateTarget(rot, true); MovementHelper.switchToBestToolFor(ctx, bcc.get(pos)); if (ctx.player().isCrouching()) { // really horrible bug where a block is visible for breaking while sneaking but not otherwise @@ -558,7 +557,13 @@ public int lengthZ() { // and is unable since it's unsneaked in the intermediary tick baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); } - if (ctx.isLookingAt(pos) || ctx.playerRotations().isReallyCloseTo(rot)) { + if (baritone.getInputOverrideHandler().isBreakingBlock(pos)) { + baritone.getLookBehavior().updateTarget(ctx.playerRotations(), true, true); + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); + } else { + baritone.getLookBehavior().updateTarget(rot, true); + } + if (ctx.isLookingAt(pos)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index ff905c02f..b02557362 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -124,11 +124,16 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { BlockPos pos = shaft.get(); BlockState state = baritone.bsi.get0(pos); if (!MovementHelper.avoidBreaking(baritone.bsi, pos.getX(), pos.getY(), pos.getZ(), state)) { + MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); + if (isSafeToCancel && baritone.getInputOverrideHandler().isBreakingBlock(pos)) { + baritone.getLookBehavior().updateTarget(ctx.playerRotations(), true, true); + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } Optional rot = RotationUtils.reachable(ctx, pos); if (rot.isPresent() && isSafeToCancel) { baritone.getLookBehavior().updateTarget(rot.get(), true); - MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); - if (ctx.isLookingAt(pos) || ctx.playerRotations().isReallyCloseTo(rot.get())) { + if (ctx.isLookingAt(pos)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); diff --git a/src/main/java/baritone/utils/BlockBreakHelper.java b/src/main/java/baritone/utils/BlockBreakHelper.java index 0c5cf6f00..55b801fa0 100644 --- a/src/main/java/baritone/utils/BlockBreakHelper.java +++ b/src/main/java/baritone/utils/BlockBreakHelper.java @@ -20,6 +20,7 @@ import baritone.api.BaritoneAPI; import baritone.api.utils.IPlayerContext; import baritone.utils.accessor.IPlayerControllerMP; +import net.minecraft.core.BlockPos; import net.minecraft.world.InteractionHand; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; @@ -34,6 +35,7 @@ public final class BlockBreakHelper { private final IPlayerContext ctx; private boolean wasHitting; + private BlockPos breakingBlock; private int breakDelayTimer = 0; BlockBreakHelper(IPlayerContext ctx) { @@ -45,10 +47,14 @@ public void stopBreakingBlock() { if (ctx.player() != null && wasHitting) { ctx.playerController().setHittingBlock(false); ctx.playerController().resetBlockRemoving(); - wasHitting = false; + clearBreakingBlock(); } } + public boolean isBreakingBlock(BlockPos pos) { + return wasHitting && pos.equals(breakingBlock); + } + public void tick(boolean isLeftClick) { if (breakDelayTimer > 0) { breakDelayTimer--; @@ -58,13 +64,14 @@ public void tick(boolean isLeftClick) { boolean isBlockTrace = trace != null && trace.getType() == HitResult.Type.BLOCK; if (isLeftClick && isBlockTrace) { + BlockHitResult blockTrace = (BlockHitResult) trace; ctx.playerController().setHittingBlock(wasHitting); if (ctx.playerController().hasBrokenBlock()) { ctx.playerController().syncHeldItem(); - ctx.playerController().clickBlock(((BlockHitResult) trace).getBlockPos(), ((BlockHitResult) trace).getDirection()); + ctx.playerController().clickBlock(blockTrace.getBlockPos(), blockTrace.getDirection()); ctx.player().swing(InteractionHand.MAIN_HAND); } else { - if (ctx.playerController().onPlayerDamageBlock(((BlockHitResult) trace).getBlockPos(), ((BlockHitResult) trace).getDirection())) { + if (ctx.playerController().onPlayerDamageBlock(blockTrace.getBlockPos(), blockTrace.getDirection())) { ctx.player().swing(InteractionHand.MAIN_HAND); } if (ctx.playerController().hasBrokenBlock()) { // block broken this tick @@ -76,12 +83,22 @@ public void tick(boolean isLeftClick) { } // if true, we're breaking a block. if false, we broke the block this tick wasHitting = !ctx.playerController().hasBrokenBlock(); + if (wasHitting) { + breakingBlock = blockTrace.getBlockPos(); + } else { + clearBreakingBlock(); + } // this value will be reset by the MC client handling mouse keys // since we're not spoofing the click keybind to the client, the client will stop the break if isDestroyingBlock is true // we store and restore this value on the next tick to determine if we're breaking a block ctx.playerController().setHittingBlock(false); } else { - wasHitting = false; + clearBreakingBlock(); } } + + private void clearBreakingBlock() { + wasHitting = false; + breakingBlock = null; + } } diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index 38a32f515..320f8fcf3 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -24,6 +24,7 @@ import baritone.api.utils.input.Input; import baritone.behavior.Behavior; import net.minecraft.client.player.KeyboardInput; +import net.minecraft.core.BlockPos; import java.util.HashMap; import java.util.Map; @@ -82,6 +83,11 @@ public final void clearAllKeys() { this.inputForceStateMap.clear(); } + @Override + public boolean isBreakingBlock(BlockPos pos) { + return this.blockBreakHelper.isBreakingBlock(pos); + } + @Override public final void onTick(TickEvent event) { if (event.getType() == TickEvent.Type.OUT) { From 4e0a3b8eadaa230a740b06e5a796d860284c1b34 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 11:04:03 -0500 Subject: [PATCH 13/26] Retry mining when held ray drifts --- src/main/java/baritone/utils/BlockBreakHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/baritone/utils/BlockBreakHelper.java b/src/main/java/baritone/utils/BlockBreakHelper.java index 55b801fa0..467d987c4 100644 --- a/src/main/java/baritone/utils/BlockBreakHelper.java +++ b/src/main/java/baritone/utils/BlockBreakHelper.java @@ -52,7 +52,7 @@ public void stopBreakingBlock() { } public boolean isBreakingBlock(BlockPos pos) { - return wasHitting && pos.equals(breakingBlock); + return wasHitting && pos.equals(breakingBlock) && ctx.isLookingAt(pos); } public void tick(boolean isLeftClick) { From 05cf9b8000b7dd6bc5262712c4f8384a2519755d Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 11:11:53 -0500 Subject: [PATCH 14/26] Keep mining holds exact --- .../baritone/api/behavior/ILookBehavior.java | 17 ++++++++++++ .../java/baritone/behavior/LookBehavior.java | 27 ++++++++++++++----- .../baritone/pathing/movement/Movement.java | 5 ++-- .../pathing/movement/MovementState.java | 14 ++++++++++ .../java/baritone/process/BuilderProcess.java | 2 +- .../java/baritone/process/MineProcess.java | 2 +- 6 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/api/java/baritone/api/behavior/ILookBehavior.java b/src/api/java/baritone/api/behavior/ILookBehavior.java index 43f99709e..71d2855db 100644 --- a/src/api/java/baritone/api/behavior/ILookBehavior.java +++ b/src/api/java/baritone/api/behavior/ILookBehavior.java @@ -51,6 +51,23 @@ default void updateTarget(Rotation rotation, boolean blockInteract, boolean rest updateTarget(rotation, blockInteract); } + /** + * Updates the current {@link ILookBehavior} target and optionally uses it without aim processing. + * + * @param rotation The target rotations + * @param blockInteract Whether the target rotations are needed for a block interaction + * @param restartInterpolation Whether active interpolation should restart from its current sample + * @param exactRotation Whether the target rotation should skip aim processing + * @see #updateTarget(Rotation, boolean, boolean) + */ + default void updateTarget( + Rotation rotation, + boolean blockInteract, + boolean restartInterpolation, + boolean exactRotation) { + updateTarget(rotation, blockInteract, restartInterpolation); + } + /** * The aim processor instance for this {@link ILookBehavior}, which is responsible for applying additional, * deterministic transformations to the target rotation set by {@link #updateTarget}. diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 952a003fa..f6189de21 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -88,7 +88,16 @@ public void updateTarget(Rotation rotation, boolean blockInteract) { @Override public void updateTarget(Rotation rotation, boolean blockInteract, boolean restartInterpolation) { - this.target = new Target(rotation, Target.Mode.resolve(ctx, blockInteract), restartInterpolation); + this.updateTarget(rotation, blockInteract, restartInterpolation, false); + } + + @Override + public void updateTarget( + Rotation rotation, + boolean blockInteract, + boolean restartInterpolation, + boolean exactRotation) { + this.target = new Target(rotation, Target.Mode.resolve(ctx, blockInteract), restartInterpolation, exactRotation); } @Override @@ -128,7 +137,7 @@ public void onPlayerUpdate(PlayerUpdateEvent event) { } this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); - final Rotation actual = this.processor.peekRotation(this.target.rotation); + final Rotation actual = this.target.rotation(this.processor); ctx.player().setYRot(actual.getYaw()); ctx.player().setXRot(actual.getPitch()); break; @@ -184,7 +193,7 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); final Rotation start = ctx.playerRotations(); - final Rotation actual = this.processor.peekRotation(this.target.rotation); + final Rotation actual = this.target.rotation(this.processor); this.updateInterpolation(start, actual, ctx.playerHead(), this.target.restartInterpolation); break; } @@ -294,7 +303,7 @@ public void onWorldEvent(WorldEvent event) { public void pig() { if (this.target != null) { - final Rotation actual = this.processor.peekRotation(this.target.rotation); + final Rotation actual = this.target.rotation(this.processor); ctx.player().setYRot(actual.getYaw()); } } @@ -310,7 +319,7 @@ public Optional getEffectiveRotation() { @Override public void onPlayerRotationMove(RotationMoveEvent event) { if (this.target != null) { - final Rotation actual = this.processor.peekRotation(this.target.rotation); + final Rotation actual = this.target.rotation(this.processor); event.setYaw(actual.getYaw()); event.setPitch(actual.getPitch()); } @@ -459,11 +468,17 @@ private static class Target { public final Rotation rotation; public final Mode mode; public final boolean restartInterpolation; + public final boolean exactRotation; - public Target(Rotation rotation, Mode mode, boolean restartInterpolation) { + public Target(Rotation rotation, Mode mode, boolean restartInterpolation, boolean exactRotation) { this.rotation = rotation; this.mode = mode; this.restartInterpolation = restartInterpolation; + this.exactRotation = exactRotation; + } + + public Rotation rotation(AimProcessor processor) { + return this.exactRotation ? this.rotation : processor.peekRotation(this.rotation); } enum Mode { diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index 011f2c0b0..366f6101c 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -137,7 +137,8 @@ public MovementStatus update() { baritone.getLookBehavior().updateTarget( rotation, target.hasToForceRotations(), - target.shouldRestartInterpolation())); + target.shouldRestartInterpolation(), + target.isExactRotation())); baritone.getInputOverrideHandler().clearAllKeys(); currentState.getInputStates().forEach((input, forced) -> { baritone.getInputOverrideHandler().setInputForceState(input, forced); @@ -165,7 +166,7 @@ protected boolean prepared(MovementState state) { somethingInTheWay = true; MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, blockPos)); if (baritone.getInputOverrideHandler().isBreakingBlock(blockPos)) { - state.setTarget(new MovementState.MovementTarget(ctx.playerRotations(), true, true)); + state.setTarget(new MovementState.MovementTarget(ctx.playerRotations(), true, true, true)); state.setInput(Input.CLICK_LEFT, true); return false; } diff --git a/src/main/java/baritone/pathing/movement/MovementState.java b/src/main/java/baritone/pathing/movement/MovementState.java index 2f3483f3f..62e884794 100644 --- a/src/main/java/baritone/pathing/movement/MovementState.java +++ b/src/main/java/baritone/pathing/movement/MovementState.java @@ -73,6 +73,7 @@ public static class MovementTarget { private boolean forceRotations; private boolean restartInterpolation; + private boolean exactRotation; public MovementTarget() { this(null, false); @@ -83,9 +84,18 @@ public MovementTarget(Rotation rotation, boolean forceRotations) { } public MovementTarget(Rotation rotation, boolean forceRotations, boolean restartInterpolation) { + this(rotation, forceRotations, restartInterpolation, false); + } + + public MovementTarget( + Rotation rotation, + boolean forceRotations, + boolean restartInterpolation, + boolean exactRotation) { this.rotation = rotation; this.forceRotations = forceRotations; this.restartInterpolation = restartInterpolation; + this.exactRotation = exactRotation; } public final Optional getRotation() { @@ -99,5 +109,9 @@ public boolean hasToForceRotations() { public boolean shouldRestartInterpolation() { return this.restartInterpolation; } + + public boolean isExactRotation() { + return this.exactRotation; + } } } diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index 466a26211..b0ef313f3 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -558,7 +558,7 @@ public int lengthZ() { baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); } if (baritone.getInputOverrideHandler().isBreakingBlock(pos)) { - baritone.getLookBehavior().updateTarget(ctx.playerRotations(), true, true); + baritone.getLookBehavior().updateTarget(ctx.playerRotations(), true, true, true); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } else { baritone.getLookBehavior().updateTarget(rot, true); diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index b02557362..b62b6705f 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -126,7 +126,7 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (!MovementHelper.avoidBreaking(baritone.bsi, pos.getX(), pos.getY(), pos.getZ(), state)) { MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); if (isSafeToCancel && baritone.getInputOverrideHandler().isBreakingBlock(pos)) { - baritone.getLookBehavior().updateTarget(ctx.playerRotations(), true, true); + baritone.getLookBehavior().updateTarget(ctx.playerRotations(), true, true, true); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } From 4251397ca4d148506d74532ad318a4e7084da8a2 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 11:27:08 -0500 Subject: [PATCH 15/26] Avoid pole-crossing rotation arcs --- .../baritone/api/utils/RotationUtils.java | 101 +++++++++++++++++- .../baritone/utils/RotationUtilsTest.java | 8 ++ 2 files changed, 105 insertions(+), 4 deletions(-) diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index e38125877..cce77379d 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -52,7 +52,12 @@ public final class RotationUtils { public static final float RAD_TO_DEG_F = (float) RAD_TO_DEG; private static final double ARC_EPSILON = 1.0E-8; + private static final double POLE_AVOIDANCE_PITCH = 85.0; + private static final double POLE_AVOIDANCE_MARGIN = 1.0; + private static final double LATITUDE_DISTANCE_STEP = 5.0; + private static final double MINOR_ARC_EPSILON = 1.0E-4; private static final double TWO_PI = Math.PI * 2.0; + private static final Vec3 UP = new Vec3(0, 1, 0); /** * Offsets from the root block position to the center of each side. @@ -373,12 +378,14 @@ public static final class RotationArc { private final Rotation startRotation; private final Rotation targetRotation; private final int length; + private final boolean latitudeArc; private int stage; public RotationArc(Rotation startRotation, Rotation targetRotation, int length) { this.startRotation = startRotation; this.targetRotation = targetRotation; this.length = Math.max(1, length); + this.latitudeArc = shouldUseLatitudeArc(startRotation, targetRotation); } public static RotationArc fromAngularSpeed( @@ -422,6 +429,9 @@ public Rotation arcAt(double t) { if (source.distanceToSqr(target) < 1.0E-8) { return this.targetRotation; } + if (this.latitudeArc) { + return latitudeArcAt(this.startRotation, this.targetRotation, t); + } return calcRotationFromVec3d( Vec3.ZERO, alerp(source, target, Vec3.ZERO, t), @@ -433,16 +443,99 @@ private static int ticksForAngularSpeed(Rotation startRotation, Rotation targetR return 1; } - Vec3 source = calcLookDirectionFromRotation(startRotation); - Vec3 target = calcLookDirectionFromRotation(targetRotation); - double dot = clamp(source.dot(target), -1, 1); - double angularDistance = Math.toDegrees(Math.acos(dot)); + double angularDistance = angularDistance(startRotation, targetRotation); if (angularDistance < ARC_EPSILON) { return 1; } return Math.max(1, (int) Math.ceil(angularDistance / degreesPerTick)); } + + private static double angularDistance(Rotation startRotation, Rotation targetRotation) { + if (shouldUseLatitudeArc(startRotation, targetRotation)) { + return latitudeAngularDistance(startRotation, targetRotation); + } + + Vec3 source = calcLookDirectionFromRotation(startRotation); + Vec3 target = calcLookDirectionFromRotation(targetRotation); + return Math.toDegrees(angularDistance(source, target)); + } + + private static double latitudeAngularDistance(Rotation startRotation, Rotation targetRotation) { + double yawDelta = Rotation.normalizeYaw(targetRotation.getYaw() - startRotation.getYaw()); + double pitchDelta = targetRotation.getPitch() - startRotation.getPitch(); + int segments = Math.max(1, (int) Math.ceil( + Math.max(Math.abs(yawDelta), Math.abs(pitchDelta)) / LATITUDE_DISTANCE_STEP)); + + Vec3 previous = calcLookDirectionFromRotation(startRotation); + double distance = 0; + for (int i = 1; i <= segments; i++) { + Vec3 current = calcLookDirectionFromRotation( + latitudeArcAt(startRotation, targetRotation, i / (double) segments)); + distance += angularDistance(previous, current); + previous = current; + } + return Math.toDegrees(distance); + } + + private static Rotation latitudeArcAt(Rotation startRotation, Rotation targetRotation, double t) { + double yawDelta = Rotation.normalizeYaw(targetRotation.getYaw() - startRotation.getYaw()); + double pitchDelta = targetRotation.getPitch() - startRotation.getPitch(); + return new Rotation( + (float) (startRotation.getYaw() + yawDelta * t), + (float) (startRotation.getPitch() + pitchDelta * t) + ).clamp(); + } + + private static boolean shouldUseLatitudeArc(Rotation startRotation, Rotation targetRotation) { + Vec3 source = calcLookDirectionFromRotation(startRotation); + Vec3 target = calcLookDirectionFromRotation(targetRotation); + if (source.distanceToSqr(target) < ARC_EPSILON) { + return false; + } + + // With roll locked, yaw becomes unstable when a great-circle arc crosses a view pole. + double endpointPitch = Math.max( + Math.abs(startRotation.getPitch()), + Math.abs(targetRotation.getPitch())); + double polePitch = maxPolePitchOnGreatCircle(source, target); + return polePitch >= POLE_AVOIDANCE_PITCH + && polePitch > endpointPitch + POLE_AVOIDANCE_MARGIN; + } + + private static double maxPolePitchOnGreatCircle(Vec3 source, Vec3 target) { + double maxY = Math.max(Math.abs(source.y), Math.abs(target.y)); + Vec3 normal = source.cross(target); + if (normal.lengthSqr() < ARC_EPSILON) { + return Math.toDegrees(Math.asin(clamp(maxY, -1, 1))); + } + + normal = normal.normalize(); + Vec3 poleProjection = UP.subtract(normal.scale(UP.dot(normal))); + if (poleProjection.lengthSqr() < ARC_EPSILON) { + return Math.toDegrees(Math.asin(clamp(maxY, -1, 1))); + } + + Vec3 poleward = poleProjection.normalize(); + if (isOnMinorArc(source, target, poleward)) { + maxY = Math.max(maxY, Math.abs(poleward.y)); + } + Vec3 oppositePoleward = poleward.scale(-1); + if (isOnMinorArc(source, target, oppositePoleward)) { + maxY = Math.max(maxY, Math.abs(oppositePoleward.y)); + } + return Math.toDegrees(Math.asin(clamp(maxY, -1, 1))); + } + + private static boolean isOnMinorArc(Vec3 source, Vec3 target, Vec3 sample) { + double total = angularDistance(source, target); + double split = angularDistance(source, sample) + angularDistance(sample, target); + return Math.abs(split - total) < MINOR_ARC_EPSILON; + } + + private static double angularDistance(Vec3 source, Vec3 target) { + return Math.acos(clamp(source.dot(target), -1, 1)); + } } @Deprecated diff --git a/src/test/java/baritone/utils/RotationUtilsTest.java b/src/test/java/baritone/utils/RotationUtilsTest.java index f385014b8..a65d3fc01 100644 --- a/src/test/java/baritone/utils/RotationUtilsTest.java +++ b/src/test/java/baritone/utils/RotationUtilsTest.java @@ -70,6 +70,14 @@ public void testRotationArcUsesAngularSpeed() { countTicks(RotationArc.fromAngularSpeed(new Rotation(0, 0), new Rotation(91, 0), 30))); } + @Test + public void testRotationArcAvoidsViewPole() { + RotationArc arc = new RotationArc(new Rotation(0, 80), new Rotation(180, 80), 10); + + assertEquals(90, arc.arcAt(0.5).getYaw(), 1.0E-4); + assertEquals(80, arc.arcAt(0.5).getPitch(), 1.0E-4); + } + private static void assertVecEquals(Vec3 expected, Vec3 actual) { assertEquals(expected.x, actual.x, EPSILON); assertEquals(expected.y, actual.y, EPSILON); From 6ef8742d827fcfea119ff1b0ddd105b6e72b0d6c Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 11:38:42 -0500 Subject: [PATCH 16/26] Keep interpolated server arcs silent --- .../java/baritone/behavior/LookBehavior.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index f6189de21..639102571 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -187,9 +187,12 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { switch (event.getState()) { case PRE: { if (this.target.mode == Target.Mode.NONE) { - this.pendingRenderArc = null; + this.clearRenderArc(); return; } + if (this.target.mode == Target.Mode.SERVER) { + this.clearRenderArc(); + } this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); final Rotation start = ctx.playerRotations(); @@ -199,7 +202,12 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { } case POST: { if (this.prevRotation != null && this.previousArcSample != null && this.currentArcSample != null) { - this.applyVisualArc(); + if (this.target.mode == Target.Mode.SERVER) { + this.clearRenderArc(); + this.applyRotation(this.prevRotation); + } else { + this.applyVisualArc(); + } this.previousArcSample = null; this.currentArcSample = null; } else if (this.prevRotation != null && this.target.mode == Target.Mode.SERVER) { @@ -259,6 +267,13 @@ private void applyVisualArc() { this.applyRotation(this.currentArcSample); } + private void clearRenderArc() { + this.renderArc = null; + this.pendingRenderArc = null; + this.cachedVisualPartialTicks = Float.NaN; + this.cachedVisualRotation = null; + } + public Rotation getVisualRotation(float partialTicks) { if (this.renderArc == null) { return null; From 1fb8cef31d165657b98adc1cd3633730d1bf72f7 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 11:57:41 -0500 Subject: [PATCH 17/26] Guard block interactions by reachability --- .../api/utils/IInputOverrideHandler.java | 5 +++ .../baritone/api/utils/IPlayerContext.java | 16 +++++++-- .../baritone/pathing/movement/Movement.java | 13 +++++-- .../pathing/movement/MovementHelper.java | 8 +++-- .../pathing/movement/MovementState.java | 34 +++++++++++++++++++ .../baritone/process/BackfillProcess.java | 3 ++ .../java/baritone/process/BuilderProcess.java | 5 ++- .../java/baritone/process/MineProcess.java | 2 ++ .../java/baritone/utils/BlockBreakHelper.java | 6 +++- .../java/baritone/utils/BlockPlaceHelper.java | 10 ++++-- .../baritone/utils/InputOverrideHandler.java | 22 ++++++++++-- 11 files changed, 110 insertions(+), 14 deletions(-) diff --git a/src/api/java/baritone/api/utils/IInputOverrideHandler.java b/src/api/java/baritone/api/utils/IInputOverrideHandler.java index 9bd4aa04d..d6c423863 100644 --- a/src/api/java/baritone/api/utils/IInputOverrideHandler.java +++ b/src/api/java/baritone/api/utils/IInputOverrideHandler.java @@ -20,6 +20,7 @@ import baritone.api.behavior.IBehavior; import baritone.api.utils.input.Input; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; /** * @author Brady @@ -36,4 +37,8 @@ public interface IInputOverrideHandler extends IBehavior { default boolean isBreakingBlock(BlockPos pos) { return false; } + + default void setBlockBreakTarget(BlockPos pos) {} + + default void setBlockPlaceTarget(BlockPos pos, Direction side) {} } diff --git a/src/api/java/baritone/api/utils/IPlayerContext.java b/src/api/java/baritone/api/utils/IPlayerContext.java index de6d7cd58..8d4514ccf 100644 --- a/src/api/java/baritone/api/utils/IPlayerContext.java +++ b/src/api/java/baritone/api/utils/IPlayerContext.java @@ -22,6 +22,7 @@ import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.SlabBlock; @@ -116,15 +117,26 @@ static double eyeHeight(boolean ifSneaking) { * * @return The position of the highlighted block */ - default Optional getSelectedBlock() { + default Optional getSelectedBlockHitResult() { HitResult result = objectMouseOver(); if (result != null && result.getType() == HitResult.Type.BLOCK) { - return Optional.of(((BlockHitResult) result).getBlockPos()); + return Optional.of((BlockHitResult) result); } return Optional.empty(); } + default Optional getSelectedBlock() { + return getSelectedBlockHitResult().map(BlockHitResult::getBlockPos); + } + default boolean isLookingAt(BlockPos pos) { return getSelectedBlock().equals(Optional.of(pos)); } + + default boolean isLookingAt(BlockPos pos, Direction side) { + return getSelectedBlockHitResult() + .filter(hit -> hit.getBlockPos().equals(pos)) + .filter(hit -> hit.getDirection() == side) + .isPresent(); + } } diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index 366f6101c..1ec8b1938 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -140,10 +140,15 @@ public MovementStatus update() { target.shouldRestartInterpolation(), target.isExactRotation())); baritone.getInputOverrideHandler().clearAllKeys(); + currentState.getBlockBreakTarget().ifPresent(baritone.getInputOverrideHandler()::setBlockBreakTarget); + currentState.getBlockPlaceTarget().ifPresent(pos -> + currentState.getBlockPlaceSide().ifPresent(side -> + baritone.getInputOverrideHandler().setBlockPlaceTarget(pos, side))); currentState.getInputStates().forEach((input, forced) -> { baritone.getInputOverrideHandler().setInputForceState(input, forced); }); currentState.getInputStates().clear(); + currentState.clearBlockInteractionTargets(); // If the current status indicates a completed movement if (currentState.getStatus().isComplete()) { @@ -166,8 +171,9 @@ protected boolean prepared(MovementState state) { somethingInTheWay = true; MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, blockPos)); if (baritone.getInputOverrideHandler().isBreakingBlock(blockPos)) { - state.setTarget(new MovementState.MovementTarget(ctx.playerRotations(), true, true, true)); - state.setInput(Input.CLICK_LEFT, true); + state.setTarget(new MovementState.MovementTarget(ctx.playerRotations(), true, true, true)) + .setBlockBreakTarget(blockPos) + .setInput(Input.CLICK_LEFT, true); return false; } Optional reachable = RotationUtils.reachable(ctx, blockPos, ctx.playerController().getBlockReachDistance()); @@ -175,7 +181,8 @@ protected boolean prepared(MovementState state) { Rotation rotTowardsBlock = reachable.get(); state.setTarget(new MovementState.MovementTarget(rotTowardsBlock, true)); if (ctx.isLookingAt(blockPos)) { - state.setInput(Input.CLICK_LEFT, true); + state.setBlockBreakTarget(blockPos) + .setInput(Input.CLICK_LEFT, true); } return false; } diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index 8b513f143..fd071daa4 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -821,14 +821,16 @@ static PlaceResult attemptToPlaceABlock(MovementState state, IBaritone baritone, } } } - if (ctx.getSelectedBlock().isPresent()) { - BlockPos selectedBlock = ctx.getSelectedBlock().get(); - Direction side = ((BlockHitResult) ctx.objectMouseOver()).getDirection(); + Optional selected = ctx.getSelectedBlockHitResult(); + if (selected.isPresent()) { + BlockPos selectedBlock = selected.get().getBlockPos(); + Direction side = selected.get().getDirection(); // only way for selectedBlock.equals(placeAt) to be true is if it's replaceable if (selectedBlock.equals(placeAt) || (MovementHelper.canPlaceAgainst(ctx, selectedBlock) && selectedBlock.relative(side).equals(placeAt))) { if (wouldSneak) { state.setInput(Input.SNEAK, true); } + state.setBlockPlaceTarget(selectedBlock, side); ((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(true, placeAt.getX(), placeAt.getY(), placeAt.getZ()); return PlaceResult.READY_TO_PLACE; } diff --git a/src/main/java/baritone/pathing/movement/MovementState.java b/src/main/java/baritone/pathing/movement/MovementState.java index 62e884794..768b60ee1 100644 --- a/src/main/java/baritone/pathing/movement/MovementState.java +++ b/src/main/java/baritone/pathing/movement/MovementState.java @@ -20,6 +20,8 @@ import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.Rotation; import baritone.api.utils.input.Input; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import java.util.HashMap; import java.util.Map; @@ -30,6 +32,9 @@ public class MovementState { private MovementStatus status; private MovementTarget target = new MovementTarget(); private final Map inputState = new HashMap<>(); + private BlockPos blockBreakTarget; + private BlockPos blockPlaceTarget; + private Direction blockPlaceSide; public MovementState setStatus(MovementStatus status) { this.status = status; @@ -58,6 +63,35 @@ public Map getInputStates() { return this.inputState; } + public MovementState setBlockBreakTarget(BlockPos pos) { + this.blockBreakTarget = pos; + return this; + } + + public Optional getBlockBreakTarget() { + return Optional.ofNullable(this.blockBreakTarget); + } + + public MovementState setBlockPlaceTarget(BlockPos pos, Direction side) { + this.blockPlaceTarget = pos; + this.blockPlaceSide = side; + return this; + } + + public Optional getBlockPlaceTarget() { + return Optional.ofNullable(this.blockPlaceTarget); + } + + public Optional getBlockPlaceSide() { + return Optional.ofNullable(this.blockPlaceSide); + } + + public void clearBlockInteractionTargets() { + this.blockBreakTarget = null; + this.blockPlaceTarget = null; + this.blockPlaceSide = null; + } + public static class MovementTarget { /** diff --git a/src/main/java/baritone/process/BackfillProcess.java b/src/main/java/baritone/process/BackfillProcess.java index af191ae1e..5d129c47b 100644 --- a/src/main/java/baritone/process/BackfillProcess.java +++ b/src/main/java/baritone/process/BackfillProcess.java @@ -77,6 +77,9 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { case NO_OPTION: continue; case READY_TO_PLACE: + fake.getBlockPlaceTarget().ifPresent(pos -> + fake.getBlockPlaceSide().ifPresent(side -> + baritone.getInputOverrideHandler().setBlockPlaceTarget(pos, side))); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); case ATTEMPTING: diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index b0ef313f3..e95378cf9 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -559,11 +559,13 @@ public int lengthZ() { } if (baritone.getInputOverrideHandler().isBreakingBlock(pos)) { baritone.getLookBehavior().updateTarget(ctx.playerRotations(), true, true, true); + baritone.getInputOverrideHandler().setBlockBreakTarget(pos); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } else { baritone.getLookBehavior().updateTarget(rot, true); } if (ctx.isLookingAt(pos)) { + baritone.getInputOverrideHandler().setBlockBreakTarget(pos); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); @@ -575,7 +577,8 @@ public int lengthZ() { baritone.getLookBehavior().updateTarget(rot, true); ctx.player().getInventory().selected = toPlace.get().hotbarSelection; baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); - if ((ctx.isLookingAt(toPlace.get().placeAgainst) && ((BlockHitResult) ctx.objectMouseOver()).getDirection().equals(toPlace.get().side)) || ctx.playerRotations().isReallyCloseTo(rot)) { + baritone.getInputOverrideHandler().setBlockPlaceTarget(toPlace.get().placeAgainst, toPlace.get().side); + if (ctx.isLookingAt(toPlace.get().placeAgainst, toPlace.get().side)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); } return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index b62b6705f..97006adc8 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -127,6 +127,7 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); if (isSafeToCancel && baritone.getInputOverrideHandler().isBreakingBlock(pos)) { baritone.getLookBehavior().updateTarget(ctx.playerRotations(), true, true, true); + baritone.getInputOverrideHandler().setBlockBreakTarget(pos); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } @@ -134,6 +135,7 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (rot.isPresent() && isSafeToCancel) { baritone.getLookBehavior().updateTarget(rot.get(), true); if (ctx.isLookingAt(pos)) { + baritone.getInputOverrideHandler().setBlockBreakTarget(pos); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); diff --git a/src/main/java/baritone/utils/BlockBreakHelper.java b/src/main/java/baritone/utils/BlockBreakHelper.java index 467d987c4..5b458a96f 100644 --- a/src/main/java/baritone/utils/BlockBreakHelper.java +++ b/src/main/java/baritone/utils/BlockBreakHelper.java @@ -55,13 +55,17 @@ public boolean isBreakingBlock(BlockPos pos) { return wasHitting && pos.equals(breakingBlock) && ctx.isLookingAt(pos); } - public void tick(boolean isLeftClick) { + public void tick(boolean isLeftClick, BlockPos target) { if (breakDelayTimer > 0) { breakDelayTimer--; return; } HitResult trace = ctx.objectMouseOver(); boolean isBlockTrace = trace != null && trace.getType() == HitResult.Type.BLOCK; + if (target != null && (!isBlockTrace || !((BlockHitResult) trace).getBlockPos().equals(target))) { + clearBreakingBlock(); + return; + } if (isLeftClick && isBlockTrace) { BlockHitResult blockTrace = (BlockHitResult) trace; diff --git a/src/main/java/baritone/utils/BlockPlaceHelper.java b/src/main/java/baritone/utils/BlockPlaceHelper.java index ca8d50523..53efcabff 100644 --- a/src/main/java/baritone/utils/BlockPlaceHelper.java +++ b/src/main/java/baritone/utils/BlockPlaceHelper.java @@ -19,6 +19,8 @@ import baritone.Baritone; import baritone.api.utils.IPlayerContext; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.phys.BlockHitResult; @@ -35,7 +37,7 @@ public class BlockPlaceHelper { this.ctx = playerContext; } - public void tick(boolean rightClickRequested) { + public void tick(boolean rightClickRequested, BlockPos target, Direction side) { if (rightClickTimer > 0) { rightClickTimer--; return; @@ -44,9 +46,13 @@ public void tick(boolean rightClickRequested) { if (!rightClickRequested || ctx.player().isHandsBusy() || mouseOver == null || mouseOver.getType() != HitResult.Type.BLOCK) { return; } + BlockHitResult blockHit = (BlockHitResult) mouseOver; + if (target != null && (!blockHit.getBlockPos().equals(target) || blockHit.getDirection() != side)) { + return; + } rightClickTimer = Baritone.settings().rightClickSpeed.value - BASE_PLACE_DELAY; for (InteractionHand hand : InteractionHand.values()) { - if (ctx.playerController().processRightClickBlock(ctx.player(), ctx.world(), hand, (BlockHitResult) mouseOver) == InteractionResult.SUCCESS) { + if (ctx.playerController().processRightClickBlock(ctx.player(), ctx.world(), hand, blockHit) == InteractionResult.SUCCESS) { ctx.player().swing(hand); return; } diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index 320f8fcf3..3c99ff92f 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -25,6 +25,7 @@ import baritone.behavior.Behavior; import net.minecraft.client.player.KeyboardInput; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import java.util.HashMap; import java.util.Map; @@ -46,6 +47,9 @@ public final class InputOverrideHandler extends Behavior implements IInputOverri private final BlockBreakHelper blockBreakHelper; private final BlockPlaceHelper blockPlaceHelper; + private BlockPos blockBreakTarget; + private BlockPos blockPlaceTarget; + private Direction blockPlaceSide; public InputOverrideHandler(Baritone baritone) { super(baritone); @@ -81,6 +85,9 @@ public final void setInputForceState(Input input, boolean forced) { @Override public final void clearAllKeys() { this.inputForceStateMap.clear(); + this.blockBreakTarget = null; + this.blockPlaceTarget = null; + this.blockPlaceSide = null; } @Override @@ -88,6 +95,17 @@ public boolean isBreakingBlock(BlockPos pos) { return this.blockBreakHelper.isBreakingBlock(pos); } + @Override + public void setBlockBreakTarget(BlockPos pos) { + this.blockBreakTarget = pos; + } + + @Override + public void setBlockPlaceTarget(BlockPos pos, Direction side) { + this.blockPlaceTarget = pos; + this.blockPlaceSide = side; + } + @Override public final void onTick(TickEvent event) { if (event.getType() == TickEvent.Type.OUT) { @@ -96,8 +114,8 @@ public final void onTick(TickEvent event) { if (isInputForcedDown(Input.CLICK_LEFT)) { setInputForceState(Input.CLICK_RIGHT, false); } - blockBreakHelper.tick(isInputForcedDown(Input.CLICK_LEFT)); - blockPlaceHelper.tick(isInputForcedDown(Input.CLICK_RIGHT)); + blockBreakHelper.tick(isInputForcedDown(Input.CLICK_LEFT), blockBreakTarget); + blockPlaceHelper.tick(isInputForcedDown(Input.CLICK_RIGHT), blockPlaceTarget, blockPlaceSide); if (inControl()) { if (ctx.player().input.getClass() != PlayerMovementInput.class) { From 4a90d17b16e961dbed3127cfd87c1287db1eb66e Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 12:12:36 -0500 Subject: [PATCH 18/26] Drop stale interpolation arcs --- src/main/java/baritone/behavior/LookBehavior.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 639102571..aa571cfbb 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -117,9 +117,7 @@ public void onPlayerUpdate(PlayerUpdateEvent event) { if (this.target == null) { if (event.getState() == EventState.PRE) { - this.renderArc = null; - this.cachedVisualPartialTicks = Float.NaN; - this.cachedVisualRotation = null; + this.resetInterpolation(); } return; } @@ -187,7 +185,7 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { switch (event.getState()) { case PRE: { if (this.target.mode == Target.Mode.NONE) { - this.clearRenderArc(); + this.resetInterpolation(); return; } if (this.target.mode == Target.Mode.SERVER) { From 5ff3dc6f836b30e4748652acb693ed2f94c4e5f7 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 12:20:28 -0500 Subject: [PATCH 19/26] Restart arcs when target visibility changes --- .../java/baritone/behavior/LookBehavior.java | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index aa571cfbb..944dccd73 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -66,6 +66,7 @@ public final class LookBehavior extends Behavior implements ILookBehavior { private final Deque smoothYawBuffer; private final Deque smoothPitchBuffer; private RotationArc interpolationArc; + private Target.Mode interpolationMode; private Vec3 interpolationOrigin; private RotationArc pendingRenderArc; private RotationArc renderArc; @@ -193,9 +194,9 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { } this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); - final Rotation start = ctx.playerRotations(); + final Rotation start = this.startingRotation(this.target.mode); final Rotation actual = this.target.rotation(this.processor); - this.updateInterpolation(start, actual, ctx.playerHead(), this.target.restartInterpolation); + this.updateInterpolation(start, actual, ctx.playerHead(), this.target.restartInterpolation, this.target.mode); break; } case POST: { @@ -221,9 +222,27 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { } } - private void updateInterpolation(Rotation start, Rotation actual, Vec3 origin, boolean restartArc) { + private Rotation startingRotation(Target.Mode mode) { + if (mode == Target.Mode.CLIENT) { + return this.prevRotation; + } + return ctx.playerRotations(); + } + + private void updateInterpolation( + Rotation start, + Rotation actual, + Vec3 origin, + boolean restartArc, + Target.Mode mode) { final int interpolationSpeed = Baritone.settings().interpolatedLookLength.value; + if (this.interpolationArc != null && this.interpolationMode != mode) { + this.interpolationArc = null; + this.interpolationOrigin = null; + this.pendingRenderArc = null; + } + if (this.interpolationArc != null && (restartArc || this.interpolationOriginMoved(origin))) { start = this.interpolationArc.getCurrentRotation(); this.interpolationArc = null; @@ -231,6 +250,7 @@ private void updateInterpolation(Rotation start, Rotation actual, Vec3 origin, b if (this.interpolationArc == null) { this.interpolationArc = RotationArc.fromAngularSpeed(start, actual, interpolationSpeed); + this.interpolationMode = mode; this.interpolationOrigin = origin; } @@ -285,6 +305,7 @@ public Rotation getVisualRotation(float partialTicks) { private void resetInterpolation() { this.interpolationArc = null; + this.interpolationMode = null; this.interpolationOrigin = null; this.pendingRenderArc = null; this.renderArc = null; From 71451e0b54b40a346837ea0467b27b6d9356244e Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 12:39:21 -0500 Subject: [PATCH 20/26] Pause actions during pause and death --- .../launch/mixins/MixinMinecraft.java | 5 ++- .../baritone/behavior/PathingBehavior.java | 9 ++++ .../baritone/utils/InputOverrideHandler.java | 23 +++++++++++ .../baritone/utils/PathingControlManager.java | 8 ++++ .../baritone/utils/PlayerControlGuard.java | 41 +++++++++++++++++++ 5 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/main/java/baritone/utils/PlayerControlGuard.java diff --git a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java index 4f9925cc7..a39e06a76 100644 --- a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java +++ b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java @@ -23,6 +23,7 @@ import baritone.api.event.events.TickEvent; import baritone.api.event.events.WorldEvent; import baritone.api.event.events.type.EventState; +import baritone.utils.PlayerControlGuard; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.multiplayer.ClientLevel; @@ -172,7 +173,9 @@ private void postLoadWorld(ClientLevel world, CallbackInfo ci) { ) private boolean passEvents(Screen screen) { // allow user input is only the primary baritone - return (BaritoneAPI.getProvider().getPrimaryBaritone().getPathingBehavior().isPathing() && player != null) || screen.passEvents; + return (BaritoneAPI.getProvider().getPrimaryBaritone().getPathingBehavior().isPathing() + && PlayerControlGuard.canControl(player, screen)) + || screen.passEvents; } // TODO diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index d8df46681..a933f55b5 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -36,6 +36,7 @@ import baritone.process.ElytraProcess; import baritone.utils.PathRenderer; import baritone.utils.PathingCommandContext; +import baritone.utils.PlayerControlGuard; import baritone.utils.pathing.Favoring; import java.util.ArrayList; import java.util.Comparator; @@ -100,6 +101,14 @@ public void onTick(TickEvent event) { return; } + if (!PlayerControlGuard.canControl(ctx)) { + pausedThisTick = true; + unpausedLastTick = false; + baritone.getInputOverrideHandler().clearAllKeys(); + baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); + baritone.getPathingControlManager().preTick(); + return; + } expectedSegmentStart = pathStart(); baritone.getPathingControlManager().preTick(); tickPath(); diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index 3c99ff92f..bd021d59a 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -24,6 +24,7 @@ import baritone.api.utils.input.Input; import baritone.behavior.Behavior; import net.minecraft.client.player.KeyboardInput; +import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -111,6 +112,10 @@ public final void onTick(TickEvent event) { if (event.getType() == TickEvent.Type.OUT) { return; } + if (!PlayerControlGuard.canControl(ctx)) { + stopControllingPlayer(); + return; + } if (isInputForcedDown(Input.CLICK_LEFT)) { setInputForceState(Input.CLICK_RIGHT, false); } @@ -130,6 +135,24 @@ public final void onTick(TickEvent event) { // gotta do it this way, or else it constantly thinks you're beginning a double tap W sprint lol } + @Override + public void onPlayerDeath() { + stopControllingPlayer(); + } + + private void stopControllingPlayer() { + clearAllKeys(); + blockBreakHelper.stopBreakingBlock(); + restorePlayerInput(); + } + + private void restorePlayerInput() { + LocalPlayer player = ctx.player(); + if (player != null && player.input != null && player.input.getClass() == PlayerMovementInput.class) { + player.input = new KeyboardInput(ctx.minecraft().options); + } + } + private boolean inControl() { for (Input input : new Input[]{Input.MOVE_FORWARD, Input.MOVE_BACK, Input.MOVE_LEFT, Input.MOVE_RIGHT, Input.SNEAK, Input.JUMP}) { if (isInputForcedDown(input)) { diff --git a/src/main/java/baritone/utils/PathingControlManager.java b/src/main/java/baritone/utils/PathingControlManager.java index 2205d62e7..1d86ac1d9 100644 --- a/src/main/java/baritone/utils/PathingControlManager.java +++ b/src/main/java/baritone/utils/PathingControlManager.java @@ -86,6 +86,10 @@ public Optional mostRecentCommand() { public void preTick() { inControlLastTick = inControlThisTick; inControlThisTick = null; + if (!PlayerControlGuard.canControl(baritone.getPlayerContext())) { + command = null; + return; + } PathingBehavior p = baritone.getPathingBehavior(); command = executeProcesses(); if (command == null) { @@ -126,6 +130,10 @@ public void preTick() { } private void postTick() { + if (!PlayerControlGuard.canControl(baritone.getPlayerContext())) { + command = null; + return; + } // 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 diff --git a/src/main/java/baritone/utils/PlayerControlGuard.java b/src/main/java/baritone/utils/PlayerControlGuard.java new file mode 100644 index 000000000..4a73ec2cb --- /dev/null +++ b/src/main/java/baritone/utils/PlayerControlGuard.java @@ -0,0 +1,41 @@ +/* + * 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; + +import baritone.api.utils.IPlayerContext; +import net.minecraft.client.gui.screens.DeathScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.player.LocalPlayer; + +public final class PlayerControlGuard { + + private PlayerControlGuard() {} + + public static boolean canControl(IPlayerContext ctx) { + return canControl(ctx.player(), ctx.minecraft().screen) + && ctx.world() != null + && !ctx.minecraft().isPaused(); + } + + public static boolean canControl(LocalPlayer player, Screen screen) { + if (player == null || !player.isAlive()) { + return false; + } + return screen == null || (!screen.isPauseScreen() && !(screen instanceof DeathScreen)); + } +} From 5a48840c274a7f9d26bdd74c79916b94affa9d2e Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 14:19:57 -0500 Subject: [PATCH 21/26] Clean up interpolated look style --- .../baritone/api/behavior/ILookBehavior.java | 17 +++++++--- .../api/utils/IInputOverrideHandler.java | 6 ++-- .../baritone/api/utils/RotationUtils.java | 30 +++++++++++++----- .../mixins/MixinClientPlayerEntity.java | 3 +- .../java/baritone/behavior/LookBehavior.java | 31 +++++++++++++++---- .../baritone/pathing/movement/Movement.java | 14 +++++++-- .../pathing/movement/MovementState.java | 8 +++-- .../baritone/process/BackfillProcess.java | 3 +- .../java/baritone/process/BuilderProcess.java | 4 ++- .../java/baritone/process/MineProcess.java | 6 +++- .../java/baritone/utils/BlockBreakHelper.java | 11 +++++-- .../java/baritone/utils/BlockPlaceHelper.java | 20 +++++++++--- .../baritone/utils/InputOverrideHandler.java | 9 ++++-- .../baritone/utils/PlayerControlGuard.java | 3 +- .../baritone/utils/RotationUtilsTest.java | 15 +++++++-- 15 files changed, 137 insertions(+), 43 deletions(-) diff --git a/src/api/java/baritone/api/behavior/ILookBehavior.java b/src/api/java/baritone/api/behavior/ILookBehavior.java index 71d2855db..b6c7d55f4 100644 --- a/src/api/java/baritone/api/behavior/ILookBehavior.java +++ b/src/api/java/baritone/api/behavior/ILookBehavior.java @@ -40,23 +40,30 @@ public interface ILookBehavior extends IBehavior { void updateTarget(Rotation rotation, boolean blockInteract); /** - * Updates the current {@link ILookBehavior} target and optionally restarts any active interpolation. + * Updates the current {@link ILookBehavior} target and optionally restarts + * any active interpolation. * * @param rotation The target rotations * @param blockInteract Whether the target rotations are needed for a block interaction - * @param restartInterpolation Whether active interpolation should restart from its current sample + * @param restartInterpolation Whether active interpolation should restart + * from its current sample * @see #updateTarget(Rotation, boolean) */ - default void updateTarget(Rotation rotation, boolean blockInteract, boolean restartInterpolation) { + default void updateTarget( + Rotation rotation, + boolean blockInteract, + boolean restartInterpolation) { updateTarget(rotation, blockInteract); } /** - * Updates the current {@link ILookBehavior} target and optionally uses it without aim processing. + * Updates the current {@link ILookBehavior} target and optionally uses it + * without aim processing. * * @param rotation The target rotations * @param blockInteract Whether the target rotations are needed for a block interaction - * @param restartInterpolation Whether active interpolation should restart from its current sample + * @param restartInterpolation Whether active interpolation should restart + * from its current sample * @param exactRotation Whether the target rotation should skip aim processing * @see #updateTarget(Rotation, boolean, boolean) */ diff --git a/src/api/java/baritone/api/utils/IInputOverrideHandler.java b/src/api/java/baritone/api/utils/IInputOverrideHandler.java index d6c423863..0f33d698e 100644 --- a/src/api/java/baritone/api/utils/IInputOverrideHandler.java +++ b/src/api/java/baritone/api/utils/IInputOverrideHandler.java @@ -38,7 +38,9 @@ default boolean isBreakingBlock(BlockPos pos) { return false; } - default void setBlockBreakTarget(BlockPos pos) {} + default void setBlockBreakTarget(BlockPos pos) { + } - default void setBlockPlaceTarget(BlockPos pos, Direction side) {} + default void setBlockPlaceTarget(BlockPos pos, Direction side) { + } } diff --git a/src/api/java/baritone/api/utils/RotationUtils.java b/src/api/java/baritone/api/utils/RotationUtils.java index cce77379d..c9c47af1f 100644 --- a/src/api/java/baritone/api/utils/RotationUtils.java +++ b/src/api/java/baritone/api/utils/RotationUtils.java @@ -317,7 +317,8 @@ public static Vec3 alerp(Vec3 start, Vec3 end, Vec3 origin, double t) { centerToEnd.dot(toCenter), centerToEnd.dot(pathDirection)); double angle = angleStart + wrapRadians(angleEnd - angleStart) * t; - Vec3 arcDirection = pathDirection.scale(Math.cos(angle)).add(toCenter.scale(Math.sin(angle))); + Vec3 arcDirection = pathDirection.scale(Math.cos(angle)) + .add(toCenter.scale(Math.sin(angle))); return center.add(arcDirection.scale(radius)); } @@ -343,7 +344,8 @@ private static Vec3 colinearAlerp( double endRadius = endOffset.length(); double radius = startRadius + (endRadius - startRadius) * t; double angle = Math.PI * t; - Vec3 arcDirection = startDirection.scale(Math.cos(angle)).add(perpendicular.scale(Math.sin(angle))); + Vec3 arcDirection = startDirection.scale(Math.cos(angle)) + .add(perpendicular.scale(Math.sin(angle))); return origin.add(arcDirection.scale(radius)); } @@ -438,7 +440,10 @@ public Rotation arcAt(double t) { this.startRotation); } - private static int ticksForAngularSpeed(Rotation startRotation, Rotation targetRotation, double degreesPerTick) { + private static int ticksForAngularSpeed( + Rotation startRotation, + Rotation targetRotation, + double degreesPerTick) { if (degreesPerTick <= ARC_EPSILON) { return 1; } @@ -461,8 +466,11 @@ private static double angularDistance(Rotation startRotation, Rotation targetRot return Math.toDegrees(angularDistance(source, target)); } - private static double latitudeAngularDistance(Rotation startRotation, Rotation targetRotation) { - double yawDelta = Rotation.normalizeYaw(targetRotation.getYaw() - startRotation.getYaw()); + private static double latitudeAngularDistance( + Rotation startRotation, + Rotation targetRotation) { + double yawDelta = Rotation.normalizeYaw( + targetRotation.getYaw() - startRotation.getYaw()); double pitchDelta = targetRotation.getPitch() - startRotation.getPitch(); int segments = Math.max(1, (int) Math.ceil( Math.max(Math.abs(yawDelta), Math.abs(pitchDelta)) / LATITUDE_DISTANCE_STEP)); @@ -478,8 +486,12 @@ private static double latitudeAngularDistance(Rotation startRotation, Rotation t return Math.toDegrees(distance); } - private static Rotation latitudeArcAt(Rotation startRotation, Rotation targetRotation, double t) { - double yawDelta = Rotation.normalizeYaw(targetRotation.getYaw() - startRotation.getYaw()); + private static Rotation latitudeArcAt( + Rotation startRotation, + Rotation targetRotation, + double t) { + double yawDelta = Rotation.normalizeYaw( + targetRotation.getYaw() - startRotation.getYaw()); double pitchDelta = targetRotation.getPitch() - startRotation.getPitch(); return new Rotation( (float) (startRotation.getYaw() + yawDelta * t), @@ -487,7 +499,9 @@ private static Rotation latitudeArcAt(Rotation startRotation, Rotation targetRot ).clamp(); } - private static boolean shouldUseLatitudeArc(Rotation startRotation, Rotation targetRotation) { + private static boolean shouldUseLatitudeArc( + Rotation startRotation, + Rotation targetRotation) { Vec3 source = calcLookDirectionFromRotation(startRotation); Vec3 target = calcLookDirectionFromRotation(targetRotation); if (source.distanceToSqr(target) < ARC_EPSILON) { diff --git a/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java b/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java index 9a5b0d847..b24c631cc 100644 --- a/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java +++ b/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java @@ -150,7 +150,8 @@ private boolean tryToStartFallFlying(final LocalPlayer instance) { @Unique private Rotation getVisualRotation(float partialTicks) { - IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((LocalPlayer) (Object) this); + IBaritone baritone = BaritoneAPI.getProvider() + .getBaritoneForPlayer((LocalPlayer) (Object) this); if (baritone == null) { return null; } diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index 944dccd73..c72963a2f 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -88,7 +88,10 @@ public void updateTarget(Rotation rotation, boolean blockInteract) { } @Override - public void updateTarget(Rotation rotation, boolean blockInteract, boolean restartInterpolation) { + public void updateTarget( + Rotation rotation, + boolean blockInteract, + boolean restartInterpolation) { this.updateTarget(rotation, blockInteract, restartInterpolation, false); } @@ -98,7 +101,11 @@ public void updateTarget( boolean blockInteract, boolean restartInterpolation, boolean exactRotation) { - this.target = new Target(rotation, Target.Mode.resolve(ctx, blockInteract), restartInterpolation, exactRotation); + this.target = new Target( + rotation, + Target.Mode.resolve(ctx, blockInteract), + restartInterpolation, + exactRotation); } @Override @@ -196,11 +203,18 @@ private void updateInterpolatedTarget(PlayerUpdateEvent event) { this.prevRotation = new Rotation(ctx.player().getYRot(), ctx.player().getXRot()); final Rotation start = this.startingRotation(this.target.mode); final Rotation actual = this.target.rotation(this.processor); - this.updateInterpolation(start, actual, ctx.playerHead(), this.target.restartInterpolation, this.target.mode); + this.updateInterpolation( + start, + actual, + ctx.playerHead(), + this.target.restartInterpolation, + this.target.mode); break; } case POST: { - if (this.prevRotation != null && this.previousArcSample != null && this.currentArcSample != null) { + if (this.prevRotation != null + && this.previousArcSample != null + && this.currentArcSample != null) { if (this.target.mode == Target.Mode.SERVER) { this.clearRenderArc(); this.applyRotation(this.prevRotation); @@ -243,7 +257,8 @@ private void updateInterpolation( this.pendingRenderArc = null; } - if (this.interpolationArc != null && (restartArc || this.interpolationOriginMoved(origin))) { + if (this.interpolationArc != null + && (restartArc || this.interpolationOriginMoved(origin))) { start = this.interpolationArc.getCurrentRotation(); this.interpolationArc = null; } @@ -504,7 +519,11 @@ private static class Target { public final boolean restartInterpolation; public final boolean exactRotation; - public Target(Rotation rotation, Mode mode, boolean restartInterpolation, boolean exactRotation) { + public Target( + Rotation rotation, + Mode mode, + boolean restartInterpolation, + boolean exactRotation) { this.rotation = rotation; this.mode = mode; this.restartInterpolation = restartInterpolation; diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index 1ec8b1938..66214c6ef 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -140,7 +140,8 @@ public MovementStatus update() { target.shouldRestartInterpolation(), target.isExactRotation())); baritone.getInputOverrideHandler().clearAllKeys(); - currentState.getBlockBreakTarget().ifPresent(baritone.getInputOverrideHandler()::setBlockBreakTarget); + currentState.getBlockBreakTarget().ifPresent( + baritone.getInputOverrideHandler()::setBlockBreakTarget); currentState.getBlockPlaceTarget().ifPresent(pos -> currentState.getBlockPlaceSide().ifPresent(side -> baritone.getInputOverrideHandler().setBlockPlaceTarget(pos, side))); @@ -171,12 +172,19 @@ protected boolean prepared(MovementState state) { somethingInTheWay = true; MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, blockPos)); if (baritone.getInputOverrideHandler().isBreakingBlock(blockPos)) { - state.setTarget(new MovementState.MovementTarget(ctx.playerRotations(), true, true, true)) + state.setTarget(new MovementState.MovementTarget( + ctx.playerRotations(), + true, + true, + true)) .setBlockBreakTarget(blockPos) .setInput(Input.CLICK_LEFT, true); return false; } - Optional reachable = RotationUtils.reachable(ctx, blockPos, ctx.playerController().getBlockReachDistance()); + Optional reachable = RotationUtils.reachable( + ctx, + blockPos, + ctx.playerController().getBlockReachDistance()); if (reachable.isPresent()) { Rotation rotTowardsBlock = reachable.get(); state.setTarget(new MovementState.MovementTarget(rotTowardsBlock, true)); diff --git a/src/main/java/baritone/pathing/movement/MovementState.java b/src/main/java/baritone/pathing/movement/MovementState.java index 768b60ee1..da2ed9da9 100644 --- a/src/main/java/baritone/pathing/movement/MovementState.java +++ b/src/main/java/baritone/pathing/movement/MovementState.java @@ -102,7 +102,8 @@ public static class MovementTarget { /** * Whether or not this target must force rotations. *

- * {@code true} if we're trying to place or break blocks, {@code false} if we're trying to look at the movement location + * {@code true} if we're trying to place or break blocks, {@code false} + * if we're trying to look at the movement location */ private boolean forceRotations; @@ -117,7 +118,10 @@ public MovementTarget(Rotation rotation, boolean forceRotations) { this(rotation, forceRotations, false); } - public MovementTarget(Rotation rotation, boolean forceRotations, boolean restartInterpolation) { + public MovementTarget( + Rotation rotation, + boolean forceRotations, + boolean restartInterpolation) { this(rotation, forceRotations, restartInterpolation, false); } diff --git a/src/main/java/baritone/process/BackfillProcess.java b/src/main/java/baritone/process/BackfillProcess.java index 5d129c47b..a629bb411 100644 --- a/src/main/java/baritone/process/BackfillProcess.java +++ b/src/main/java/baritone/process/BackfillProcess.java @@ -79,7 +79,8 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { case READY_TO_PLACE: fake.getBlockPlaceTarget().ifPresent(pos -> fake.getBlockPlaceSide().ifPresent(side -> - baritone.getInputOverrideHandler().setBlockPlaceTarget(pos, side))); + baritone.getInputOverrideHandler() + .setBlockPlaceTarget(pos, side))); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); case ATTEMPTING: diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index e95378cf9..e01f78760 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -577,7 +577,9 @@ public int lengthZ() { baritone.getLookBehavior().updateTarget(rot, true); ctx.player().getInventory().selected = toPlace.get().hotbarSelection; baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); - baritone.getInputOverrideHandler().setBlockPlaceTarget(toPlace.get().placeAgainst, toPlace.get().side); + baritone.getInputOverrideHandler().setBlockPlaceTarget( + toPlace.get().placeAgainst, + toPlace.get().side); if (ctx.isLookingAt(toPlace.get().placeAgainst, toPlace.get().side)) { baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); } diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index 97006adc8..bb8dcadb0 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -126,7 +126,11 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (!MovementHelper.avoidBreaking(baritone.bsi, pos.getX(), pos.getY(), pos.getZ(), state)) { MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); if (isSafeToCancel && baritone.getInputOverrideHandler().isBreakingBlock(pos)) { - baritone.getLookBehavior().updateTarget(ctx.playerRotations(), true, true, true); + baritone.getLookBehavior().updateTarget( + ctx.playerRotations(), + true, + true, + true); baritone.getInputOverrideHandler().setBlockBreakTarget(pos); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); diff --git a/src/main/java/baritone/utils/BlockBreakHelper.java b/src/main/java/baritone/utils/BlockBreakHelper.java index 5b458a96f..8132454ba 100644 --- a/src/main/java/baritone/utils/BlockBreakHelper.java +++ b/src/main/java/baritone/utils/BlockBreakHelper.java @@ -62,7 +62,8 @@ public void tick(boolean isLeftClick, BlockPos target) { } HitResult trace = ctx.objectMouseOver(); boolean isBlockTrace = trace != null && trace.getType() == HitResult.Type.BLOCK; - if (target != null && (!isBlockTrace || !((BlockHitResult) trace).getBlockPos().equals(target))) { + if (target != null + && (!isBlockTrace || !((BlockHitResult) trace).getBlockPos().equals(target))) { clearBreakingBlock(); return; } @@ -72,10 +73,14 @@ public void tick(boolean isLeftClick, BlockPos target) { ctx.playerController().setHittingBlock(wasHitting); if (ctx.playerController().hasBrokenBlock()) { ctx.playerController().syncHeldItem(); - ctx.playerController().clickBlock(blockTrace.getBlockPos(), blockTrace.getDirection()); + ctx.playerController().clickBlock( + blockTrace.getBlockPos(), + blockTrace.getDirection()); ctx.player().swing(InteractionHand.MAIN_HAND); } else { - if (ctx.playerController().onPlayerDamageBlock(blockTrace.getBlockPos(), blockTrace.getDirection())) { + if (ctx.playerController().onPlayerDamageBlock( + blockTrace.getBlockPos(), + blockTrace.getDirection())) { ctx.player().swing(InteractionHand.MAIN_HAND); } if (ctx.playerController().hasBrokenBlock()) { // block broken this tick diff --git a/src/main/java/baritone/utils/BlockPlaceHelper.java b/src/main/java/baritone/utils/BlockPlaceHelper.java index 53efcabff..2fdff9a02 100644 --- a/src/main/java/baritone/utils/BlockPlaceHelper.java +++ b/src/main/java/baritone/utils/BlockPlaceHelper.java @@ -43,20 +43,32 @@ public void tick(boolean rightClickRequested, BlockPos target, Direction side) { return; } HitResult mouseOver = ctx.objectMouseOver(); - if (!rightClickRequested || ctx.player().isHandsBusy() || mouseOver == null || mouseOver.getType() != HitResult.Type.BLOCK) { + if (!rightClickRequested + || ctx.player().isHandsBusy() + || mouseOver == null + || mouseOver.getType() != HitResult.Type.BLOCK) { return; } BlockHitResult blockHit = (BlockHitResult) mouseOver; - if (target != null && (!blockHit.getBlockPos().equals(target) || blockHit.getDirection() != side)) { + if (target != null + && (!blockHit.getBlockPos().equals(target) || blockHit.getDirection() != side)) { return; } rightClickTimer = Baritone.settings().rightClickSpeed.value - BASE_PLACE_DELAY; for (InteractionHand hand : InteractionHand.values()) { - if (ctx.playerController().processRightClickBlock(ctx.player(), ctx.world(), hand, blockHit) == InteractionResult.SUCCESS) { + if (ctx.playerController().processRightClickBlock( + ctx.player(), + ctx.world(), + hand, + blockHit) == InteractionResult.SUCCESS) { ctx.player().swing(hand); return; } - if (!ctx.player().getItemInHand(hand).isEmpty() && ctx.playerController().processRightClick(ctx.player(), ctx.world(), hand) == InteractionResult.SUCCESS) { + if (!ctx.player().getItemInHand(hand).isEmpty() + && ctx.playerController().processRightClick( + ctx.player(), + ctx.world(), + hand) == InteractionResult.SUCCESS) { return; } } diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index bd021d59a..5a47264a1 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -120,7 +120,10 @@ public final void onTick(TickEvent event) { setInputForceState(Input.CLICK_RIGHT, false); } blockBreakHelper.tick(isInputForcedDown(Input.CLICK_LEFT), blockBreakTarget); - blockPlaceHelper.tick(isInputForcedDown(Input.CLICK_RIGHT), blockPlaceTarget, blockPlaceSide); + blockPlaceHelper.tick( + isInputForcedDown(Input.CLICK_RIGHT), + blockPlaceTarget, + blockPlaceSide); if (inControl()) { if (ctx.player().input.getClass() != PlayerMovementInput.class) { @@ -148,7 +151,9 @@ private void stopControllingPlayer() { private void restorePlayerInput() { LocalPlayer player = ctx.player(); - if (player != null && player.input != null && player.input.getClass() == PlayerMovementInput.class) { + if (player != null + && player.input != null + && player.input.getClass() == PlayerMovementInput.class) { player.input = new KeyboardInput(ctx.minecraft().options); } } diff --git a/src/main/java/baritone/utils/PlayerControlGuard.java b/src/main/java/baritone/utils/PlayerControlGuard.java index 4a73ec2cb..b4660fb1e 100644 --- a/src/main/java/baritone/utils/PlayerControlGuard.java +++ b/src/main/java/baritone/utils/PlayerControlGuard.java @@ -24,7 +24,8 @@ public final class PlayerControlGuard { - private PlayerControlGuard() {} + private PlayerControlGuard() { + } public static boolean canControl(IPlayerContext ctx) { return canControl(ctx.player(), ctx.minecraft().screen) diff --git a/src/test/java/baritone/utils/RotationUtilsTest.java b/src/test/java/baritone/utils/RotationUtilsTest.java index a65d3fc01..c6bd1b3e1 100644 --- a/src/test/java/baritone/utils/RotationUtilsTest.java +++ b/src/test/java/baritone/utils/RotationUtilsTest.java @@ -61,13 +61,22 @@ public void testAlerpOppositeRayUsesStableHalfCircle() { public void testRotationArcUsesAngularSpeed() { assertEquals( 1, - countTicks(RotationArc.fromAngularSpeed(new Rotation(0, 0), new Rotation(10, 0), 30))); + countTicks(RotationArc.fromAngularSpeed( + new Rotation(0, 0), + new Rotation(10, 0), + 30))); assertEquals( 3, - countTicks(RotationArc.fromAngularSpeed(new Rotation(0, 0), new Rotation(90, 0), 30))); + countTicks(RotationArc.fromAngularSpeed( + new Rotation(0, 0), + new Rotation(90, 0), + 30))); assertEquals( 4, - countTicks(RotationArc.fromAngularSpeed(new Rotation(0, 0), new Rotation(91, 0), 30))); + countTicks(RotationArc.fromAngularSpeed( + new Rotation(0, 0), + new Rotation(91, 0), + 30))); } @Test From 08d08008a95424c18d518ca6cee30c5dc453f0f8 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Tue, 7 Jul 2026 14:33:57 -0500 Subject: [PATCH 22/26] Address Codacy feedback --- .../api/utils/IInputOverrideHandler.java | 2 + .../mixins/MixinClientPlayerEntity.java | 2 + .../java/baritone/behavior/LookBehavior.java | 8 ++- .../java/baritone/utils/BlockPlaceHelper.java | 50 +++++++++++++++---- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/api/java/baritone/api/utils/IInputOverrideHandler.java b/src/api/java/baritone/api/utils/IInputOverrideHandler.java index 0f33d698e..35ea3b628 100644 --- a/src/api/java/baritone/api/utils/IInputOverrideHandler.java +++ b/src/api/java/baritone/api/utils/IInputOverrideHandler.java @@ -39,8 +39,10 @@ default boolean isBreakingBlock(BlockPos pos) { } default void setBlockBreakTarget(BlockPos pos) { + // Optional hook for handlers that support targeted block breaking. } default void setBlockPlaceTarget(BlockPos pos, Direction side) { + // Optional hook for handlers that support targeted block placing. } } diff --git a/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java b/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java index b24c631cc..83800bafd 100644 --- a/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java +++ b/src/launch/java/baritone/launch/mixins/MixinClientPlayerEntity.java @@ -62,6 +62,7 @@ private void onPreUpdate(CallbackInfo ci) { at = @At("HEAD"), cancellable = true ) + @SuppressWarnings({"unused", "PMD.UnusedPrivateMethod"}) private void getViewYRot(float partialTicks, CallbackInfoReturnable cir) { Rotation visualRotation = this.getVisualRotation(partialTicks); if (visualRotation != null) { @@ -74,6 +75,7 @@ private void getViewYRot(float partialTicks, CallbackInfoReturnable cir) at = @At("HEAD"), cancellable = true ) + @SuppressWarnings({"unused", "PMD.UnusedPrivateMethod"}) private void getViewXRot(float partialTicks, CallbackInfoReturnable cir) { Rotation visualRotation = this.getVisualRotation(partialTicks); if (visualRotation != null) { diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index c72963a2f..a1d6689e0 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -250,6 +250,7 @@ private void updateInterpolation( boolean restartArc, Target.Mode mode) { final int interpolationSpeed = Baritone.settings().interpolatedLookLength.value; + Rotation arcStart = start; if (this.interpolationArc != null && this.interpolationMode != mode) { this.interpolationArc = null; @@ -259,12 +260,15 @@ private void updateInterpolation( if (this.interpolationArc != null && (restartArc || this.interpolationOriginMoved(origin))) { - start = this.interpolationArc.getCurrentRotation(); + arcStart = this.interpolationArc.getCurrentRotation(); this.interpolationArc = null; } if (this.interpolationArc == null) { - this.interpolationArc = RotationArc.fromAngularSpeed(start, actual, interpolationSpeed); + this.interpolationArc = RotationArc.fromAngularSpeed( + arcStart, + actual, + interpolationSpeed); this.interpolationMode = mode; this.interpolationOrigin = origin; } diff --git a/src/main/java/baritone/utils/BlockPlaceHelper.java b/src/main/java/baritone/utils/BlockPlaceHelper.java index 2fdff9a02..2475485c3 100644 --- a/src/main/java/baritone/utils/BlockPlaceHelper.java +++ b/src/main/java/baritone/utils/BlockPlaceHelper.java @@ -38,23 +38,55 @@ public class BlockPlaceHelper { } public void tick(boolean rightClickRequested, BlockPos target, Direction side) { + if (this.isCoolingDown()) { + return; + } + + BlockHitResult blockHit = this.targetedBlockHit(rightClickRequested, target, side); + if (blockHit == null) { + return; + } + + rightClickTimer = Baritone.settings().rightClickSpeed.value - BASE_PLACE_DELAY; + this.tryRightClick(blockHit); + } + + private boolean isCoolingDown() { if (rightClickTimer > 0) { rightClickTimer--; - return; + return true; } + return false; + } + + private BlockHitResult targetedBlockHit( + boolean rightClickRequested, + BlockPos target, + Direction side) { + if (!rightClickRequested || ctx.player().isHandsBusy()) { + return null; + } + HitResult mouseOver = ctx.objectMouseOver(); - if (!rightClickRequested - || ctx.player().isHandsBusy() - || mouseOver == null + if (mouseOver == null || mouseOver.getType() != HitResult.Type.BLOCK) { - return; + return null; } + BlockHitResult blockHit = (BlockHitResult) mouseOver; - if (target != null - && (!blockHit.getBlockPos().equals(target) || blockHit.getDirection() != side)) { - return; + if (!this.matchesTarget(blockHit, target, side)) { + return null; } - rightClickTimer = Baritone.settings().rightClickSpeed.value - BASE_PLACE_DELAY; + + return blockHit; + } + + private boolean matchesTarget(BlockHitResult blockHit, BlockPos target, Direction side) { + return target == null + || (blockHit.getBlockPos().equals(target) && blockHit.getDirection() == side); + } + + private void tryRightClick(BlockHitResult blockHit) { for (InteractionHand hand : InteractionHand.values()) { if (ctx.playerController().processRightClickBlock( ctx.player(), From 49c6d4c206fb8104531ccdf7b0c0a97f3202c805 Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Sun, 19 Jul 2026 10:52:04 -0500 Subject: [PATCH 23/26] Hold builder placement targets --- .../java/baritone/process/BuilderProcess.java | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index e01f78760..0c6726bfa 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -103,6 +103,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil private int layer; private int numRepeats; private List approxPlaceable; + private Placement heldPlacement; public int stopAtHeight = 0; public BuilderProcess(Baritone baritone) { @@ -169,6 +170,7 @@ public boolean partOfMask(int x, int y, int z, BlockState current) { this.numRepeats = 0; this.observedCompleted = new LongOpenHashSet(); this.incorrectPositions = null; + this.heldPlacement = null; } public void resume() { @@ -177,6 +179,7 @@ public void resume() { public void pause() { paused = true; + this.heldPlacement = null; } @Override @@ -312,12 +315,19 @@ private Optional> toBreakNearPlayer(BuilderCalcu public static class Placement { private final int hotbarSelection; + private final BlockPos target; private final BlockPos placeAgainst; private final Direction side; private final Rotation rot; - public Placement(int hotbarSelection, BlockPos placeAgainst, Direction side, Rotation rot) { + public Placement( + int hotbarSelection, + BlockPos target, + BlockPos placeAgainst, + Direction side, + Rotation rot) { this.hotbarSelection = hotbarSelection; + this.target = target; this.placeAgainst = placeAgainst; this.side = side; this.rot = rot; @@ -353,6 +363,34 @@ private Optional searchForPlacables(BuilderCalculationContext bcc, Li return Optional.empty(); } + private Optional revalidateHeldPlacement(BuilderCalculationContext bcc) { + if (this.heldPlacement == null) { + return Optional.empty(); + } + BlockPos target = this.heldPlacement.target; + BlockState current = bcc.bsi.get0(target); + BlockState desired = bcc.getSchematic(target.getX(), target.getY(), target.getZ(), current); + if (desired == null + || !MovementHelper.isReplaceable( + target.getX(), + target.getY(), + target.getZ(), + current, + bcc.bsi) + || valid(current, desired, false)) { + this.heldPlacement = null; + return Optional.empty(); + } + Optional placement = possibleToPlace( + desired, + target.getX(), + target.getY(), + target.getZ(), + bcc.bsi); + this.heldPlacement = placement.orElse(null); + return placement; + } + public boolean placementPlausible(BlockPos pos, BlockState state) { VoxelShape voxelshape = state.getCollisionShape(ctx.world(), pos); return voxelshape.isEmpty() || ctx.world().isUnobstructed(null, voxelshape.move(pos.getX(), pos.getY(), pos.getZ())); @@ -386,7 +424,12 @@ private Optional possibleToPlace(BlockState toPlace, int x, int y, in if (result != null && result.getType() == HitResult.Type.BLOCK && ((BlockHitResult) result).getBlockPos().equals(placeAgainstPos) && ((BlockHitResult) result).getDirection() == against.getOpposite()) { OptionalInt hotbar = hasAnyItemThatWouldPlace(toPlace, result, actualRot); if (hotbar.isPresent()) { - return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot)); + return Optional.of(new Placement( + hotbar.getAsInt(), + new BetterBlockPos(x, y, z), + placeAgainstPos, + against.getOpposite(), + rot)); } } } @@ -518,6 +561,7 @@ public int lengthZ() { if (Baritone.settings().buildInLayers.value && layer * Baritone.settings().layerHeight.value < stopAtHeight) { logDirect("Starting layer " + layer); layer++; + this.heldPlacement = null; return onTick(calcFailed, isSafeToCancel, recursions + 1); } Vec3i repeat = Baritone.settings().buildRepeat.value; @@ -534,6 +578,7 @@ public int lengthZ() { // build repeat time layer = 0; origin = new BlockPos(origin).offset(repeat); + this.heldPlacement = null; if (!Baritone.settings().buildRepeatSneaky.value) { schematic.reset(); } @@ -546,6 +591,7 @@ public int lengthZ() { Optional> toBreak = toBreakNearPlayer(bcc); if (toBreak.isPresent() && isSafeToCancel && ctx.player().isOnGround()) { + this.heldPlacement = null; // we'd like to pause to break this block // only change look direction if it's safe (don't want to fuck up an in progress parkour for example Rotation rot = toBreak.get().getB(); @@ -571,8 +617,12 @@ public int lengthZ() { return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } List desirableOnHotbar = new ArrayList<>(); - Optional toPlace = searchForPlacables(bcc, desirableOnHotbar); + Optional toPlace = revalidateHeldPlacement(bcc); + if (!toPlace.isPresent()) { + toPlace = searchForPlacables(bcc, desirableOnHotbar); + } if (toPlace.isPresent() && isSafeToCancel && ctx.player().isOnGround() && ticks <= 0) { + this.heldPlacement = toPlace.get(); Rotation rot = toPlace.get().rot; baritone.getLookBehavior().updateTarget(rot, true); ctx.player().getInventory().selected = toPlace.get().hotbarSelection; @@ -585,6 +635,7 @@ public int lengthZ() { } return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); } + this.heldPlacement = null; if (Baritone.settings().allowInventory.value) { ArrayList usefulSlots = new ArrayList<>(); @@ -1002,6 +1053,7 @@ public void onLostControl() { numRepeats = 0; paused = false; observedCompleted = null; + heldPlacement = null; } @Override From 32f6b8b03bf5b9a98c3051c88c4e35048c8fb7bc Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Sun, 19 Jul 2026 11:34:44 -0500 Subject: [PATCH 24/26] Target farm block actions --- .../java/baritone/process/FarmProcess.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/baritone/process/FarmProcess.java b/src/main/java/baritone/process/FarmProcess.java index 05b893a27..c6141335a 100644 --- a/src/main/java/baritone/process/FarmProcess.java +++ b/src/main/java/baritone/process/FarmProcess.java @@ -104,6 +104,22 @@ public FarmProcess(Baritone baritone) { super(baritone); } + private void leftClickBlock(BlockPos pos) { + baritone.getInputOverrideHandler().setBlockBreakTarget(pos); + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); + } + + private void rightClickBlock(BlockPos pos, Direction side) { + baritone.getInputOverrideHandler().setBlockPlaceTarget(pos, side); + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + } + + private void rightClickSelectedBlock(BlockPos pos) { + ctx.getSelectedBlockHitResult() + .filter(hit -> hit.getBlockPos().equals(pos)) + .ifPresent(hit -> rightClickBlock(hit.getBlockPos(), hit.getDirection())); + } + @Override public boolean isActive() { return active; @@ -278,7 +294,7 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { baritone.getLookBehavior().updateTarget(rot.get(), true); MovementHelper.switchToBestToolFor(ctx, ctx.world().getBlockState(pos)); if (ctx.isLookingAt(pos)) { - baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); + leftClickBlock(pos); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } @@ -295,8 +311,8 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), blockReachDistance); if (result instanceof BlockHitResult && ((BlockHitResult) result).getDirection() == Direction.UP) { baritone.getLookBehavior().updateTarget(rot.get(), true); - if (ctx.isLookingAt(pos)) { - baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + if (ctx.isLookingAt(pos, Direction.UP)) { + rightClickBlock(pos, Direction.UP); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } @@ -316,8 +332,8 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { HitResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), blockReachDistance); if (result instanceof BlockHitResult && ((BlockHitResult) result).getDirection() == dir) { baritone.getLookBehavior().updateTarget(rot.get(), true); - if (ctx.isLookingAt(pos)) { - baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + if (ctx.isLookingAt(pos, dir)) { + rightClickBlock(pos, dir); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } @@ -332,7 +348,7 @@ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, this::isBoneMeal)) { baritone.getLookBehavior().updateTarget(rot.get(), true); if (ctx.isLookingAt(pos)) { - baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + rightClickSelectedBlock(pos); } return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); } From 8a4ecd99bb5b412111cdc92a7feea0f6740eeadd Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Sun, 19 Jul 2026 11:34:44 -0500 Subject: [PATCH 25/26] Target get-to-block right clicks --- src/main/java/baritone/process/GetToBlockProcess.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/baritone/process/GetToBlockProcess.java b/src/main/java/baritone/process/GetToBlockProcess.java index 1352232d4..6a0aece17 100644 --- a/src/main/java/baritone/process/GetToBlockProcess.java +++ b/src/main/java/baritone/process/GetToBlockProcess.java @@ -35,6 +35,7 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; import java.util.*; @@ -214,7 +215,13 @@ private boolean rightClick() { Optional reachable = RotationUtils.reachable(ctx, pos, ctx.playerController().getBlockReachDistance()); if (reachable.isPresent()) { baritone.getLookBehavior().updateTarget(reachable.get(), true); - if (knownLocations.contains(ctx.getSelectedBlock().orElse(null))) { + Optional selected = ctx.getSelectedBlockHitResult() + .filter(hit -> knownLocations.contains(hit.getBlockPos())); + if (selected.isPresent()) { + BlockHitResult hit = selected.get(); + baritone.getInputOverrideHandler().setBlockPlaceTarget( + hit.getBlockPos(), + hit.getDirection()); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); // TODO find some way to right click even if we're in an ESC menu System.out.println(ctx.player().containerMenu); if (!(ctx.player().containerMenu instanceof InventoryMenu)) { From dc3fae4ba87c7f3ba97088af7daacf8df78ccf7d Mon Sep 17 00:00:00 2001 From: TheThouZands Date: Sun, 19 Jul 2026 11:34:48 -0500 Subject: [PATCH 26/26] Target movement block interactions --- .../baritone/pathing/movement/Movement.java | 42 ++++++++++++++++--- .../movement/movements/MovementFall.java | 4 +- .../movement/movements/MovementPillar.java | 7 ++-- .../movement/movements/MovementTraverse.java | 23 ++++++---- 4 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index 66214c6ef..b4a900c93 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -30,6 +30,7 @@ import net.minecraft.core.Direction; import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; public abstract class Movement implements IMovement, MovementHelper { @@ -113,6 +114,37 @@ protected boolean playerInValidPosition() { return getValidPositions().contains(ctx.playerFeet()) || getValidPositions().contains(((PathingBehavior) baritone.getPathingBehavior()).pathStart()); } + protected boolean leftClickBlock(MovementState state, BlockPos pos) { + if (!ctx.isLookingAt(pos)) { + return false; + } + state.setBlockBreakTarget(pos) + .setInput(Input.CLICK_LEFT, true); + return true; + } + + protected boolean leftClickSelectedBlock(MovementState state) { + Optional selected = ctx.getSelectedBlock(); + if (!selected.isPresent()) { + return false; + } + state.setBlockBreakTarget(selected.get()) + .setInput(Input.CLICK_LEFT, true); + return true; + } + + protected boolean rightClickBlock(MovementState state, BlockPos pos) { + Optional selected = ctx.getSelectedBlockHitResult() + .filter(hit -> hit.getBlockPos().equals(pos)); + if (!selected.isPresent()) { + return false; + } + BlockHitResult hit = selected.get(); + state.setBlockPlaceTarget(hit.getBlockPos(), hit.getDirection()) + .setInput(Input.CLICK_RIGHT, true); + return true; + } + /** * Handles the execution of the latest Movement * State, and offers a Status to the calling class. @@ -127,7 +159,10 @@ public MovementStatus update() { currentState.setInput(Input.JUMP, true); } if (ctx.player().isInWall()) { - ctx.getSelectedBlock().ifPresent(pos -> MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, pos))); + ctx.getSelectedBlock().ifPresent(pos -> { + MovementHelper.switchToBestToolFor(ctx, BlockStateInterface.get(ctx, pos)); + currentState.setBlockBreakTarget(pos); + }); currentState.setInput(Input.CLICK_LEFT, true); } @@ -188,10 +223,7 @@ protected boolean prepared(MovementState state) { if (reachable.isPresent()) { Rotation rotTowardsBlock = reachable.get(); state.setTarget(new MovementState.MovementTarget(rotTowardsBlock, true)); - if (ctx.isLookingAt(blockPos)) { - state.setBlockBreakTarget(blockPos) - .setInput(Input.CLICK_LEFT, true); - } + leftClickBlock(state, blockPos); return false; } //get rekt minecraft diff --git a/src/main/java/baritone/pathing/movement/movements/MovementFall.java b/src/main/java/baritone/pathing/movement/movements/MovementFall.java index c537fc2ca..9700a4e82 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementFall.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementFall.java @@ -110,8 +110,8 @@ public MovementState updateState(MovementState state) { targetRotation = new Rotation(toDest.getYaw(), 90.0F); - if (ctx.isLookingAt(dest) || ctx.isLookingAt(dest.below())) { - state.setInput(Input.CLICK_RIGHT, true); + if (!rightClickBlock(state, dest)) { + rightClickBlock(state, dest.below()); } } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java index bbda3b982..450e45093 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java @@ -221,10 +221,11 @@ public MovementState updateState(MovementState state) { .map(rot -> new MovementState.MovementTarget(rot, true)) .ifPresent(state::setTarget); state.setInput(Input.JUMP, false); // breaking is like 5x slower when you're jumping - state.setInput(Input.CLICK_LEFT, true); + leftClickBlock(state, src); blockIsThere = false; - } else if (ctx.player().isCrouching() && (ctx.isLookingAt(src.below()) || ctx.isLookingAt(src)) && ctx.player().position().y > dest.getY() + 0.1) { - state.setInput(Input.CLICK_RIGHT, true); + } else if (ctx.player().isCrouching() && ctx.player().position().y > dest.getY() + 0.1) { + rightClickBlock(state, src.below()); + rightClickBlock(state, src); } } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index bbfd67d4c..cd6ff8449 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -228,8 +228,15 @@ public MovementState updateState(MovementState state) { boolean canOpen = !(Blocks.IRON_DOOR.equals(pb0.getBlock()) || Blocks.IRON_DOOR.equals(pb1.getBlock())); if (notPassable && canOpen) { - return state.setTarget(new MovementState.MovementTarget(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), VecUtils.calculateBlockCenter(ctx.world(), positionsToBreak[0]), ctx.playerRotations()), true)) - .setInput(Input.CLICK_RIGHT, true); + BlockPos blocked = pb0.getBlock() instanceof DoorBlock ? positionsToBreak[0] : positionsToBreak[1]; + state.setTarget(new MovementState.MovementTarget( + RotationUtils.calcRotationFromVec3d( + ctx.playerHead(), + VecUtils.calculateBlockCenter(ctx.world(), blocked), + ctx.playerRotations()), + true)); + rightClickBlock(state, blocked); + return state; } } @@ -240,7 +247,9 @@ public MovementState updateState(MovementState state) { if (blocked != null) { Optional rotation = RotationUtils.reachable(ctx, blocked); if (rotation.isPresent()) { - return state.setTarget(new MovementState.MovementTarget(rotation.get(), true)).setInput(Input.CLICK_RIGHT, true); + state.setTarget(new MovementState.MovementTarget(rotation.get(), true)); + rightClickBlock(state, blocked); + return state; } } } @@ -316,7 +325,7 @@ public MovementState updateState(MovementState state) { } } else if (ctx.playerRotations().isReallyCloseTo(state.getTarget().rotation)) { // well i guess theres something in the way - return state.setInput(Input.CLICK_LEFT, true); + leftClickSelectedBlock(state); } return state; } @@ -342,12 +351,12 @@ public MovementState updateState(MovementState state) { } else { state.setTarget(new MovementState.MovementTarget(backToFace, true)); } - if (ctx.isLookingAt(goalLook)) { - return state.setInput(Input.CLICK_RIGHT, true); // wait to right click until we are able to place + if (rightClickBlock(state, goalLook)) { + return state; // wait to right click until we are able to place } // Out.log("Trying to look at " + goalLook + ", actually looking at" + Baritone.whatAreYouLookingAt()); if (ctx.playerRotations().isReallyCloseTo(state.getTarget().rotation)) { - state.setInput(Input.CLICK_LEFT, true); + leftClickSelectedBlock(state); } return state; }