diff --git a/src/api/java/baritone/api/event/events/SprintStateEvent.java b/src/api/java/baritone/api/event/events/SprintStateEvent.java
new file mode 100644
index 00000000..e7b8d193
--- /dev/null
+++ b/src/api/java/baritone/api/event/events/SprintStateEvent.java
@@ -0,0 +1,42 @@
+/*
+ * 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.api.event.events;
+
+import baritone.api.event.events.type.ManagedPlayerEvent;
+import net.minecraft.client.entity.EntityPlayerSP;
+
+/**
+ * @author Brady
+ * @since 1/18/2019
+ */
+public class SprintStateEvent extends ManagedPlayerEvent {
+
+ private Boolean state;
+
+ public SprintStateEvent(EntityPlayerSP player) {
+ super(player);
+ }
+
+ public final void setState(boolean state) {
+ this.state = state;
+ }
+
+ public final Boolean getState() {
+ return this.state;
+ }
+}
diff --git a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java
index 135c29a7..71045768 100644
--- a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java
+++ b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java
@@ -57,6 +57,9 @@ public interface AbstractGameEventListener extends IGameEventListener {
@Override
default void onPlayerRotationMove(RotationMoveEvent event) {}
+ @Override
+ default void onPlayerSprintState(SprintStateEvent event) {}
+
@Override
default void onBlockInteract(BlockInteractEvent event) {}
diff --git a/src/api/java/baritone/api/event/listener/IGameEventListener.java b/src/api/java/baritone/api/event/listener/IGameEventListener.java
index 0572cbe9..dc471e5f 100644
--- a/src/api/java/baritone/api/event/listener/IGameEventListener.java
+++ b/src/api/java/baritone/api/event/listener/IGameEventListener.java
@@ -109,6 +109,14 @@ public interface IGameEventListener {
*/
void onPlayerRotationMove(RotationMoveEvent event);
+ /**
+ * Called whenever the sprint keybind state is checked in {@link EntityPlayerSP#onLivingUpdate}
+ *
+ * @param event The event
+ * @see EntityPlayerSP#onLivingUpdate()
+ */
+ void onPlayerSprintState(SprintStateEvent event);
+
/**
* Called when the local player interacts with a block, whether it is breaking or opening/placing.
*
diff --git a/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java b/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java
index 9c9509f0..2f87b72d 100644
--- a/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java
+++ b/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java
@@ -21,8 +21,10 @@ import baritone.api.BaritoneAPI;
import baritone.api.behavior.IPathingBehavior;
import baritone.api.event.events.ChatEvent;
import baritone.api.event.events.PlayerUpdateEvent;
+import baritone.api.event.events.SprintStateEvent;
import baritone.api.event.events.type.EventState;
import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.client.settings.KeyBinding;
import net.minecraft.entity.player.PlayerCapabilities;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -87,4 +89,18 @@ public class MixinEntityPlayerSP {
IPathingBehavior pathingBehavior = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this).getPathingBehavior();
return !pathingBehavior.isPathing() && capabilities.allowFlying;
}
+
+ @Redirect(
+ method = "onLivingUpdate",
+ at = @At(
+ value = "INVOKE",
+ target = "net/minecraft/client/settings/KeyBinding.isKeyDown()Z"
+ )
+ )
+ private boolean isKeyDown(KeyBinding keyBinding) {
+ EntityPlayerSP self = (EntityPlayerSP) (Object) this;
+ SprintStateEvent event = new SprintStateEvent(self);
+ BaritoneAPI.getProvider().getBaritoneForPlayer(self).getGameEventHandler().onPlayerSprintState(event);
+ return event.getState() == null ? keyBinding.isKeyDown() : event.getState();
+ }
}
diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java
index 8c5fb8a4..92eb242e 100644
--- a/src/main/java/baritone/behavior/PathingBehavior.java
+++ b/src/main/java/baritone/behavior/PathingBehavior.java
@@ -19,10 +19,7 @@ package baritone.behavior;
import baritone.Baritone;
import baritone.api.behavior.IPathingBehavior;
-import baritone.api.event.events.PathEvent;
-import baritone.api.event.events.PlayerUpdateEvent;
-import baritone.api.event.events.RenderEvent;
-import baritone.api.event.events.TickEvent;
+import baritone.api.event.events.*;
import baritone.api.pathing.calc.IPath;
import baritone.api.pathing.goals.Goal;
import baritone.api.pathing.goals.GoalXZ;
@@ -98,6 +95,13 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
dispatchEvents();
}
+ @Override
+ public void onPlayerSprintState(SprintStateEvent event) {
+ if (current != null) {
+ event.setState(current.isSprinting());
+ }
+ }
+
private void tickPath() {
if (pauseRequestedLastTick && safeToCancel) {
pauseRequestedLastTick = false;
diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java
index e85c46de..3b327002 100644
--- a/src/main/java/baritone/event/GameEventHandler.java
+++ b/src/main/java/baritone/event/GameEventHandler.java
@@ -121,6 +121,11 @@ public final class GameEventHandler implements IEventBus, Helper {
listeners.forEach(l -> l.onPlayerRotationMove(event));
}
+ @Override
+ public void onPlayerSprintState(SprintStateEvent event) {
+ listeners.forEach(l -> l.onPlayerSprintState(event));
+ }
+
@Override
public void onBlockInteract(BlockInteractEvent event) {
listeners.forEach(l -> l.onBlockInteract(event));
diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java
index cad144aa..ef856f42 100644
--- a/src/main/java/baritone/pathing/path/PathExecutor.java
+++ b/src/main/java/baritone/pathing/path/PathExecutor.java
@@ -79,6 +79,8 @@ public class PathExecutor implements IPathExecutor, Helper {
private PathingBehavior behavior;
private IPlayerContext ctx;
+ private boolean sprintNextTick;
+
public PathExecutor(PathingBehavior behavior, IPath path) {
this.behavior = behavior;
this.ctx = behavior.ctx;
@@ -269,7 +271,7 @@ public class PathExecutor implements IPathExecutor, Helper {
onTick();
return true;
} else {
- sprintIfRequested();
+ sprintNextTick = shouldSprintNextTick();
ticksOnCurrent++;
if (ticksOnCurrent > currentMovementOriginalCostEstimate + Baritone.settings().movementTimeoutTicks.get()) {
// only cancel if the total time has exceeded the initial estimate
@@ -371,21 +373,17 @@ public class PathExecutor implements IPathExecutor, Helper {
return true;
}
- private void sprintIfRequested() {
+ private boolean shouldSprintNextTick() {
// first and foremost, if allowSprint is off, or if we don't have enough hunger, don't try and sprint
if (!new CalculationContext(behavior.baritone).canSprint) {
behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, false);
- ctx.player().setSprinting(false);
- return;
+ return false;
}
// if the movement requested sprinting, then we're done
if (behavior.baritone.getInputOverrideHandler().isInputForcedDown(Input.SPRINT)) {
behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, false);
- if (!ctx.player().isSprinting()) {
- ctx.player().setSprinting(true);
- }
- return;
+ return true;
}
// we'll take it from here, no need for minecraft to see we're holding down control and sprint for us
@@ -397,30 +395,23 @@ public class PathExecutor implements IPathExecutor, Helper {
if (((MovementDescend) current).safeMode()) {
logDebug("Sprinting would be unsafe");
- ctx.player().setSprinting(false);
- return;
+ return false;
}
IMovement next = path.movements().get(pathPosition + 1);
if (next instanceof MovementAscend && current.getDirection().up().equals(next.getDirection().down())) {
// a descend then an ascend in the same direction
- if (!ctx.player().isSprinting()) {
- ctx.player().setSprinting(true);
- }
pathPosition++;
// okay to skip clearKeys and / or onChangeInPathPosition here since this isn't possible to repeat, since it's asymmetric
logDebug("Skipping descend to straight ascend");
- return;
+ return true;
}
if (canSprintInto(ctx, current, next)) {
if (ctx.playerFeet().equals(current.getDest())) {
pathPosition++;
onChangeInPathPosition();
}
- if (!ctx.player().isSprinting()) {
- ctx.player().setSprinting(true);
- }
- return;
+ return true;
}
//logDebug("Turning off sprinting " + movement + " " + next + " " + movement.getDirection() + " " + next.getDirection().down() + " " + next.getDirection().down().equals(movement.getDirection()));
}
@@ -430,14 +421,11 @@ public class PathExecutor implements IPathExecutor, Helper {
BlockPos center = current.getSrc().up();
if (ctx.player().posY >= center.getY()) { // playerFeet adds 0.1251 to account for soul sand
behavior.baritone.getInputOverrideHandler().setInputForceState(Input.JUMP, false);
- if (!ctx.player().isSprinting()) {
- ctx.player().setSprinting(true);
- }
- return;
+ return true;
}
}
}
- ctx.player().setSprinting(false);
+ return false;
}
private static boolean canSprintInto(IPlayerContext ctx, IMovement current, IMovement next) {
@@ -532,4 +520,8 @@ public class PathExecutor implements IPathExecutor, Helper {
public Set toWalkInto() {
return Collections.unmodifiableSet(toWalkInto);
}
+
+ public boolean isSprinting() {
+ return sprintNextTick;
+ }
}