getBaritone() {
+ // noinspection ConstantConditions
+ if (EntityPlayerSP.class.isInstance(this)) {
+ return Optional.ofNullable(BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this));
+ } else {
+ return Optional.empty();
+ }
}
}
diff --git a/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java b/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java
index 4f6031d78..281ff96f5 100644
--- a/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java
+++ b/src/launch/java/baritone/launch/mixins/MixinEntityPlayerSP.java
@@ -72,22 +72,6 @@ public class MixinEntityPlayerSP {
}
}
- @Inject(
- method = "onUpdate",
- at = @At(
- value = "INVOKE",
- target = "net/minecraft/client/entity/EntityPlayerSP.onUpdateWalkingPlayer()V",
- shift = At.Shift.BY,
- by = 2
- )
- )
- private void onPostUpdate(CallbackInfo ci) {
- IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer((EntityPlayerSP) (Object) this);
- if (baritone != null) {
- baritone.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.POST));
- }
- }
-
@Redirect(
method = "onLivingUpdate",
at = @At(
diff --git a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java
index 097c72905..edc1e3fcc 100644
--- a/src/launch/java/baritone/launch/mixins/MixinMinecraft.java
+++ b/src/launch/java/baritone/launch/mixins/MixinMinecraft.java
@@ -20,6 +20,7 @@ package baritone.launch.mixins;
import baritone.api.BaritoneAPI;
import baritone.api.IBaritone;
import baritone.api.event.events.BlockInteractEvent;
+import baritone.api.event.events.PlayerUpdateEvent;
import baritone.api.event.events.TickEvent;
import baritone.api.event.events.WorldEvent;
import baritone.api.event.events.type.EventState;
@@ -84,7 +85,21 @@ public class MixinMinecraft {
baritone.getGameEventHandler().onTick(tickProvider.apply(EventState.PRE, type));
}
+ }
+ @Inject(
+ method = "runTick",
+ at = @At(
+ value = "INVOKE",
+ target = "net/minecraft/client/multiplayer/WorldClient.updateEntities()V",
+ shift = At.Shift.AFTER
+ )
+ )
+ private void postUpdateEntities(CallbackInfo ci) {
+ IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(this.player);
+ if (baritone != null) {
+ baritone.getGameEventHandler().onPlayerUpdate(new PlayerUpdateEvent(EventState.POST));
+ }
}
@Inject(
diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java
index 776e646af..957421970 100755
--- a/src/main/java/baritone/Baritone.java
+++ b/src/main/java/baritone/Baritone.java
@@ -101,8 +101,8 @@ public class Baritone implements IBaritone {
this.playerContext = new BaritonePlayerContext(this, mc);
{
- this.pathingBehavior = this.registerBehavior(PathingBehavior::new);
this.lookBehavior = this.registerBehavior(LookBehavior::new);
+ this.pathingBehavior = this.registerBehavior(PathingBehavior::new);
this.inventoryBehavior = this.registerBehavior(InventoryBehavior::new);
this.inputOverrideHandler = this.registerBehavior(InputOverrideHandler::new);
this.registerBehavior(WaypointBehavior::new);
diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java
index 67aa45e39..4ea0274d7 100644
--- a/src/main/java/baritone/behavior/LookBehavior.java
+++ b/src/main/java/baritone/behavior/LookBehavior.java
@@ -20,13 +20,12 @@ package baritone.behavior;
import baritone.Baritone;
import baritone.api.Settings;
import baritone.api.behavior.ILookBehavior;
-import baritone.api.event.events.PacketEvent;
-import baritone.api.event.events.PlayerUpdateEvent;
-import baritone.api.event.events.RotationMoveEvent;
-import baritone.api.event.events.WorldEvent;
-import baritone.api.utils.Helper;
+import baritone.api.behavior.look.IAimProcessor;
+import baritone.api.behavior.look.ITickableAimProcessor;
+import baritone.api.event.events.*;
import baritone.api.utils.IPlayerContext;
import baritone.api.utils.Rotation;
+import baritone.behavior.look.ForkableRandom;
import net.minecraft.network.play.client.CPacketPlayer;
import java.util.Optional;
@@ -44,14 +43,17 @@ public final class LookBehavior extends Behavior implements ILookBehavior {
private Rotation serverRotation;
/**
- * The last player rotation. Used when free looking
+ * The last player rotation. Used to restore the player's angle when using free look.
*
* @see Settings#freeLook
*/
private Rotation prevRotation;
+ private final AimProcessor processor;
+
public LookBehavior(Baritone baritone) {
super(baritone);
+ this.processor = new AimProcessor(baritone.getPlayerContext());
}
@Override
@@ -59,6 +61,18 @@ public final class LookBehavior extends Behavior implements ILookBehavior {
this.target = new Target(rotation, blockInteract);
}
+ @Override
+ public IAimProcessor getAimProcessor() {
+ return this.processor;
+ }
+
+ @Override
+ public void onTick(TickEvent event) {
+ if (event.getType() == TickEvent.Type.IN) {
+ this.processor.tick();
+ }
+ }
+
@Override
public void onPlayerUpdate(PlayerUpdateEvent event) {
if (this.target == null) {
@@ -67,34 +81,16 @@ public final class LookBehavior extends Behavior implements ILookBehavior {
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 (this.target.mode == Target.Mode.SERVER) {
this.prevRotation = new Rotation(ctx.player().rotationYaw, ctx.player().rotationPitch);
}
- final float oldYaw = ctx.playerRotations().getYaw();
- final float oldPitch = ctx.playerRotations().getPitch();
-
- float desiredYaw = this.target.rotation.getYaw();
- float desiredPitch = this.target.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
- if (desiredPitch == oldPitch) {
- desiredPitch = nudgeToLevel(desiredPitch);
- }
-
- desiredYaw += (Math.random() - 0.5) * Baritone.settings().randomLooking.value;
- desiredPitch += (Math.random() - 0.5) * Baritone.settings().randomLooking.value;
-
- ctx.player().rotationYaw = calculateMouseMove(oldYaw, desiredYaw);
- ctx.player().rotationPitch = calculateMouseMove(oldPitch, desiredPitch);
-
- if (this.target.mode == Target.Mode.CLIENT) {
- // The target can be invalidated now since it won't be needed for RotationMoveEvent
- this.target = null;
- }
+ final Rotation actual = this.processor.peekRotation(this.target.rotation);
+ ctx.player().rotationYaw = actual.getYaw();
+ ctx.player().rotationPitch = actual.getPitch();
break;
}
case POST: {
@@ -133,7 +129,8 @@ public final class LookBehavior extends Behavior implements ILookBehavior {
public void pig() {
if (this.target != null) {
- ctx.player().rotationYaw = this.target.rotation.getYaw();
+ final Rotation actual = this.processor.peekRotation(this.target.rotation);
+ ctx.player().rotationYaw = actual.getYaw();
}
}
@@ -148,36 +145,133 @@ public final class LookBehavior extends Behavior implements ILookBehavior {
@Override
public void onPlayerRotationMove(RotationMoveEvent event) {
if (this.target != null) {
- event.setYaw(this.target.rotation.getYaw());
+ final Rotation actual = this.processor.peekRotation(this.target.rotation);
+ event.setYaw(actual.getYaw());
+ event.setPitch(actual.getPitch());
}
}
- /**
- * Nudges the player's pitch to a regular level. (Between {@code -20} and {@code 10}, increments are by {@code 1})
- */
- private static float nudgeToLevel(float pitch) {
- if (pitch < -20) {
- return pitch + 1;
- } else if (pitch > 10) {
- return pitch - 1;
+ private static final class AimProcessor extends AbstractAimProcessor {
+
+ public AimProcessor(final IPlayerContext ctx) {
+ super(ctx);
+ }
+
+ @Override
+ protected Rotation getPrevRotation() {
+ // Implementation will use LookBehavior.serverRotation
+ return ctx.playerRotations();
}
- return pitch;
}
- private float calculateMouseMove(float current, float target) {
- final float delta = target - current;
- final int deltaPx = angleToMouse(delta);
- return current + mouseToAngle(deltaPx);
- }
+ private static abstract class AbstractAimProcessor implements ITickableAimProcessor {
- private int angleToMouse(float angleDelta) {
- final float minAngleChange = mouseToAngle(1);
- return Math.round(angleDelta / minAngleChange);
- }
+ protected final IPlayerContext ctx;
+ private final ForkableRandom rand;
+ private double randomYawOffset;
+ private double randomPitchOffset;
- private float mouseToAngle(int mouseDelta) {
- final float f = ctx.minecraft().gameSettings.mouseSensitivity * 0.6f + 0.2f;
- return mouseDelta * f * f * f * 8.0f * 0.15f;
+ public AbstractAimProcessor(IPlayerContext ctx) {
+ this.ctx = ctx;
+ this.rand = new ForkableRandom();
+ }
+
+ private AbstractAimProcessor(final AbstractAimProcessor source) {
+ this.ctx = source.ctx;
+ this.rand = source.rand.fork();
+ this.randomYawOffset = source.randomYawOffset;
+ this.randomPitchOffset = source.randomPitchOffset;
+ }
+
+ @Override
+ public final Rotation peekRotation(final Rotation rotation) {
+ final Rotation prev = this.getPrevRotation();
+
+ 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
+ if (desiredPitch == prev.getPitch()) {
+ desiredPitch = nudgeToLevel(desiredPitch);
+ }
+
+ desiredYaw += this.randomYawOffset;
+ desiredPitch += this.randomPitchOffset;
+
+ return new Rotation(
+ this.calculateMouseMove(prev.getYaw(), desiredYaw),
+ this.calculateMouseMove(prev.getPitch(), desiredPitch)
+ );
+ }
+
+ @Override
+ public final void tick() {
+ this.randomYawOffset = (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value;
+ this.randomPitchOffset = (this.rand.nextDouble() - 0.5) * Baritone.settings().randomLooking.value;
+ }
+
+ @Override
+ public final void advance(int ticks) {
+ for (int i = 0; i < ticks; i++) {
+ this.tick();
+ }
+ }
+
+ @Override
+ public Rotation nextRotation(final Rotation rotation) {
+ final Rotation actual = this.peekRotation(rotation);
+ this.tick();
+ return actual;
+ }
+
+ @Override
+ public final ITickableAimProcessor fork() {
+ return new AbstractAimProcessor(this) {
+
+ private Rotation prev = AbstractAimProcessor.this.getPrevRotation();
+
+ @Override
+ public Rotation nextRotation(final Rotation rotation) {
+ return (this.prev = super.nextRotation(rotation));
+ }
+
+ @Override
+ protected Rotation getPrevRotation() {
+ return this.prev;
+ }
+ };
+ }
+
+ protected abstract Rotation getPrevRotation();
+
+ /**
+ * 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) {
+ return pitch + 1;
+ } else if (pitch > 10) {
+ return pitch - 1;
+ }
+ return pitch;
+ }
+
+ private float calculateMouseMove(float current, float target) {
+ final float delta = target - current;
+ final int deltaPx = angleToMouse(delta);
+ return current + mouseToAngle(deltaPx);
+ }
+
+ private int angleToMouse(float angleDelta) {
+ final float minAngleChange = mouseToAngle(1);
+ return Math.round(angleDelta / minAngleChange);
+ }
+
+ private float mouseToAngle(int mouseDelta) {
+ final float f = ctx.minecraft().gameSettings.mouseSensitivity * 0.6f + 0.2f;
+ return mouseDelta * f * f * f * 8.0f * 0.15f;
+ }
}
private static class Target {
diff --git a/src/main/java/baritone/behavior/look/ForkableRandom.java b/src/main/java/baritone/behavior/look/ForkableRandom.java
new file mode 100644
index 000000000..5f5f942d2
--- /dev/null
+++ b/src/main/java/baritone/behavior/look/ForkableRandom.java
@@ -0,0 +1,85 @@
+/*
+ * 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.behavior.look;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.LongSupplier;
+
+/**
+ * Implementation of Xoroshiro256++
+ *
+ * Extended to produce random double-precision floating point numbers, and allow copies to be spawned via {@link #fork},
+ * which share the same internal state as the source object.
+ *
+ * @author Brady
+ */
+public final class ForkableRandom {
+
+ private static final double DOUBLE_UNIT = 0x1.0p-53;
+
+ private final long[] s;
+
+ public ForkableRandom() {
+ this(System.nanoTime() ^ System.currentTimeMillis());
+ }
+
+ public ForkableRandom(long seedIn) {
+ final AtomicLong seed = new AtomicLong(seedIn);
+ final LongSupplier splitmix64 = () -> {
+ long z = seed.addAndGet(0x9e3779b97f4a7c15L);
+ z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L;
+ z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL;
+ return z ^ (z >>> 31);
+ };
+ this.s = new long[] {
+ splitmix64.getAsLong(),
+ splitmix64.getAsLong(),
+ splitmix64.getAsLong(),
+ splitmix64.getAsLong()
+ };
+ }
+
+ private ForkableRandom(long[] s) {
+ this.s = s;
+ }
+
+ public double nextDouble() {
+ return (this.next() >>> 11) * DOUBLE_UNIT;
+ }
+
+ public long next() {
+ final long result = rotl(this.s[0] + this.s[3], 23) + this.s[0];
+ final long t = this.s[1] << 17;
+ this.s[2] ^= this.s[0];
+ this.s[3] ^= this.s[1];
+ this.s[1] ^= this.s[2];
+ this.s[0] ^= this.s[3];
+ this.s[2] ^= t;
+ this.s[3] = rotl(this.s[3], 45);
+ return result;
+ }
+
+ public ForkableRandom fork() {
+ return new ForkableRandom(Arrays.copyOf(this.s, 4));
+ }
+
+ private static long rotl(long x, int k) {
+ return (x << k) | (x >>> (64 - k));
+ }
+}