From 85b837bc1cc1139087f12f55861c665be9e8e439 Mon Sep 17 00:00:00 2001 From: aver <66864263+AverWasTaken@users.noreply.github.com> Date: Sun, 10 Aug 2025 14:13:20 -0700 Subject: [PATCH 1/5] Initial Jumpsprint Commit --- src/api/java/baritone/api/Settings.java | 5 + .../movement/movements/MovementAscend.java | 76 ++++++- .../movement/movements/MovementDescend.java | 4 + .../movement/movements/MovementDiagonal.java | 4 + .../movement/movements/MovementTraverse.java | 5 +- .../pathing/path/JumpSprintController.java | 203 ++++++++++++++++++ .../baritone/pathing/path/PathExecutor.java | 25 ++- 7 files changed, 309 insertions(+), 13 deletions(-) create mode 100644 src/main/java/baritone/pathing/path/JumpSprintController.java diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index be8859856..80dbfe8f0 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -808,6 +808,11 @@ public final class Settings { */ public final Setting sprintInWater = new Setting<>(true); + /** + * Jump while sprinting on flat traverses for extra speed + */ + public final Setting jumpsprint = new Setting<>(false); + /** * When GetToBlockProcess or MineProcess fails to calculate a path, instead of just giving up, mark the closest instance * of that block as "unreachable" and go towards the next closest. GetToBlock expands this search to the whole "vein"; MineProcess does not. diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 717cd2e70..75fb51bf4 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -37,6 +37,8 @@ public class MovementAscend extends Movement { private int ticksWithoutPlacement = 0; + // Latch to guarantee a jump within a short window once we commit to takeoff + private int takeoffLatchTicks = 0; public MovementAscend(IBaritone baritone, BetterBlockPos src, BetterBlockPos dest) { super(baritone, src, dest, new BetterBlockPos[]{dest, src.above(2), dest.above()}, dest.below()); @@ -46,6 +48,7 @@ public MovementAscend(IBaritone baritone, BetterBlockPos src, BetterBlockPos des public void reset() { super.reset(); ticksWithoutPlacement = 0; + takeoffLatchTicks = 0; } @Override @@ -161,11 +164,8 @@ public MovementState updateState(MovementState state) { return state.setStatus(MovementStatus.UNREACHABLE); } super.updateState(state); - // TODO incorporate some behavior from ActionClimb (specifically how it waited until it was at most 1.2 blocks away before starting to jump - // for efficiency in ascending minimal height staircases, which is just repeated MovementAscend, so that it doesn't bonk its head on the ceiling repeatedly) - if (state.getStatus() != MovementStatus.RUNNING) { - return state; - } + // Allow approach timing even if state isn't yet RUNNING, as long as the landing block is walkable. + boolean isRunning = state.getStatus() == MovementStatus.RUNNING; if (ctx.playerFeet().equals(dest) || ctx.playerFeet().equals(dest.offset(getDirection().below()))) { return state.setStatus(MovementStatus.SUCCESS); @@ -188,6 +188,61 @@ public MovementState updateState(MovementState state) { return state; } MovementHelper.moveTowards(ctx, state, dest); + // Maintain forward; adjust sprint and jump timing based on edge distance for clean cadence + state.setInput(Input.MOVE_FORWARD, true); + if (Baritone.settings().sprintAscends.value) { + int dx = Integer.signum(dest.x - src.x); + int dz = Integer.signum(dest.z - src.z); + boolean alongX = Math.abs(dx) == 1; + + double px = ctx.player().position().x; + double pz = ctx.player().position().z; + // Distance to the leading edge of the current block in the direction of travel + double edgeDist; + if (alongX) { + double edgeX = dx > 0 ? (src.x + 1.0D) : (double) src.x; + edgeDist = Math.max(0.0, dx > 0 ? (edgeX - px) : (px - edgeX)); + } else { + double edgeZ = dz > 0 ? (src.z + 1.0D) : (double) src.z; + edgeDist = Math.max(0.0, dz > 0 ? (edgeZ - pz) : (pz - edgeZ)); + } + + // Small lateral tolerance: avoid jumping if we're too far off the center line + double lateralOffset = alongX ? Math.abs((src.z + 0.5D) - pz) : Math.abs((src.x + 0.5D) - px); + boolean centered = lateralOffset <= 0.28D; + + boolean onGround = ctx.player().onGround(); + // Secondary proximity using dest center to avoid missing the window if edge calc drifts + double flatDistToDestCenter = Math.max(Math.abs((dest.x + 0.5D) - px), Math.abs((dest.z + 0.5D) - pz)); + boolean withinTaper = onGround && centered && edgeDist < 0.60D && edgeDist > 0.32D; + boolean shouldJump = onGround && ( + (centered && edgeDist <= 0.32D) || + flatDistToDestCenter <= 0.85D || + ctx.player().horizontalCollision + ); + + // Takeoff latch: once we enter taper or are very close, guarantee jump in the next 2 ticks + if (withinTaper || (onGround && edgeDist <= 0.40D)) { + takeoffLatchTicks = Math.max(takeoffLatchTicks, 2); + } + if (takeoffLatchTicks > 0) { + takeoffLatchTicks--; + // While latched, bias toward jumping sooner rather than later + if (onGround) { + shouldJump = true; + } + } + + // Apply inputs without early returns to avoid missing the jump window due to ordering elsewhere + state.setInput(Input.SPRINT, !withinTaper); + if (shouldJump) { + state.setInput(Input.SPRINT, true); + state.setInput(Input.JUMP, true); + } + if (!onGround) { + state.setInput(Input.SPRINT, true); + } + } if (MovementHelper.isBottomSlab(jumpingOnto) && !MovementHelper.isBottomSlab(BlockStateInterface.get(ctx, src.below()))) { return state; // don't jump while walking from a non double slab into a bottom slab } @@ -197,10 +252,10 @@ public MovementState updateState(MovementState state) { return state; } - int xAxis = Math.abs(src.getX() - dest.getX()); // either 0 or 1 - int zAxis = Math.abs(src.getZ() - dest.getZ()); // either 0 or 1 - double flatDistToNext = xAxis * Math.abs((dest.getX() + 0.5D) - ctx.player().position().x) + zAxis * Math.abs((dest.getZ() + 0.5D) - ctx.player().position().z); - double sideDist = zAxis * Math.abs((dest.getX() + 0.5D) - ctx.player().position().x) + xAxis * Math.abs((dest.getZ() + 0.5D) - ctx.player().position().z); + int xAxis = Math.abs(dest.x - src.x); // either 0 or 1 + int zAxis = Math.abs(dest.z - src.z); // either 0 or 1 + double flatDistToNext = xAxis * Math.abs((dest.x + 0.5D) - ctx.player().position().x) + zAxis * Math.abs((dest.z + 0.5D) - ctx.player().position().z); + double sideDist = zAxis * Math.abs((dest.x + 0.5D) - ctx.player().position().x) + xAxis * Math.abs((dest.z + 0.5D) - ctx.player().position().z); double lateralMotion = xAxis * ctx.player().getDeltaMovement().z + zAxis * ctx.player().getDeltaMovement().x; if (Math.abs(lateralMotion) > 0.1) { @@ -218,7 +273,8 @@ public MovementState updateState(MovementState state) { // Once we are pointing the right way and moving, start jumping // This is slightly more efficient because otherwise we might start jumping before moving, and fall down without moving onto the block we want to jump onto // Also wait until we are close enough, because we might jump and hit our head on an adjacent block - return state.setInput(Input.JUMP, true); + // Re-engage sprint at jump to preserve momentum up the block + return state.setInput(Input.SPRINT, true).setInput(Input.JUMP, true); } public boolean headBonkClear() { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index 4d607aa85..a1661dac3 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -228,6 +228,10 @@ public MovementState updateState(MovementState state) { if (state.getStatus() != MovementStatus.RUNNING) { return state; } + // Keep sprint through descends when not in liquid; prevents de-sprinting during downhill chains + if (!MovementHelper.isLiquid(ctx, ctx.playerFeet())) { + state.setInput(Input.SPRINT, true); + } BlockPos playerFeet = ctx.playerFeet(); BlockPos fakeDest = new BlockPos(dest.getX() * 2 - src.getX(), dest.getY(), dest.getZ() * 2 - src.getZ()); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index cf387d5c9..acd5f362b 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -269,6 +269,10 @@ public MovementState updateState(MovementState state) { } if (sprint()) { state.setInput(Input.SPRINT, true); + // keep sprint while airborne to avoid unintended de-sprint on diagonal hops + if (!ctx.player().onGround()) { + state.setInput(Input.SPRINT, true); + } } MovementHelper.moveTowards(ctx, state, dest); return state; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index 705863407..60b1fca2b 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -245,9 +245,11 @@ public MovementState updateState(MovementState state) { BlockPos feet = ctx.playerFeet(); if (feet.getY() != dest.getY() && !ladder) { logDebug("Wrong Y coordinate"); + // Always keep sprint held while airborne during a traverse; PathExecutor manages jump timing + state.setInput(Input.SPRINT, true); if (feet.getY() < dest.getY()) { System.out.println("In movement traverse"); - return state.setInput(Input.JUMP, true); + return state; // jump already held if applicable; keep sprint } return state; } @@ -271,6 +273,7 @@ public MovementState updateState(MovementState state) { BlockState intoAbove = BlockStateInterface.get(ctx, into.above()); if (wasTheBridgeBlockAlwaysThere && (!MovementHelper.isLiquid(ctx, feet) || Baritone.settings().sprintInWater.value) && (!MovementHelper.avoidWalkingInto(intoBelow) || MovementHelper.isWater(intoBelow)) && !MovementHelper.avoidWalkingInto(intoAbove)) { state.setInput(Input.SPRINT, true); + // Let PathExecutor decide final jump timing; we only hint here to avoid conflicting inputs near edges } BlockState destDown = BlockStateInterface.get(ctx, dest.below()); diff --git a/src/main/java/baritone/pathing/path/JumpSprintController.java b/src/main/java/baritone/pathing/path/JumpSprintController.java new file mode 100644 index 000000000..f980fd115 --- /dev/null +++ b/src/main/java/baritone/pathing/path/JumpSprintController.java @@ -0,0 +1,203 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.pathing.path; + +import baritone.Baritone; +import baritone.api.pathing.calc.IPath; +import baritone.api.pathing.movement.IMovement; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.Helper; +import baritone.api.utils.IPlayerContext; +import baritone.api.utils.VecUtils; +import baritone.api.utils.input.Input; +import baritone.behavior.PathingBehavior; +import baritone.pathing.movement.Movement; +import baritone.pathing.movement.MovementHelper; +import baritone.pathing.movement.movements.MovementAscend; +import baritone.pathing.movement.movements.MovementTraverse; +import baritone.pathing.movement.movements.MovementDiagonal; +import baritone.pathing.movement.movements.MovementParkour; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; +import baritone.utils.BlockStateInterface; + + +/** + * Minimal, conservative jump-sprint assist for flat straight segments. + * + * Design goals: + * - Never fight movement classes for sprint control + * - Never touch rotations (no yaw/pitch steering) + * - Only emit short JUMP pulses when clearly safe and beneficial + */ +public final class JumpSprintController implements Helper { + + // cadence guard so we don't spam jump multiple ticks in a row + private int ticks; + private int lastJumpPulseTick = -1000; + private boolean wasOnGround; + + // warmup for trajectory stabilization on straight runs + private static final int WARMUP_TILES = 2; + private int straightDx = 0; + private int straightDz = 0; + private int warmupTilesRemaining = 0; + private int lastPathIndex = -1; + + /** + * Apply jump timing for the current tick. Sprint is handled elsewhere. + */ + public void apply(PathingBehavior behavior, + IPlayerContext ctx, + Movement movement, + IPath path, + int pathPosition) { + ticks++; + if (!Baritone.settings().jumpsprint.value) { + return; + } + + // Only consider flat straight traverses to avoid corner skew. Skip diagonals and others. + if (!(movement instanceof MovementTraverse)) { + return; + } + + try { + // Don't interfere while interacting with blocks or in liquids + if (MovementHelper.isLiquid(ctx, ctx.playerFeet())) { + return; + } + BlockStateInterface bsi = new BlockStateInterface(ctx); + if (!((Movement) movement).toBreak(bsi).isEmpty() || !((Movement) movement).toPlace(bsi).isEmpty()) { + return; + } + + int dx = Integer.signum(movement.getDest().x - movement.getSrc().x); + int dy = Integer.signum(movement.getDest().y - movement.getSrc().y); + int dz = Integer.signum(movement.getDest().z - movement.getSrc().z); + if (dy != 0) { + return; // not flat + } + + // Always keep sprint and forward held during traverse to prevent midair de-sprint and deceleration + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + ctx.player().setSprinting(true); + + // Track straight run and apply warmup across tiles to stabilize trajectory + boolean directionChanged = (dx != straightDx) || (dz != straightDz); + boolean pathAdvanced = (lastPathIndex != -1 && pathPosition != lastPathIndex); + if (directionChanged) { + straightDx = dx; + straightDz = dz; + warmupTilesRemaining = WARMUP_TILES; + } else if (pathAdvanced && warmupTilesRemaining > 0) { + warmupTilesRemaining--; + } + + // Require at least one more movement straight ahead in the same flat direction, + // and avoid an immediate ascend/parkour in the same direction (they control their timing). + IMovement next = (pathPosition < path.length() - 1) ? path.movements().get(pathPosition + 1) : null; + if (next == null) { + return; + } + int ndx = Integer.signum(((Movement) next).getDest().x - ((Movement) next).getSrc().x); + int ndz = Integer.signum(((Movement) next).getDest().z - ((Movement) next).getSrc().z); + boolean sameFlat = dx == ndx && dz == ndz; + if (!sameFlat) { + // turning soon; let movement logic handle inputs this tick + lastPathIndex = pathPosition; + return; + } + if (next instanceof MovementAscend || next instanceof MovementParkour) { + lastPathIndex = pathPosition; + return; // let ascend/parkour manage their own jump timing + } + + // Two-step lookahead to avoid jumping into an imminent corner or ascend/parkour + if (pathPosition + 2 < path.length()) { + IMovement next2 = path.movements().get(pathPosition + 2); + int ndx2 = Integer.signum(((Movement) next2).getDest().x - ((Movement) next2).getSrc().x); + int ndz2 = Integer.signum(((Movement) next2).getDest().z - ((Movement) next2).getSrc().z); + boolean sameFlat2 = dx == ndx2 && dz == ndz2; + if (!sameFlat2 || next2 instanceof MovementAscend || next2 instanceof MovementParkour) { + // approaching a turn or ascend/parkour; leave inputs untouched for movement/PathExecutor to manage + lastPathIndex = pathPosition; + return; + } + } + + // If we're wildly off-center relative to the current segment, don't risk skewing + BetterBlockPos anchor = path.positions().get(pathPosition); + double lateralOffset = lateralOffsetFromCenterXZ(ctx, anchor, dx, dz); + if (lateralOffset > 0.28) { // roughly quarter-block tolerance + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + wasOnGround = ctx.player().onGround(); + return; + } + + // Engagement conditions + boolean onGround = ctx.player().onGround(); + boolean nearEnd = (path.length() - pathPosition) <= 3; + if (nearEnd) { + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + wasOnGround = onGround; + lastPathIndex = pathPosition; + return; + } + + // Warmup: don't jump yet to stabilize heading + if (warmupTilesRemaining > 0) { + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + wasOnGround = onGround; + lastPathIndex = pathPosition; + return; + } + + // Engage: hold jump continuously during straight traverse + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + ctx.player().setSprinting(true); + + wasOnGround = onGround; + lastPathIndex = pathPosition; + } catch (Throwable t) { + logDebug("JumpSprintController suppressed an exception: " + t); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); + } + } + + private static double lateralOffsetFromCenterXZ(IPlayerContext ctx, BetterBlockPos anchor, int dx, int dz) { + // Compute lateral offset from the center line of travel within the current tile + double px = ctx.player().position().x; + double pz = ctx.player().position().z; + double cx = anchor.x + 0.5; + double cz = anchor.z + 0.5; + // Perpendicular unit (not normalized but consistent for signum components) + double perpX = -dz; + double perpZ = dx; + double vx = px - cx; + double vz = pz - cz; + double lateral = Math.abs(vx * perpX + vz * perpZ); + return lateral; + } +} + diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index 8a5359b5b..98279fbc6 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -75,6 +75,7 @@ public class PathExecutor implements IPathExecutor, Helper { private final IPlayerContext ctx; private boolean sprintNextTick; + private final JumpSprintController jumpSprintController = new JumpSprintController(); public PathExecutor(PathingBehavior behavior, IPath path) { this.behavior = behavior; @@ -236,8 +237,10 @@ public boolean onTick() { } else { sprintNextTick = shouldSprintNextTick(); if (!sprintNextTick) { - ctx.player().setSprinting(false); // letting go of control doesn't make you stop sprinting actually + ctx.player().setSprinting(false); // letting go of control doesn't make you stop sprinting actually, who thought } + // Centralized jump-sprint control + jumpSprintController.apply(behavior, ctx, movement, path, pathPosition); ticksOnCurrent++; if (ticksOnCurrent > currentMovementOriginalCostEstimate + Baritone.settings().movementTimeoutTicks.value) { // only cancel if the total time has exceeded the initial estimate @@ -342,6 +345,12 @@ public boolean snipsnapifpossible() { } private boolean shouldSprintNextTick() { + // For MovementAscend, trust the movement's own sprint/jump timing and avoid clearing sprint here + IMovement currentMovement = path.movements().get(pathPosition); + if (currentMovement instanceof MovementAscend) { + return true; // let MovementAscend and MovementState drive sprint; don't clear it below + } + boolean requested = behavior.baritone.getInputOverrideHandler().isInputForcedDown(Input.SPRINT); // we'll take it from here, no need for minecraft to see we're holding down control and sprint for us @@ -351,7 +360,19 @@ private boolean shouldSprintNextTick() { if (!new CalculationContext(behavior.baritone, false).canSprint) { return false; } - IMovement current = path.movements().get(pathPosition); + IMovement current = currentMovement; + + // Ensure sprint stays engaged during straight segments when jumpsprint is enabled, + // but don't force sprint if an ascend/parkour is imminent (let those control cadence) + if (Baritone.settings().jumpsprint.value && (current instanceof MovementTraverse || current instanceof MovementDiagonal)) { + IMovement next = pathPosition < path.length() - 1 ? path.movements().get(pathPosition + 1) : null; + if (!(next instanceof MovementAscend) && !(next instanceof MovementParkour)) { + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); + return true; + } + } + + // (Ascend handled above) // traverse requests sprinting, so we need to do this check first if (current instanceof MovementTraverse && pathPosition < path.length() - 3) { From ecbb55ef995838bf2ef6ace1b5bde3d02c4e0b70 Mon Sep 17 00:00:00 2001 From: aver <66864263+AverWasTaken@users.noreply.github.com> Date: Sun, 10 Aug 2025 14:19:33 -0700 Subject: [PATCH 2/5] Lookahead More no need to jumpsprint if its only 3 blocks, just reg sprint so we can have better timing --- .../movement/movements/MovementAscend.java | 16 +++++++--- .../pathing/path/JumpSprintController.java | 32 ++++++++++++++++++- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 75fb51bf4..40a77e578 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -197,6 +197,11 @@ public MovementState updateState(MovementState state) { double px = ctx.player().position().x; double pz = ctx.player().position().z; + double vx = ctx.player().getDeltaMovement().x; + double vz = ctx.player().getDeltaMovement().z; + double speedXZ = Math.hypot(vx, vz); + // Speed factor in [0,1]: 0 near slow walk, 1 at fast sprint/boosts + double speedFactor = Math.max(0.0, Math.min(1.0, (speedXZ - 0.19D) / 0.08D)); // Distance to the leading edge of the current block in the direction of travel double edgeDist; if (alongX) { @@ -214,16 +219,19 @@ public MovementState updateState(MovementState state) { boolean onGround = ctx.player().onGround(); // Secondary proximity using dest center to avoid missing the window if edge calc drifts double flatDistToDestCenter = Math.max(Math.abs((dest.x + 0.5D) - px), Math.abs((dest.z + 0.5D) - pz)); - boolean withinTaper = onGround && centered && edgeDist < 0.60D && edgeDist > 0.32D; + // Dynamic thresholds based on approach speed: earlier taper / earlier jump when too fast + double taperStart = 0.60D + 0.25D * speedFactor; // up to 0.85 at high speed + double jumpDist = 0.32D + 0.10D * speedFactor; // up to 0.42 at high speed + boolean withinTaper = onGround && centered && edgeDist < taperStart && edgeDist > jumpDist; boolean shouldJump = onGround && ( - (centered && edgeDist <= 0.32D) || + (centered && edgeDist <= jumpDist) || flatDistToDestCenter <= 0.85D || ctx.player().horizontalCollision ); // Takeoff latch: once we enter taper or are very close, guarantee jump in the next 2 ticks - if (withinTaper || (onGround && edgeDist <= 0.40D)) { - takeoffLatchTicks = Math.max(takeoffLatchTicks, 2); + if (withinTaper || (onGround && edgeDist <= (jumpDist + 0.08D))) { + takeoffLatchTicks = Math.max(takeoffLatchTicks, 2 + (int) Math.round(2 * speedFactor)); } if (takeoffLatchTicks > 0) { takeoffLatchTicks--; diff --git a/src/main/java/baritone/pathing/path/JumpSprintController.java b/src/main/java/baritone/pathing/path/JumpSprintController.java index f980fd115..efc25e90e 100644 --- a/src/main/java/baritone/pathing/path/JumpSprintController.java +++ b/src/main/java/baritone/pathing/path/JumpSprintController.java @@ -111,7 +111,7 @@ public void apply(PathingBehavior behavior, } // Require at least one more movement straight ahead in the same flat direction, - // and avoid an immediate ascend/parkour in the same direction (they control their timing). + // and avoid an upcoming ascend/parkour/turn within a short horizon (they control their timing). IMovement next = (pathPosition < path.length() - 1) ? path.movements().get(pathPosition + 1) : null; if (next == null) { return; @@ -129,6 +129,16 @@ public void apply(PathingBehavior behavior, return; // let ascend/parkour manage their own jump timing } + // Horizon suppression: if an ascend/parkour or flat turn exists within 4 moves, don't jump-sprint; just regular sprint + if (shouldSuppressForHorizon(path, pathPosition, dx, dz)) { + // keep sprint and forward, but no jump this tick + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + lastPathIndex = pathPosition; + return; + } + // Two-step lookahead to avoid jumping into an imminent corner or ascend/parkour if (pathPosition + 2 < path.length()) { IMovement next2 = path.movements().get(pathPosition + 2); @@ -199,5 +209,25 @@ private static double lateralOffsetFromCenterXZ(IPlayerContext ctx, BetterBlockP double lateral = Math.abs(vx * perpX + vz * perpZ); return lateral; } + + private static boolean shouldSuppressForHorizon(IPath path, int pathPosition, int dx, int dz) { + // Look up to 4 moves ahead for: + // - Ascend/parkour in same flat direction (we want to sprint into it, not jump-sprint before) + // - Any flat direction change (turn), to avoid engaging too close to corners + int maxAhead = 4; + for (int ahead = 1; ahead <= maxAhead && pathPosition + ahead < path.length(); ahead++) { + IMovement m = path.movements().get(pathPosition + ahead); + int adx = Integer.signum(((Movement) m).getDest().x - ((Movement) m).getSrc().x); + int adz = Integer.signum(((Movement) m).getDest().z - ((Movement) m).getSrc().z); + boolean sameFlat = dx == adx && dz == adz; + if (!sameFlat) { + return true; // turn coming up; skip jump-sprint + } + if (m instanceof MovementAscend || m instanceof MovementParkour) { + return true; // step-up/jump coming soon; prefer regular sprint approach + } + } + return false; + } } From 8ccaf23f0eaae26dcb039a203fefabf3115f5776 Mon Sep 17 00:00:00 2001 From: aver <66864263+AverWasTaken@users.noreply.github.com> Date: Sun, 10 Aug 2025 14:37:30 -0700 Subject: [PATCH 3/5] stop trying to jump off shit!! --- .../pathing/path/JumpSprintController.java | 105 ++++++++++++++++-- .../baritone/pathing/path/PathExecutor.java | 10 +- 2 files changed, 103 insertions(+), 12 deletions(-) diff --git a/src/main/java/baritone/pathing/path/JumpSprintController.java b/src/main/java/baritone/pathing/path/JumpSprintController.java index efc25e90e..413477d0c 100644 --- a/src/main/java/baritone/pathing/path/JumpSprintController.java +++ b/src/main/java/baritone/pathing/path/JumpSprintController.java @@ -32,6 +32,7 @@ import baritone.pathing.movement.movements.MovementTraverse; import baritone.pathing.movement.movements.MovementDiagonal; import baritone.pathing.movement.movements.MovementParkour; +import baritone.pathing.movement.movements.MovementDescend; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import baritone.utils.BlockStateInterface; @@ -39,11 +40,6 @@ /** * Minimal, conservative jump-sprint assist for flat straight segments. - * - * Design goals: - * - Never fight movement classes for sprint control - * - Never touch rotations (no yaw/pitch steering) - * - Only emit short JUMP pulses when clearly safe and beneficial */ public final class JumpSprintController implements Helper { @@ -58,6 +54,8 @@ public final class JumpSprintController implements Helper { private int straightDz = 0; private int warmupTilesRemaining = 0; private int lastPathIndex = -1; + private static final int STABILIZE_TICKS_AFTER_TURN = 4; + private int stabilizationTicksRemaining = 0; /** * Apply jump timing for the current tick. Sprint is handled elsewhere. @@ -72,8 +70,8 @@ public void apply(PathingBehavior behavior, return; } - // Only consider flat straight traverses to avoid corner skew. Skip diagonals and others. - if (!(movement instanceof MovementTraverse)) { + // Consider flat straight traverses and long flat diagonals. Skip other movement types. + if (!(movement instanceof MovementTraverse || movement instanceof MovementDiagonal)) { return; } @@ -106,9 +104,13 @@ public void apply(PathingBehavior behavior, straightDx = dx; straightDz = dz; warmupTilesRemaining = WARMUP_TILES; + stabilizationTicksRemaining = STABILIZE_TICKS_AFTER_TURN; // buffer after turn/diagonal change } else if (pathAdvanced && warmupTilesRemaining > 0) { warmupTilesRemaining--; } + if (pathAdvanced && stabilizationTicksRemaining > 0) { + stabilizationTicksRemaining--; + } // Require at least one more movement straight ahead in the same flat direction, // and avoid an upcoming ascend/parkour/turn within a short horizon (they control their timing). @@ -139,6 +141,26 @@ public void apply(PathingBehavior behavior, return; } + // Elevation safety: analyze upcoming elevation to decide whether jump-sprint is beneficial + ElevationEval eval = evaluateElevationHorizon(ctx, path, pathPosition, dx, dz); + // If we are approaching a long climb (lots of ascends), jump-sprint provides little benefit and may destabilize + if (eval.ascendHeavy) { + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + lastPathIndex = pathPosition; + return; + } + // If a dangerous drop is imminent (risk of fall damage), suppress jump-sprint to avoid overshooting the ledge + if (eval.dangerousDropSoon) { + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + lastPathIndex = pathPosition; + return; + } + // If a small safe descend is next, allow downhill jump-sprint; ensure sprint held midair is already handled below + // Two-step lookahead to avoid jumping into an imminent corner or ascend/parkour if (pathPosition + 2 < path.length()) { IMovement next2 = path.movements().get(pathPosition + 2); @@ -173,8 +195,12 @@ public void apply(PathingBehavior behavior, return; } - // Warmup: don't jump yet to stabilize heading - if (warmupTilesRemaining > 0) { + // Warmup & stabilization: don't jump yet to stabilize heading and lateral velocity + double vx = ctx.player().getDeltaMovement().x; + double vz = ctx.player().getDeltaMovement().z; + double perpVel = Math.abs(vx * (-dz) + vz * dx); // velocity perpendicular to our travel axis + boolean lateralVelocityHigh = perpVel > 0.035; // small threshold to avoid oscillation + if (warmupTilesRemaining > 0 || stabilizationTicksRemaining > 0 || lateralVelocityHigh) { behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); wasOnGround = onGround; @@ -182,7 +208,17 @@ public void apply(PathingBehavior behavior, return; } - // Engage: hold jump continuously during straight traverse + // For diagonals, preserve midair momentum by not toggling jump while airborne; only ensure sprint + if (movement instanceof MovementDiagonal) { + if (!onGround) { + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + lastPathIndex = pathPosition; + return; + } + } + + // Engage: hold jump continuously during traverse/diagonal on ground behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); ctx.player().setSprinting(true); @@ -229,5 +265,54 @@ private static boolean shouldSuppressForHorizon(IPath path, int pathPosition, in } return false; } + + private static final class ElevationEval { + final boolean ascendHeavy; + final boolean dangerousDropSoon; + ElevationEval(boolean ascendHeavy, boolean dangerousDropSoon) { + this.ascendHeavy = ascendHeavy; + this.dangerousDropSoon = dangerousDropSoon; + } + } + + private ElevationEval evaluateElevationHorizon(IPlayerContext ctx, IPath path, int pathPosition, int dx, int dz) { + // Look ahead up to N positions to measure cumulative ascents and first significant drop + final int horizon = Math.min(8, path.length() - pathPosition - 1); + if (horizon <= 0) { + return new ElevationEval(false, false); + } + int ascends = 0; + int cumulativeClimb = 0; + int firstDropMagnitude = 0; + BetterBlockPos prev = path.positions().get(pathPosition); + for (int i = 1; i <= horizon; i++) { + BetterBlockPos cur = path.positions().get(pathPosition + i); + int dy = cur.y - prev.y; + if (dy > 0) { + cumulativeClimb += dy; + ascends++; + } else if (dy < 0 && firstDropMagnitude == 0) { + firstDropMagnitude = -dy; // positive magnitude + } + prev = cur; + } + + // Ascend-heavy if we climb at least 3 blocks within horizon or more than half of upcoming moves are ascends + boolean ascendHeavy = cumulativeClimb >= 3 || ascends >= Math.max(2, horizon / 2); + + // Dangerous drop if drop magnitude exceeds allowed no-water fall height + boolean dangerousDropSoon = false; + if (firstDropMagnitude > 0) { + int safeNoWater = Baritone.settings().maxFallHeightNoWater.value + 1; // +1 because landing block reduces effective fall + if (firstDropMagnitude > safeNoWater) { + // Check if landing in water would mitigate + BetterBlockPos landing = path.positions().get(Math.min(pathPosition + 1 + firstDropMagnitude, path.positions().size() - 1)); + if (!MovementHelper.isWater(BlockStateInterface.get(ctx, landing))) { + dangerousDropSoon = true; + } + } + } + return new ElevationEval(ascendHeavy, dangerousDropSoon); + } } diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index 98279fbc6..9bfb945bf 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -239,8 +239,14 @@ public boolean onTick() { if (!sprintNextTick) { ctx.player().setSprinting(false); // letting go of control doesn't make you stop sprinting actually, who thought } - // Centralized jump-sprint control - jumpSprintController.apply(behavior, ctx, movement, path, pathPosition); + // Centralized jump-sprint control (don't allow this to crash execution near goal edges) + try { + if (pathPosition < path.length() - 1) { + jumpSprintController.apply(behavior, ctx, movement, path, pathPosition); + } + } catch (Throwable t) { + logDebug("JumpSprintController apply error: " + t); + } ticksOnCurrent++; if (ticksOnCurrent > currentMovementOriginalCostEstimate + Baritone.settings().movementTimeoutTicks.value) { // only cancel if the total time has exceeded the initial estimate From 3a0ecea55819b9e7b66ef359f2bec20bc9734d07 Mon Sep 17 00:00:00 2001 From: aver <66864263+AverWasTaken@users.noreply.github.com> Date: Sun, 10 Aug 2025 14:46:07 -0700 Subject: [PATCH 4/5] owww my head stop it --- .../pathing/path/JumpSprintController.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/baritone/pathing/path/JumpSprintController.java b/src/main/java/baritone/pathing/path/JumpSprintController.java index 413477d0c..dddfd2f87 100644 --- a/src/main/java/baritone/pathing/path/JumpSprintController.java +++ b/src/main/java/baritone/pathing/path/JumpSprintController.java @@ -208,6 +208,15 @@ public void apply(PathingBehavior behavior, return; } + // Headroom check: avoid head-bonking into low ceilings (trees/leaves/etc.) in the next few tiles + if (!headroomClear(ctx, path, pathPosition, 3)) { + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + lastPathIndex = pathPosition; + return; + } + // For diagonals, preserve midair momentum by not toggling jump while airborne; only ensure sprint if (movement instanceof MovementDiagonal) { if (!onGround) { @@ -266,6 +275,20 @@ private static boolean shouldSuppressForHorizon(IPath path, int pathPosition, in return false; } + private boolean headroomClear(IPlayerContext ctx, IPath path, int pathPosition, int tilesAhead) { + int maxIdx = Math.min(path.positions().size() - 1, pathPosition + tilesAhead); + for (int i = pathPosition + 1; i <= maxIdx; i++) { + BetterBlockPos p = path.positions().get(i); + if (MovementHelper.avoidWalkingInto(BlockStateInterface.get(ctx, p.above()))) { + return false; + } + if (MovementHelper.avoidWalkingInto(BlockStateInterface.get(ctx, p.above(2)))) { + return false; + } + } + return true; + } + private static final class ElevationEval { final boolean ascendHeavy; final boolean dangerousDropSoon; From 96a4d35250f008e26c561ed6e1fb2d60cb20a862 Mon Sep 17 00:00:00 2001 From: aver <66864263+AverWasTaken@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:02:40 -0700 Subject: [PATCH 5/5] change settings & debug messages "wrong y coord" stfu --- src/api/java/baritone/api/Settings.java | 12 +++- .../movement/movements/MovementTraverse.java | 8 +-- .../pathing/path/JumpSprintController.java | 71 +++++++++++++++++-- .../baritone/pathing/path/PathExecutor.java | 33 ++++++--- 4 files changed, 102 insertions(+), 22 deletions(-) diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 80dbfe8f0..3425de165 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -811,7 +811,17 @@ public final class Settings { /** * Jump while sprinting on flat traverses for extra speed */ - public final Setting jumpsprint = new Setting<>(false); + public final Setting allowJumpSprint = new Setting<>(false); + + /** + * Allow jump-sprinting on long flat diagonals + */ + public final Setting allowJumpSprintDiagonal = new Setting<>(true); + + /** + * Allow head-hit jump pulses under low ceilings to maintain speed + */ + public final Setting allowHeadHits = new Setting<>(true); /** * When GetToBlockProcess or MineProcess fails to calculate a path, instead of just giving up, mark the closest instance diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index 60b1fca2b..cc51bf5c6 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -244,13 +244,9 @@ public MovementState updateState(MovementState state) { boolean isTheBridgeBlockThere = MovementHelper.canWalkOn(ctx, positionToPlace) || ladder || MovementHelper.canUseFrostWalker(ctx, positionToPlace); BlockPos feet = ctx.playerFeet(); if (feet.getY() != dest.getY() && !ladder) { - logDebug("Wrong Y coordinate"); - // Always keep sprint held while airborne during a traverse; PathExecutor manages jump timing + // When jump-sprinting across flat traverses, being airborne is expected; do not warn. + // Keep sprint held while airborne to maintain cadence; PathExecutor manages jump timing. state.setInput(Input.SPRINT, true); - if (feet.getY() < dest.getY()) { - System.out.println("In movement traverse"); - return state; // jump already held if applicable; keep sprint - } return state; } diff --git a/src/main/java/baritone/pathing/path/JumpSprintController.java b/src/main/java/baritone/pathing/path/JumpSprintController.java index dddfd2f87..f7ebe286b 100644 --- a/src/main/java/baritone/pathing/path/JumpSprintController.java +++ b/src/main/java/baritone/pathing/path/JumpSprintController.java @@ -56,6 +56,9 @@ public final class JumpSprintController implements Helper { private int lastPathIndex = -1; private static final int STABILIZE_TICKS_AFTER_TURN = 4; private int stabilizationTicksRemaining = 0; + private String lastDebugNote = null; + private static final int DEBUG_MIN_INTERVAL_TICKS = 20; // limit logs to ~once per second + private int lastDebugTick = -100000; /** * Apply jump timing for the current tick. Sprint is handled elsewhere. @@ -66,12 +69,13 @@ public void apply(PathingBehavior behavior, IPath path, int pathPosition) { ticks++; - if (!Baritone.settings().jumpsprint.value) { + if (!Baritone.settings().allowJumpSprint.value) { return; } // Consider flat straight traverses and long flat diagonals. Skip other movement types. - if (!(movement instanceof MovementTraverse || movement instanceof MovementDiagonal)) { + boolean diagonalAllowed = Baritone.settings().allowJumpSprintDiagonal.value; + if (!(movement instanceof MovementTraverse || (diagonalAllowed && movement instanceof MovementDiagonal))) { return; } @@ -85,6 +89,23 @@ public void apply(PathingBehavior behavior, return; } + // Aggressive low-ceiling boost: if headroom is blocked right now, spam jump while keeping sprint+forward + if (Baritone.settings().allowHeadHits.value) { + try { + BetterBlockPos headNow = ctx.playerFeet().above(); + boolean lowCeilingNow = MovementHelper.avoidWalkingInto(BlockStateInterface.get(ctx, headNow)) + || MovementHelper.avoidWalkingInto(BlockStateInterface.get(ctx, headNow.above())); + if (lowCeilingNow) { + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + debugOnce("jumpsprint: ceiling mash"); + lastPathIndex = pathPosition; + return; + } + } catch (Throwable ignored) {} + } + int dx = Integer.signum(movement.getDest().x - movement.getSrc().x); int dy = Integer.signum(movement.getDest().y - movement.getSrc().y); int dz = Integer.signum(movement.getDest().z - movement.getSrc().z); @@ -105,6 +126,7 @@ public void apply(PathingBehavior behavior, straightDz = dz; warmupTilesRemaining = WARMUP_TILES; stabilizationTicksRemaining = STABILIZE_TICKS_AFTER_TURN; // buffer after turn/diagonal change + debugOnce("jumpsprint: direction changed, warmup+stabilize"); } else if (pathAdvanced && warmupTilesRemaining > 0) { warmupTilesRemaining--; } @@ -123,10 +145,12 @@ public void apply(PathingBehavior behavior, boolean sameFlat = dx == ndx && dz == ndz; if (!sameFlat) { // turning soon; let movement logic handle inputs this tick + debugOnce("jumpsprint: suppressed (turn next)"); lastPathIndex = pathPosition; return; } if (next instanceof MovementAscend || next instanceof MovementParkour) { + debugOnce("jumpsprint: suppressed (ascend/parkour next)"); lastPathIndex = pathPosition; return; // let ascend/parkour manage their own jump timing } @@ -137,6 +161,7 @@ public void apply(PathingBehavior behavior, behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + debugOnce("jumpsprint: suppressed (horizon)"); lastPathIndex = pathPosition; return; } @@ -148,16 +173,18 @@ public void apply(PathingBehavior behavior, behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + debugOnce("jumpsprint: suppressed (ascend-heavy)"); lastPathIndex = pathPosition; return; } // If a dangerous drop is imminent (risk of fall damage), suppress jump-sprint to avoid overshooting the ledge if (eval.dangerousDropSoon) { behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); - behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); - behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + debugOnce("jumpsprint: suppressed (dangerous drop)"); lastPathIndex = pathPosition; - return; + return; } // If a small safe descend is next, allow downhill jump-sprint; ensure sprint held midair is already handled below @@ -169,6 +196,7 @@ public void apply(PathingBehavior behavior, boolean sameFlat2 = dx == ndx2 && dz == ndz2; if (!sameFlat2 || next2 instanceof MovementAscend || next2 instanceof MovementParkour) { // approaching a turn or ascend/parkour; leave inputs untouched for movement/PathExecutor to manage + debugOnce("jumpsprint: suppressed (2-ahead)"); lastPathIndex = pathPosition; return; } @@ -181,6 +209,7 @@ public void apply(PathingBehavior behavior, behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); wasOnGround = ctx.player().onGround(); + debugOnce("jumpsprint: suppressed (off-center)"); return; } @@ -191,6 +220,7 @@ public void apply(PathingBehavior behavior, behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); wasOnGround = onGround; + debugOnce("jumpsprint: suppressed (near end)"); lastPathIndex = pathPosition; return; } @@ -204,6 +234,9 @@ public void apply(PathingBehavior behavior, behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); wasOnGround = onGround; + if (warmupTilesRemaining > 0) debugOnce("jumpsprint: warmup"); + else if (stabilizationTicksRemaining > 0) debugOnce("jumpsprint: stabilize after turn"); + else debugOnce("jumpsprint: waiting (lateral vel)"); lastPathIndex = pathPosition; return; } @@ -213,6 +246,7 @@ public void apply(PathingBehavior behavior, behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + debugOnce("jumpsprint: suppressed (low headroom ahead)"); lastPathIndex = pathPosition; return; } @@ -222,6 +256,7 @@ public void apply(PathingBehavior behavior, if (!onGround) { behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); + debugOnce("jumpsprint: diagonal air preserve"); lastPathIndex = pathPosition; return; } @@ -231,6 +266,18 @@ public void apply(PathingBehavior behavior, behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); behavior.baritone.getInputOverrideHandler().setInputForceState(Input.MOVE_FORWARD, true); ctx.player().setSprinting(true); + debugOnce("jumpsprint: ENGAGE"); + + // Ceiling-boost: if there is a low ceiling directly above while sprinting, pulse jump to gain micro speed bursts + try { + BetterBlockPos head = ctx.playerFeet().above(); + boolean lowCeiling = MovementHelper.avoidWalkingInto(BlockStateInterface.get(ctx, head)) + || MovementHelper.avoidWalkingInto(BlockStateInterface.get(ctx, head.above())); + if (lowCeiling) { + behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, true); + } + } catch (Throwable ignored) { + } wasOnGround = onGround; lastPathIndex = pathPosition; @@ -240,6 +287,20 @@ public void apply(PathingBehavior behavior, } } + private void debugOnce(String note) { + if (note == null) { + return; + } + // Rate limit: only log if enough ticks passed since last log + if ((ticks - lastDebugTick) < DEBUG_MIN_INTERVAL_TICKS) { + return; + } + // Log even if same note (but still rate-limited) + logDebug(note); + lastDebugNote = note; + lastDebugTick = ticks; + } + private static double lateralOffsetFromCenterXZ(IPlayerContext ctx, BetterBlockPos anchor, int dx, int dz) { // Compute lateral offset from the center line of travel within the current tile double px = ctx.player().position().x; diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index 9bfb945bf..20f0eb153 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -222,7 +222,14 @@ public boolean onTick() { clearKeys(); return true; } - MovementStatus movementStatus = movement.update(); + MovementStatus movementStatus; + try { + movementStatus = movement.update(); + } catch (Throwable t) { + logDebug("Movement update error: " + t); + cancel(); + return true; + } if (movementStatus == UNREACHABLE || movementStatus == FAILED) { logDebug("Movement returns status " + movementStatus); cancel(); @@ -370,8 +377,9 @@ private boolean shouldSprintNextTick() { // Ensure sprint stays engaged during straight segments when jumpsprint is enabled, // but don't force sprint if an ascend/parkour is imminent (let those control cadence) - if (Baritone.settings().jumpsprint.value && (current instanceof MovementTraverse || current instanceof MovementDiagonal)) { - IMovement next = pathPosition < path.length() - 1 ? path.movements().get(pathPosition + 1) : null; + if (Baritone.settings().allowJumpSprint.value && (current instanceof MovementTraverse || current instanceof MovementDiagonal)) { + int movementsSize = path.movements().size(); + IMovement next = pathPosition < movementsSize - 1 ? path.movements().get(pathPosition + 1) : null; if (!(next instanceof MovementAscend) && !(next instanceof MovementParkour)) { behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, true); return true; @@ -381,9 +389,11 @@ private boolean shouldSprintNextTick() { // (Ascend handled above) // traverse requests sprinting, so we need to do this check first - if (current instanceof MovementTraverse && pathPosition < path.length() - 3) { - IMovement next = path.movements().get(pathPosition + 1); - if (next instanceof MovementAscend && sprintableAscend(ctx, (MovementTraverse) current, (MovementAscend) next, path.movements().get(pathPosition + 2))) { + if (current instanceof MovementTraverse) { + int movementsSize = path.movements().size(); + if (pathPosition < movementsSize - 3) { + IMovement next = path.movements().get(pathPosition + 1); + if (next instanceof MovementAscend && sprintableAscend(ctx, (MovementTraverse) current, (MovementAscend) next, path.movements().get(pathPosition + 2))) { if (skipNow(ctx, current)) { logDebug("Skipping traverse to straight ascend"); pathPosition++; @@ -394,6 +404,7 @@ private boolean shouldSprintNextTick() { } else { logDebug("Too far to the side to safely sprint ascend"); } + } } } @@ -405,7 +416,8 @@ private boolean shouldSprintNextTick() { // however, descend and ascend don't request sprinting, because they don't know the context of what movement comes after it if (current instanceof MovementDescend) { - if (pathPosition < path.length() - 2) { + int movementsSize = path.movements().size(); + if (pathPosition < movementsSize - 2) { // keep this out of onTick, even if that means a tick of delay before it has an effect IMovement next = path.movements().get(pathPosition + 1); if (MovementHelper.canUseFrostWalker(ctx, next.getDest().below())) { @@ -430,7 +442,7 @@ private boolean shouldSprintNextTick() { return false; } - if (pathPosition < path.length() - 2) { + if (pathPosition < movementsSize - 2) { IMovement next = path.movements().get(pathPosition + 1); if (next instanceof MovementAscend && current.getDirection().above().equals(next.getDirection().below())) { // a descend then an ascend in the same direction @@ -443,7 +455,7 @@ private boolean shouldSprintNextTick() { } if (canSprintFromDescendInto(ctx, current, next)) { - if (next instanceof MovementDescend && pathPosition < path.length() - 3) { + if (next instanceof MovementDescend && pathPosition < movementsSize - 3) { IMovement next_next = path.movements().get(pathPosition + 2); if (next_next instanceof MovementDescend && !canSprintFromDescendInto(ctx, next, next_next)) { return false; @@ -473,7 +485,8 @@ private boolean shouldSprintNextTick() { return true; } } - if (pathPosition < path.length() - 2 && prev instanceof MovementTraverse && sprintableAscend(ctx, (MovementTraverse) prev, (MovementAscend) current, path.movements().get(pathPosition + 1))) { + int movementsSize = path.movements().size(); + if (pathPosition < movementsSize - 2 && prev instanceof MovementTraverse && sprintableAscend(ctx, (MovementTraverse) prev, (MovementAscend) current, path.movements().get(pathPosition + 1))) { return true; } }