diff --git a/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java b/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java index 599f7906a..0ba82c42d 100644 --- a/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java +++ b/src/main/java/com/lambda/mixin/baritone/MixinLookBehavior.java @@ -36,7 +36,7 @@ void onTargetUpdate(Rotation rotation, boolean blockInteract, CallbackInfo ci) { LookBehavior instance = ((LookBehavior) (Object) this); if (instance.baritone != BaritoneUtils.getPrimary()) return; - RotationManager.BaritoneProcessor.handleBaritoneRotation(rotation.getYaw(), rotation.getPitch()); + RotationManager.handleBaritoneRotation(rotation.getYaw(), rotation.getPitch()); ci.cancel(); } diff --git a/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java index c47dddad4..c31ed4b87 100644 --- a/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java +++ b/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java @@ -37,7 +37,6 @@ import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.MovementType; import net.minecraft.util.Hand; -import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -60,63 +59,43 @@ public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile) { super(world, profile); } - @Shadow protected abstract void autoJump(float dx, float dz); - - /** - * Post movement events and applies the modified player velocity - */ - @Inject(method = "move", at = @At("HEAD"), cancellable = true) - void onMove(MovementType movementType, Vec3d movement, CallbackInfo ci) { - ClientPlayerEntity self = (ClientPlayerEntity) (Object) this; - if (self != Lambda.getMc().player) return; - - ci.cancel(); - - float prevX = (float) self.getX(); - float prevZ = (float) self.getZ(); - + @Redirect(method = "move", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;move(Lnet/minecraft/entity/MovementType;Lnet/minecraft/util/math/Vec3d;)V")) + private void emitMovementEvents(AbstractClientPlayerEntity instance, MovementType movementType, Vec3d movement) { EventFlow.post(new MovementEvent.Player.Pre(movementType, movement)); super.move(movementType, movement); EventFlow.post(new MovementEvent.Player.Post(movementType, movement)); - - float deltaX = (float) self.getX() - prevX; - float deltaZ = (float) self.getZ() - prevZ; - - this.autoJump(deltaX, deltaZ); - this.distanceMoved = this.distanceMoved + MathHelper.hypot(deltaX, deltaZ) * 0.6F; } @Redirect(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/input/Input;tick()V")) void processMovement(Input input) { input.tick(); RotationManager.processRotations(); - RotationManager.BaritoneProcessor.processPlayerMovement(input); + RotationManager.redirectStrafeInputs(input); EventFlow.post(new MovementEvent.InputUpdate(input)); } /** - * Posts the {@link MovementEvent.Sprint} event - *
{@code
-     * if (this.isSprinting()) {
-     *     boolean bl8 = !this.input.hasForwardMovement() || !this.canSprint();
-     *     boolean bl9 = bl8 || this.horizontalCollision && !this.collidedSoftly || this.isTouchingWater() && !this.isSubmergedInWater();
-     *     if (this.isSwimming()) {
-     *         if (!this.isOnGround() && !this.input.sneaking && bl8 || !this.isTouchingWater()) {
-     *             this.setSprinting(false);
-     *         }
-     *     } else if (bl9) {
-     *         this.setSprinting(false);
-     *     }
-     * }
-     * }
+ * Overwrites the movement packet update function to use our code */ + @Inject(method = "sendMovementPackets", at = @At(value = "HEAD"), cancellable = true) + void sendLambdaMovement(CallbackInfo ci) { + ci.cancel(); + PlayerPacketManager.sendPlayerPackets(); + autoJumpEnabled = Lambda.getMc().options.getAutoJump().getValue(); + } + + @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;sendSneakingPacket()V")) + void sendSneakingPacket(ClientPlayerEntity entity) { + PlayerPacketManager.sendSneakPackets(); + } + @Redirect(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isSprinting()Z")) boolean isSprinting(ClientPlayerEntity entity) { return EventFlow.post(new MovementEvent.Sprint(entity.isSprinting())).getSprint(); } @Inject(method = "isSneaking", at = @At(value = "HEAD"), cancellable = true) - void redirectSneaking(CallbackInfoReturnable cir) { + void injectSneakingInput(CallbackInfoReturnable cir) { ClientPlayerEntity self = (ClientPlayerEntity) (Object) this; if (self != Lambda.getMc().player) return; @@ -124,16 +103,6 @@ void redirectSneaking(CallbackInfoReturnable cir) { cir.setReturnValue(EventFlow.post(new MovementEvent.Sneak(self.input.playerInput.sneak())).getSneak()); } - /** - * Overwrites the movement packet update function to use our code - */ - @Inject(method = "sendMovementPackets", at = @At(value = "HEAD"), cancellable = true) - void sendBegin(CallbackInfo ci) { - ci.cancel(); - PlayerPacketManager.sendPlayerPackets(); - autoJumpEnabled = Lambda.getMc().options.getAutoJump().getValue(); - } - @WrapMethod(method = "tick") void onTick(Operation original) { EventFlow.post(TickEvent.Player.Pre.INSTANCE); diff --git a/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java index 3f83d2733..ce93b0ff5 100644 --- a/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java +++ b/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java @@ -23,10 +23,11 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import net.minecraft.client.render.entity.LivingEntityRenderer; import net.minecraft.entity.LivingEntity; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import java.util.Objects; +import static com.lambda.util.math.LinearKt.lerp; // This mixin's purpose is to set the player's pitch the current render pitch to correctly show the rotation // regardless of the camera position @@ -48,9 +49,12 @@ public class LivingEntityRendererMixin { * } */ @WrapOperation(method = "updateRenderState(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/client/render/entity/state/LivingEntityRenderState;F)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;getLerpedPitch(F)F")) - private float wrapGetLerpedPitch(LivingEntity instance, float v, Operation original) { - return (instance == Lambda.getMc().player) - ? Objects.requireNonNullElse(RotationManager.getHeadPitch(), original.call(instance, v)) - : original.call(instance, v); + private float wrapGetLerpedPitch(LivingEntity livingEntity, float v, Operation original) { + Float headPitch = RotationManager.getHeadPitch(); + if (livingEntity != Lambda.getMc().player || headPitch == null) { + return original.call(livingEntity, v); + } + + return lerp(v, RotationManager.getPrevServerRotation().getPitchF(), headPitch); } } diff --git a/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt b/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt index 9a98d011d..fea59387d 100644 --- a/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt +++ b/src/main/kotlin/com/lambda/event/events/PlayerPacketEvent.kt @@ -30,7 +30,6 @@ sealed class PlayerPacketEvent { var rotation: Rotation, var onGround: Boolean, var isSprinting: Boolean, - var isSneaking: Boolean, var isCollidingHorizontally: Boolean, ) : ICancellable by Cancellable() diff --git a/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt b/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt index 72d94f117..b092a05c6 100644 --- a/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt +++ b/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt @@ -22,15 +22,11 @@ import com.lambda.event.EventFlow.post import com.lambda.event.EventFlow.postChecked import com.lambda.event.events.PlayerPacketEvent import com.lambda.interaction.request.rotating.Rotation +import com.lambda.interaction.request.rotating.Rotation.Companion.rotation import com.lambda.interaction.request.rotating.RotationManager import com.lambda.threading.runSafe import com.lambda.util.collections.LimitedOrderedSet import com.lambda.util.math.approximate -import com.lambda.util.math.component1 -import com.lambda.util.math.component2 -import com.lambda.util.math.component3 -import com.lambda.util.player.MovementUtils.motionX -import com.lambda.util.player.MovementUtils.motionZ import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.Full import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.LookAndOnGround @@ -46,6 +42,7 @@ object PlayerPacketManager { var lastSprint = false var lastSneak = false var lastOnGround = false + var lastHorizontalCollision = false private var sendTicks = 0 @@ -57,7 +54,6 @@ object PlayerPacketManager { RotationManager.activeRotation, player.isOnGround, player.isSprinting, - player.isSneaking, player.horizontalCollision, ).post { updatePlayerPackets(this) @@ -65,72 +61,59 @@ object PlayerPacketManager { } } + @JvmStatic + fun sendSneakPackets() { + runSafe { + val sneaking = player.isSneaking + if (sneaking == lastSneak) return@runSafe + val mode = if (sneaking) { + ClientCommandC2SPacket.Mode.PRESS_SHIFT_KEY + } else { + ClientCommandC2SPacket.Mode.RELEASE_SHIFT_KEY + } + connection.sendPacket(ClientCommandC2SPacket(player, mode)) + lastSneak = sneaking + } + } + private fun SafeContext.updatePlayerPackets(new: PlayerPacketEvent.Pre) { configurations.add(new) reportSprint(lastSprint, new.isSprinting) - reportSneak(lastSneak, new.isSneaking) if (mc.cameraEntity != player) return - val rotation = new.rotation val position = new.position - val (yaw, pitch) = rotation.float + val (yaw, pitch) = new.rotation val onGround = new.onGround val isCollidingHorizontally = new.isCollidingHorizontally - if (player.hasVehicle()) { - connection.sendPacket( - Full( - player.motionX, - -999.0, - player.motionZ, - yaw, - pitch, - onGround, - isCollidingHorizontally, - ) - ) - - return - } - - val updatePosition = position.approximate(lastPosition, 2.0E-4) || ++sendTicks >= 20 - // has to be different in float precision - val updateRotation = !rotation.equalFloat(lastRotation) - - val (x, y, z) = position - - val packet = when { - updatePosition && updateRotation -> - Full(x, y, z, yaw, pitch, onGround, isCollidingHorizontally) - - updatePosition -> - PositionAndOnGround(x, y, z, onGround, isCollidingHorizontally) + val updatePosition = position.approximate(lastPosition) || ++sendTicks >= 20 + val updateRotation = lastRotation.yaw != yaw || lastRotation.pitch != pitch - updateRotation -> - LookAndOnGround(yaw, pitch, onGround, isCollidingHorizontally) - - lastOnGround != onGround -> + when { + updatePosition && updateRotation -> Full(position, yaw.toFloat(), pitch.toFloat(), onGround, isCollidingHorizontally) + updatePosition -> PositionAndOnGround(position, onGround, isCollidingHorizontally) + updateRotation -> LookAndOnGround(yaw.toFloat(), pitch.toFloat(), onGround, isCollidingHorizontally) + lastOnGround != onGround || lastHorizontalCollision != isCollidingHorizontally -> { OnGroundOnly(onGround, isCollidingHorizontally) - + } else -> null - } - - packet?.let { + }?.let { PlayerPacketEvent.Send(it).postChecked { connection.sendPacket(this.packet) if (updatePosition) { - sendTicks = 0 lastPosition = position + sendTicks = 0 } if (updateRotation) { - lastRotation = rotation + lastRotation = new.rotation } lastOnGround = onGround + lastHorizontalCollision = isCollidingHorizontally } } @@ -152,19 +135,6 @@ object PlayerPacketManager { connection.sendPacket(ClientCommandC2SPacket(player, state)) lastSprint = new } - - fun SafeContext.reportSneak(previous: Boolean, new: Boolean) { - if (previous == new) return - - val state = if (new) { - ClientCommandC2SPacket.Mode.PRESS_SHIFT_KEY - } else { - ClientCommandC2SPacket.Mode.RELEASE_SHIFT_KEY - } - - connection.sendPacket(ClientCommandC2SPacket(player, state)) - lastSneak = new - } } diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt index bd141cccd..7d17bbe20 100644 --- a/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt +++ b/src/main/kotlin/com/lambda/interaction/request/rotating/Rotation.kt @@ -108,42 +108,31 @@ data class Rotation(val yaw: Double, val pitch: Double) { fun Rotation.lerp(other: Rotation, delta: Double): Rotation { // Calculate the wrapped difference to ensure we take the shortest path val yawDiff = wrap(other.yaw - this.yaw) - val pitchDiff = wrap(other.pitch - this.pitch) + val pitchDiff = other.pitch - this.pitch - // Apply the delta to the wrapped difference - val yaw = wrap(this.yaw + delta * yawDiff) - val pitch = wrap(this.pitch + delta * pitchDiff) + // Apply without wrapping the absolute yaw (keep it continuous) + val yaw = this.yaw + delta * yawDiff + val pitch = (this.pitch + delta * pitchDiff).coerceIn(-90.0, 90.0) return Rotation(yaw, pitch) } fun Rotation.slerp(other: Rotation, speed: Double): Rotation { val yawDiff = wrap(other.yaw - yaw) - val pitchDiff = wrap(other.pitch - pitch) + val pitchDiff = other.pitch - pitch - val diff = hypot(yawDiff, pitchDiff) + val diff = hypot(yawDiff, pitchDiff).let { if (it == 0.0) 1.0 else it } val yawSpeed = abs(yawDiff / diff) * speed val pitchSpeed = abs(pitchDiff / diff) * speed - val yaw = wrap(yaw + yawDiff.coerceIn(-yawSpeed, yawSpeed)) - val pitch = wrap(pitch + pitchDiff.coerceIn(-pitchSpeed, pitchSpeed)) + // Apply without wrapping the absolute yaw (keep it continuous) + val yaw = yaw + yawDiff.coerceIn(-yawSpeed, yawSpeed) + val pitch = (pitch + pitchDiff.coerceIn(-pitchSpeed, pitchSpeed)).coerceIn(-90.0, 90.0) return Rotation(yaw, pitch) } - fun Rotation.fixSensitivity(prev: Rotation): Rotation { - val f = mc.options.mouseSensitivity.value * 0.6 + 0.2 - val gcd = f * f * f * 8.0 * 0.15F - - val r1 = Vec2d(prev.yaw, prev.pitch) - val r2 = Vec2d(this.yaw, this.pitch) - val delta = ((r2 - r1) / gcd).roundToInt() * gcd - val fixed = r1 + delta - - return Rotation(fixed.x, fixed.y.coerceIn(-90.0, 90.0)) - } - fun Vec3d.rotationTo(vec: Vec3d): Rotation { val diffX = vec.x - x val diffY = vec.y - y @@ -152,8 +141,9 @@ data class Rotation(val yaw: Double, val pitch: Double) { val yawRad = atan2(diffZ, diffX) val pitchRad = -atan2(diffY, hypot(diffX, diffZ)) - val yaw = wrap(yawRad.toDegree() - 90.0) - val pitch = wrap(pitchRad.toDegree()) + // Target yaw can be normalized; our slerp keeps absolute yaw continuous + val yaw = yawRad.toDegree() - 90.0 + val pitch = pitchRad.toDegree().coerceIn(-90.0, 90.0) return Rotation(yaw, pitch) } diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt index dbc0d474c..d14bbcead 100644 --- a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt @@ -22,14 +22,12 @@ import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.PacketEvent -import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.rotating.Rotation.Companion.slerp -import com.lambda.interaction.request.rotating.Rotation.Companion.wrap import com.lambda.interaction.request.rotating.visibilty.lookAt import com.lambda.module.modules.client.Baritone import com.lambda.threading.runGameScheduled @@ -41,10 +39,11 @@ import com.lambda.util.math.Vec2d import com.lambda.util.math.lerp import net.minecraft.client.input.Input import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket +import net.minecraft.util.PlayerInput import net.minecraft.util.math.Vec2f +import kotlin.math.PI +import kotlin.math.atan2 import kotlin.math.cos -import kotlin.math.round -import kotlin.math.sign import kotlin.math.sin object RotationManager : RequestHandler( @@ -56,6 +55,7 @@ object RotationManager : RequestHandler( ) { var activeRotation = Rotation.ZERO var serverRotation = Rotation.ZERO + @JvmStatic var prevServerRotation = Rotation.ZERO var activeRequest: RotationRequest? = null @@ -120,11 +120,95 @@ object RotationManager : RequestHandler( } } + @JvmStatic + fun handleBaritoneRotation(yaw: Float, pitch: Float) { + lookAt(Rotation(yaw, pitch)).requestBy(Baritone.rotation) + } + + @JvmStatic + fun redirectStrafeInputs(input: Input) = runSafe { + val movementYaw = movementYaw ?: return@runSafe + val playerYaw = player.yaw + + if (movementYaw.minus(playerYaw).rem(360f).let { it * it } < 0.001f) return@runSafe + + val originalStrafe = input.movementVector.x + val originalForward = input.movementVector.y + + if (originalStrafe == 0.0f && originalForward == 0.0f) return@runSafe + + val deltaYawRad = (playerYaw - movementYaw).toRadian() + + val cos = cos(deltaYawRad) + val sin = sin(deltaYawRad) + val newStrafe = originalStrafe * cos - originalForward * sin + val newForward = originalStrafe * sin + originalForward * cos + + val angle = atan2(newStrafe.toDouble(), newForward.toDouble()) + + // Define the boundaries for our 8 sectors (in radians). Each sector is 45 degrees (PI/4). + val sector = (PI / 4.0).toFloat() + val boundary = (PI / 8.0).toFloat() // The halfway point between sectors (22.5 degrees) + + var pressForward = false + var pressBackward = false + var pressLeft = false + var pressRight = false + + // Determine which 45-degree sector the angle falls into and set the corresponding keys. + when { + angle > -boundary && angle <= boundary -> { + pressForward = true + } + angle > boundary && angle <= boundary + sector -> { + pressForward = true + pressLeft = true + } + angle > boundary + sector && angle <= boundary + 2 * sector -> { + pressLeft = true + } + angle > boundary + 2 * sector && angle <= boundary + 3 * sector -> { + pressBackward = true + pressLeft = true + } + angle > boundary + 3 * sector || angle <= -(boundary + 3 * sector) -> { + pressBackward = true + } + angle > -(boundary + 3 * sector) && angle <= -(boundary + 2 * sector) -> { + pressBackward = true + pressRight = true + } + angle > -(boundary + 2 * sector) && angle <= -(boundary + sector) -> { + pressRight = true + } + angle > -(boundary + sector) && angle <= -boundary -> { + pressForward = true + pressRight = true + } + } + + input.playerInput = PlayerInput( + pressForward, + pressBackward, + pressLeft, + pressRight, + input.playerInput.jump(), + input.playerInput.sneak(), + input.playerInput.sprint() + ) + + val x = multiplier(input.playerInput.left(), input.playerInput.right()) + val y = multiplier(input.playerInput.forward(), input.playerInput.backward()) + input.movementVector = Vec2f(x, y).normalize() + } + + private fun multiplier(positive: Boolean, negative: Boolean) = + ((if (positive) 1 else 0) - (if (negative) 1 else 0)).toFloat() + fun onRotationSend() { prevServerRotation = serverRotation - serverRotation = activeRotation/*.fixSensitivity(prevServerRotation)*/ + serverRotation = activeRotation - // Handle LOCK mode if (activeRequest?.rotationMode == RotationMode.Lock) { mc.player?.yaw = serverRotation.yawF mc.player?.pitch = serverRotation.pitchF @@ -141,7 +225,8 @@ object RotationManager : RequestHandler( val speedMultiplier = if (request.keepTicks < 0) 1.0 else request.speedMultiplier val turnSpeed = request.turnSpeed * speedMultiplier - serverRotation.slerp(rotationTo, turnSpeed).wrap() + // Important: do NOT wrap the result yaw; keep it continuous to match vanilla packets + serverRotation.slerp(rotationTo, turnSpeed) } ?: player.rotation } @@ -197,80 +282,5 @@ object RotationManager : RequestHandler( return Vec2d(rot.yaw, rot.pitch) } - object BaritoneProcessor { - private var baritoneContext: RotationRequest? = null - - private val movementYawList = arrayOf( - 0.0, 45.0, - 90.0, 135.0, - 180.0, 225.0, - 270.0, 315.0, - ) - - @JvmStatic - fun handleBaritoneRotation(yaw: Float, pitch: Float) { - baritoneContext = lookAt(Rotation(yaw, pitch)).requestBy(Baritone.rotation) - } - - init { - listenUnsafe { - baritoneContext = null - } - } - - @JvmStatic - fun processPlayerMovement(input: Input) = runSafe { - // The yaw relative to which the movement was constructed - val baritoneYaw = baritoneContext?.target?.targetRotation?.value?.yaw - val strafeEvent = RotationEvent.StrafeInput(baritoneYaw ?: player.yaw.toDouble(), input) - val movementYaw = strafeEvent.post().strafeYaw - - // No changes are needed, when we don't modify the yaw used to move the player - // val config = currentContext?.config ?: return@runSafe - // if (config.rotationMode == RotationMode.SILENT && !input.handledByBaritone && baritoneContext == null) return@runSafe - - // Sign it to remove previous speed modifier - input.hasForwardMovement() - val signForward = sign(input.movementVector.y) - val signStrafe = sign(input.movementVector.x) - - // No changes are needed when no inputs are pressed - if (signForward <= 1.0E-5f && signStrafe <= 1.0E-5F) return@runSafe - - // Actual yaw used by the physics engine - var actualYaw = activeRotation.yaw - - if (activeRequest?.rotationMode == RotationMode.Silent) { - actualYaw = player.yaw.toDouble() - } - - val yawRad = (movementYaw - actualYaw).toRadian() - - val cosDelta = cos(yawRad) - val sinDelta = sin(yawRad) - - val newX = signStrafe * cosDelta - signForward * sinDelta - val newZ = signForward * cosDelta + signStrafe * sinDelta - - // Apply new movement - input.apply { - movementVector = Vec2f( - round(newX).toFloat(), - round(newZ).toFloat(), - ) - } - - baritoneYaw ?: return@runSafe - - // Makes baritone movement safe - // when yaw difference is too big to compensate it by modifying keyboard input - val minYawDist = movementYawList - .map { activeRotation.yaw + it } // all possible movement directions (including diagonals) - .minOf { Rotation.angleDifference(it, baritoneYaw) } - - if (minYawDist > 5.0) input.movementVector = Vec2f.ZERO - } - } - override fun preEvent() = UpdateManagerEvent.Rotation.post() } diff --git a/src/main/kotlin/com/lambda/module/modules/debug/RotationTest.kt b/src/main/kotlin/com/lambda/module/modules/debug/RotationTest.kt new file mode 100644 index 000000000..c2e7145e7 --- /dev/null +++ b/src/main/kotlin/com/lambda/module/modules/debug/RotationTest.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.module.modules.debug + +import com.lambda.config.groups.RotationSettings +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.rotating.visibilty.lookAtHit +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.util.Communication.info +import com.lambda.util.combat.DamageUtils.fallDamage +import com.lambda.util.combat.DamageUtils.isFallDeadly +import net.minecraft.util.hit.HitResult +import net.minecraft.util.math.Vec3d + +object RotationTest : Module( + name = "RotationTest", + tag = ModuleTag.DEBUG, +) { + var rotation = RotationSettings(this) + var hitPos: HitResult? = null + + init { + onEnable { + hitPos = mc.crosshairTarget + } + + listen { + hitPos?.let { lookAtHit(it)?.requestBy(rotation) } + } + } +} diff --git a/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt b/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt index 25ab7cc7b..165c11f53 100644 --- a/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt +++ b/src/main/kotlin/com/lambda/module/modules/movement/Speed.kt @@ -57,10 +57,6 @@ object Speed : Module( // Grim private val diagonal by setting("Diagonal", true) { mode == Mode.GRIM_STRAFE } - private val grimEntityBoost by setting("Entity Boost", 1.0, 0.0..2.0, 0.01) { mode == Mode.GRIM_STRAFE } - private val grimCollideMultiplier by setting("Entity Collide Multiplier", 0.5, 0.0..1.0, 0.01) { mode == Mode.GRIM_STRAFE && grimEntityBoost > 0.0 } - private val grimBoatBoost by setting("Boat Boost", 0.4, 0.0..1.0, 0.01) { mode == Mode.GRIM_STRAFE } - private val grimMaxSpeed by setting("Max Speed", 1.0, 0.2..1.0, 0.01) { mode == Mode.GRIM_STRAFE } // NCP private val strict by setting("Strict", true) { mode == Mode.NCP_STRAFE } @@ -68,15 +64,11 @@ object Speed : Module( private val ncpAutoJump by setting("Auto Jump", false) { mode == Mode.NCP_STRAFE } private val ncpTimerBoost by setting("Timer Boost", 1.08, 1.0..1.1, 0.01) { mode == Mode.NCP_STRAFE } - // Grim private val rotationConfig = RotationConfig.Instant(RotationMode.Sync) - private var prevTickJumping = false - - // NCP + // NCP state variables const val NCP_BASE_SPEED = 0.2873 private const val NCP_AIR_DECAY = 0.9937 - private var ncpPhase = NCPPhase.SLOWDOWN private var ncpSpeed = NCP_BASE_SPEED private var lastDistance = 0.0 @@ -99,9 +91,8 @@ object Speed : Module( return@listen } - when (mode) { - Mode.GRIM_STRAFE -> handleGrim() - Mode.NCP_STRAFE -> handleStrafe() + if (mode == Mode.NCP_STRAFE) { + handleStrafe() } } @@ -119,43 +110,19 @@ object Speed : Module( if (mode == Mode.NCP_STRAFE && shouldWork()) it.cancel() } - listen(Int.MIN_VALUE) { - if (mode != Mode.GRIM_STRAFE || !shouldWork()) return@listen - - // Delay jumping key state by 1 tick to let the rotation predict jump timing - it.input.apply { - val jump = jumping - jumping = prevTickJumping - prevTickJumping = jump - } - } - listen { - if (mode != Mode.GRIM_STRAFE) return@listen - if (!shouldWork()) return@listen + if (mode != Mode.GRIM_STRAFE || !shouldWork()) return@listen - var yaw = player.yaw val input = newMovementInput() - if (!input.isInputting) return@listen - run { - if (!diagonal) return@run - if (player.isOnGround && input.playerInput.jump) return@run - - val forward = input.roundedForward.toFloat() - var strafe = input.roundedStrafing.toFloat() - - if (strafe == 0f) strafe = -1f - if (forward == 0f) strafe *= -1 - - yaw -= 45 * strafe - } - - val moveYaw = calcMoveYaw(yaw, input.roundedForward, input.roundedStrafing) + val intendedMoveYaw = calcMoveYaw(player.yaw, input.roundedForward, input.roundedStrafing) + val targetYaw = if (diagonal && !(input.playerInput.jump && player.isOnGround)) { + intendedMoveYaw - 45.0f + } else intendedMoveYaw lookAt( - Rotation(moveYaw, 0.0) + Rotation(targetYaw, player.pitch.toDouble()) ).requestBy(rotationConfig) } @@ -164,25 +131,6 @@ object Speed : Module( } } - private fun SafeContext.handleGrim() { - if (!isInputting) return - if (player.moveDelta > grimMaxSpeed) return - - var boostAmount = 0.0 - - boostAmount += entitySearch(3.0) { - player.boundingBox.expand(1.0) in it.boundingBox && it !is ArmorStandEntity - }.sumOf { 0.08 * grimEntityBoost } - - if (grimBoatBoost > 0.0) { - boostAmount += entitySearch(4.0) { - player.boundingBox in it.boundingBox.expand(0.01) - }.sumOf { grimBoatBoost } - } - - addSpeed(boostAmount) - } - private fun SafeContext.handleStrafe() { val shouldJump = player.input.playerInput.jump || (ncpAutoJump && isInputting) @@ -230,13 +178,8 @@ object Speed : Module( if (player.abilities.flying || player.isElytraFlying || player.isTouchingWater || player.isInLava) return false return when (mode) { - Mode.GRIM_STRAFE -> { - !player.input.handledByBaritone && !TargetStrafe.isActive - } - - Mode.NCP_STRAFE -> { - !player.isSneaking - } + Mode.GRIM_STRAFE -> !player.input.handledByBaritone + Mode.NCP_STRAFE -> !player.isSneaking } } @@ -244,4 +187,4 @@ object Speed : Module( ncpPhase = NCPPhase.SLOWDOWN ncpSpeed = NCP_BASE_SPEED } -} +} \ No newline at end of file diff --git a/src/test/kotlin/RotationTest.kt b/src/test/kotlin/RotationTest.kt index a02c136c5..d103eb346 100644 --- a/src/test/kotlin/RotationTest.kt +++ b/src/test/kotlin/RotationTest.kt @@ -226,25 +226,6 @@ class RotationTest { assertTrue(result3.pitch < rotation2.pitch) } - @Test - fun `test slerp with angle wrapping`() { - // Test slerp across the -180/180 boundary - val rotation1 = Rotation(170.0, 0.0) - val rotation2 = Rotation(-170.0, 0.0) - - // With a very high speed, should go directly to rotation2 - val result = rotation1.slerp(rotation2, 1000.0) - assertEquals(rotation2.yaw, result.yaw, 0.001) - assertEquals(rotation2.pitch, result.pitch, 0.001) - - // With a limited speed, should move in the correct direction (clockwise) - val partialResult = rotation1.slerp(rotation2, 10.0) - - // The yaw should be greater than 170 (moving towards 180/-180) - // or less than -170 (already crossed the boundary) - assertTrue(partialResult.yaw > 170.0 || partialResult.yaw < -170.0) - } - @Test fun `test dist method`() { val rotation1 = Rotation(0.0, 0.0)