diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/EntityMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/EntityMixin.java index fc772cb46bd..f4d26e96d1f 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/EntityMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/EntityMixin.java @@ -23,6 +23,7 @@ import meteordevelopment.meteorclient.systems.modules.world.HighwayBuilder; import meteordevelopment.meteorclient.utils.Utils; import meteordevelopment.meteorclient.utils.entity.fakeplayer.FakePlayerEntity; +import meteordevelopment.meteorclient.utils.player.Rotations; import net.minecraft.client.Camera; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.MoverType; @@ -107,6 +108,12 @@ private void onMove(MoverType moverType, Vec3 delta, CallbackInfo ci) { } } + @ModifyExpressionValue(method = "moveRelative", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getYRot()F")) + private float moveRelativeYaw(float original) { + if ((Object) this == mc.player && Rotations.shouldCorrectPlayerInput()) return Rotations.serverYaw; + return original; + } + @ModifyExpressionValue(method = "getBlockSpeedFactor", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;getBlock()Lnet/minecraft/world/level/block/Block;")) private Block modifyBlockSpeedFactor(Block original) { if ((Object) this != mc.player) return original; diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/FireworkRocketEntityMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/FireworkRocketEntityMixin.java index 7edab1947cd..d393fca3810 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/FireworkRocketEntityMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/FireworkRocketEntityMixin.java @@ -5,17 +5,23 @@ package meteordevelopment.meteorclient.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import meteordevelopment.meteorclient.systems.modules.Modules; import meteordevelopment.meteorclient.systems.modules.movement.ElytraBoost; +import meteordevelopment.meteorclient.utils.player.Rotations; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.projectile.FireworkRocketEntity; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import static meteordevelopment.meteorclient.MeteorClient.mc; + @Mixin(FireworkRocketEntity.class) public abstract class FireworkRocketEntityMixin { @Shadow @@ -24,6 +30,15 @@ public abstract class FireworkRocketEntityMixin { @Shadow private int lifetime; + @Shadow + private LivingEntity attachedToEntity; + + @ModifyExpressionValue(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getLookAngle()Lnet/minecraft/world/phys/Vec3;")) + private Vec3 fixRocketBoostDirection(Vec3 original) { + if (attachedToEntity == mc.player && mc.player != null && Rotations.shouldCorrectPlayerInput()) return Rotations.getServerDirection(); + return original; + } + @Inject(method = "tick", at = @At("TAIL")) private void onTick(CallbackInfo ci) { FireworkRocketEntity firework = ((FireworkRocketEntity) (Object) this); diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/ItemMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/ItemMixin.java index 31f45980051..551cf745709 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/ItemMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/ItemMixin.java @@ -5,11 +5,17 @@ package meteordevelopment.meteorclient.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.events.render.TooltipDataEvent; +import meteordevelopment.meteorclient.utils.player.Rotations; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.tooltip.TooltipComponent; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -17,6 +23,8 @@ import java.util.Optional; +import static meteordevelopment.meteorclient.MeteorClient.mc; + @Mixin(Item.class) public abstract class ItemMixin { @Inject(method = "getTooltipImage", at = @At("HEAD"), cancellable = true) @@ -26,4 +34,10 @@ private void onTooltipData(ItemStack itemStack, CallbackInfoReturnable effect) { @ModifyExpressionValue(method = "jumpFromGround", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getYRot()F")) private float modifyGetYaw(float original) { if ((Object) this != mc.player) return original; + if (Rotations.shouldCorrectPlayerInput()) original = Rotations.serverYaw; if (!Modules.get().get(Sprint.class).rageSprint()) return original; float forward = Math.signum(mc.player.zza); @@ -141,4 +144,16 @@ private void onDropItem(ItemStack itemStack, boolean thrownFromHand, Prediction if (MeteorClient.EVENT_BUS.post(DropItemsEvent.get(itemStack)).isCancelled()) cir.setReturnValue(null); } } + + @ModifyExpressionValue(method = "updateFallFlyingMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getLookAngle()Lnet/minecraft/world/phys/Vec3;")) + private Vec3 fixFallFlyingLookAngle(Vec3 original) { + if ((Object) this == mc.player && Rotations.shouldCorrectPlayerInput()) return Rotations.getServerDirection(); + return original; + } + + @ModifyExpressionValue(method = "updateFallFlyingMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;getXRot()F")) + private float fixFallFlyingPitch(float original) { + if ((Object) this == mc.player && Rotations.shouldCorrectPlayerInput()) return Rotations.serverPitch; + return original; + } } diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/LocalPlayerMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/LocalPlayerMixin.java index cebbd842957..b52722338ba 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/LocalPlayerMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/LocalPlayerMixin.java @@ -8,6 +8,8 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import com.mojang.authlib.GameProfile; import meteordevelopment.meteorclient.MeteorClient; @@ -20,6 +22,7 @@ import meteordevelopment.meteorclient.systems.modules.player.Portals; import meteordevelopment.meteorclient.systems.modules.render.NoRender; import meteordevelopment.meteorclient.utils.entity.fakeplayer.FakePlayerEntity; +import meteordevelopment.meteorclient.utils.player.Rotations; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.player.AbstractClientPlayer; @@ -103,6 +106,12 @@ private void preAiStep(CallbackInfo ci) { MeteorClient.EVENT_BUS.post(PlayerTickMovementEvent.get()); } + @WrapOperation(method = "aiStep", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/ClientInput;tick()V")) + private void wrapTick(ClientInput input, Operation original) { + original.call(input); + if (Rotations.shouldCorrectPlayerInput()) Rotations.setCorrectedPlayerInput(input); + } + @ModifyReturnValue(method = "getJumpRidingScale", at = @At("RETURN")) private float modifyJumpRidingScale(float original) { if (Modules.get().get(EntityControl.class).maxJump()) return 1f; diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/MultiPlayerGameModeMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/MultiPlayerGameModeMixin.java index c2014ac42d3..8270171a677 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/MultiPlayerGameModeMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/MultiPlayerGameModeMixin.java @@ -8,6 +8,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.events.entity.DropItemsEvent; import meteordevelopment.meteorclient.events.entity.player.*; @@ -15,6 +16,7 @@ import meteordevelopment.meteorclient.systems.modules.Modules; import meteordevelopment.meteorclient.systems.modules.player.BreakDelay; import meteordevelopment.meteorclient.systems.modules.player.SpeedMine; +import meteordevelopment.meteorclient.utils.player.Rotations; import meteordevelopment.meteorclient.utils.world.BlockUtils; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.MultiPlayerGameMode; @@ -23,6 +25,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; +import net.minecraft.network.protocol.game.ServerboundUseItemPacket; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; @@ -153,6 +156,12 @@ private void onUseItem(Player player, InteractionHand hand, CallbackInfoReturnab if (event.toReturn != null) cir.setReturnValue(event.toReturn); } + @WrapOperation(method = "lambda$useItem$0", at = @At(value = "NEW", target = "(Lnet/minecraft/world/InteractionHand;IFF)Lnet/minecraft/network/protocol/game/ServerboundUseItemPacket;")) + private ServerboundUseItemPacket fixItemRotation(InteractionHand hand, int sequence, float yaw, float pitch, Operation original, @Local(argsOnly = true) Player player) { + if (player == mc.player && Rotations.rotating) return original.call(hand, sequence, Rotations.serverYaw, Rotations.serverPitch); + return original.call(hand, sequence, yaw, pitch); + } + @Inject(method = "stopDestroyBlock", at = @At("HEAD"), cancellable = true) private void onStopDestroyBlock(CallbackInfo ci) { if (BlockUtils.breaking) ci.cancel(); diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/PlayerMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/PlayerMixin.java index 8ac78b5cb0c..268f907288c 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/PlayerMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/PlayerMixin.java @@ -5,6 +5,7 @@ package meteordevelopment.meteorclient.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.ModifyReturnValue; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import meteordevelopment.meteorclient.MeteorClient; @@ -15,6 +16,7 @@ import meteordevelopment.meteorclient.systems.modules.movement.Sprint; import meteordevelopment.meteorclient.systems.modules.player.Reach; import meteordevelopment.meteorclient.systems.modules.player.SpeedMine; +import meteordevelopment.meteorclient.utils.player.Rotations; import meteordevelopment.meteorclient.utils.world.BlockUtils; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.EntityType; @@ -115,6 +117,12 @@ private void onGetFlyingSpeed(CallbackInfoReturnable cir) { return Modules.get().get(Sprint.class).stopSprinting(); } + @ModifyExpressionValue(method = {"causeExtraKnockback", "doSweepAttack"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;getYRot()F")) + private float fixAttackYaw(float original) { + if ((Object) this == mc.player && Rotations.shouldCorrectPlayerInput()) return Rotations.serverYaw; + return original; + } + @ModifyReturnValue(method = "blockInteractionRange", at = @At("RETURN")) private double modifyBlockInteractionRange(double original) { return Math.max(0, original + Modules.get().get(Reach.class).blockReach()); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/config/Config.java b/src/main/java/meteordevelopment/meteorclient/systems/config/Config.java index ba0d7db1543..0bef7fe70f7 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/config/Config.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/config/Config.java @@ -183,7 +183,14 @@ public class Config extends System { public final Setting rotationHoldTicks = sgMisc.add(new IntSetting.Builder() .name("rotation-hold") .description("Hold long to hold server side rotation when not sending any packets.") - .defaultValue(4) + .defaultValue(2) + .build() + ); + + public final Setting rotationMovementCorrection = sgMisc.add(new BoolSetting.Builder() + .name("rotation-movement-correction") + .description("Adjust player movement depending on server side rotation (Grim movefix).") + .defaultValue(true) .build() ); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/KillAura.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/KillAura.java index aeea4ad28f7..c6aca2fddb8 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/KillAura.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/KillAura.java @@ -68,7 +68,7 @@ public class KillAura extends Module { .build() ); - private final Setting rotation = sgGeneral.add(new EnumSetting.Builder() + private final Setting rotate = sgGeneral.add(new EnumSetting.Builder() .name("rotate") .description("Determines when you should rotate towards the target.") .defaultValue(RotationMode.Always) @@ -350,14 +350,17 @@ private void onTick(TickEvent.Pre event) { } attacking = true; - if (rotation.get() == RotationMode.Always) - Rotations.rotate(Rotations.getYaw(primary), Rotations.getPitch(primary, Target.Body)); + if (pauseOnCombat.get() && PathManagers.get().isPathing() && !wasPathing) { PathManagers.get().pause(); wasPathing = true; } - if (delayCheck()) targets.forEach(this::attack); + if (rotate.get() == RotationMode.Always) { + Rotations.rotate(primary, 30, () -> attackTargets()); + } else { + attackTargets(); + } } @EventHandler @@ -456,11 +459,20 @@ private boolean delayCheck() { } else return true; } else return mc.player.getAttackStrengthScale(delay) >= 1; } + + private void attackTargets() { + if (!delayCheck()) return; - private void attack(Entity target) { - if (rotation.get() == RotationMode.OnHit) - Rotations.rotate(Rotations.getYaw(target), Rotations.getPitch(target, Target.Body)); + for (Entity target : targets) { + if (rotate.get() == RotationMode.OnHit) { + Rotations.rotate(target, 30, () -> attack(target)); + } else { + attack(target); + } + } + } + private void attack(Entity target) { mc.gameMode.attack(mc.player, target); SwingMode.Both.attackSwing(InteractionHand.MAIN_HAND); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/movement/NoFall.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/movement/NoFall.java index dad0eb93fae..981642b8015 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/movement/NoFall.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/movement/NoFall.java @@ -427,7 +427,7 @@ private void queueBucketUse(FindItemResult item, BlockHitResult hit, PlacedItem boolean useNow = pickup || isWithinPlaceReach(hit.getLocation()); Vec3 aim = pickup ? Vec3.atCenterOf(hit.getBlockPos()) : hit.getLocation(); if (rotate.get()) Rotations.rotate(Rotations.getYaw(aim), Rotations.getPitch(aim), - Integer.MAX_VALUE, true, useNow ? null : use); + Integer.MAX_VALUE, useNow ? null : use); else if (!useNow) pendingBucketUse = use; if (useNow) use.run(); } diff --git a/src/main/java/meteordevelopment/meteorclient/utils/player/Rotations.java b/src/main/java/meteordevelopment/meteorclient/utils/player/Rotations.java index fd562c33c0e..2e879be423c 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/player/Rotations.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/player/Rotations.java @@ -11,31 +11,32 @@ import meteordevelopment.meteorclient.systems.config.Config; import meteordevelopment.meteorclient.utils.PreInit; import meteordevelopment.meteorclient.utils.entity.Target; -import meteordevelopment.meteorclient.utils.misc.Pool; import meteordevelopment.orbit.EventHandler; +import meteordevelopment.orbit.EventPriority; +import net.minecraft.client.player.ClientInput; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Input; +import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import java.util.ArrayList; +import java.util.function.Supplier; import java.util.List; import static meteordevelopment.meteorclient.MeteorClient.mc; public class Rotations { - private static final Pool rotationPool = new Pool<>(Rotation::new); + private static final List queue = new ArrayList<>(); private static final List rotations = new ArrayList<>(); - public static float serverYaw; - public static float serverPitch; - public static int rotationTimer; private static float preYaw, prePitch; - private static int i = 0; + public static float serverYaw, serverPitch; private static Rotation lastRotation; private static int lastRotationTimer; - private static boolean sentLastRotation; + public static boolean rotating = false; private Rotations() { @@ -46,140 +47,181 @@ public static void init() { MeteorClient.EVENT_BUS.subscribe(Rotations.class); } - public static void rotate(double yaw, double pitch, int priority, boolean clientSide, Runnable callback) { - Rotation rotation = rotationPool.get(); - rotation.set(yaw, pitch, priority, clientSide, callback); - + // Rotation Setup + + private static void rotate(Rotation rotation) { int i = 0; - for (; i < rotations.size(); i++) { - if (priority > rotations.get(i).priority) break; + for (; i < queue.size(); i++) { + if (rotation.priority > queue.get(i).priority) break; } - rotations.add(i, rotation); + queue.add(i, rotation); + } + + @Deprecated // clientSide boolean is no longer used. + public static void rotate(double yaw, double pitch, int priority, boolean clientSide, Runnable callback) { + rotate(new Rotation(yaw, pitch, priority, callback)); } public static void rotate(double yaw, double pitch, int priority, Runnable callback) { - rotate(yaw, pitch, priority, false, callback); + rotate(new Rotation(yaw, pitch, priority, callback)); } public static void rotate(double yaw, double pitch, Runnable callback) { - rotate(yaw, pitch, 0, callback); + rotate(new Rotation(yaw, pitch, 0, callback)); } public static void rotate(double yaw, double pitch, int priority) { - rotate(yaw, pitch, priority, null); + rotate(new Rotation(yaw, pitch, priority, null)); } public static void rotate(double yaw, double pitch) { - rotate(yaw, pitch, 0, null); + rotate(new Rotation(yaw, pitch, 0, null)); } - private static void resetLastRotation() { - if (lastRotation != null) { - rotationPool.free(lastRotation); - - lastRotation = null; - lastRotationTimer = 0; - } + public static void rotate(Entity entity, int priority, Runnable callback) { + rotate(new Rotation(entity, priority, callback)); } - @EventHandler - private static void onSendMovementPacketsPre(SendMovementPacketsEvent.Pre event) { - if (mc.getCameraEntity() != mc.player) return; - sentLastRotation = false; - - if (!rotations.isEmpty()) { - rotating = true; - resetLastRotation(); + public static void rotate(Entity entity, Runnable callback) { + rotate(new Rotation(entity, 0, callback)); + } - Rotation rotation = rotations.get(i); - setupMovementPacketRotation(rotation); + public static void rotate(Vec3 position, int priority, Runnable callback) { + rotate(new Rotation(position, priority, callback)); + } - if (rotations.size() > 1) rotationPool.free(rotation); + public static void rotate(Vec3 position, Runnable callback) { + rotate(new Rotation(position, 0, callback)); + } - i++; - } else if (lastRotation != null) { - if (lastRotationTimer >= Config.get().rotationHoldTicks.get()) { - resetLastRotation(); - rotating = false; - } else { - setupMovementPacketRotation(lastRotation); - sentLastRotation = true; + public static void rotate(int priority, Supplier calculate, Runnable callback) { + rotate(new Rotation(priority, calculate, callback)); + } - lastRotationTimer++; - } - } + public static void rotate(Supplier calculate, Runnable callback) { + rotate(new Rotation(0, calculate, callback)); } - private static void setupMovementPacketRotation(Rotation rotation) { - setClientRotation(rotation); - setCamRotation(rotation.yaw, rotation.pitch); + public static void rotate(Supplier calculate) { + rotate(new Rotation(0, calculate, null)); } - private static void setClientRotation(Rotation rotation) { + // Rotation Execution + + private static void setPreRotation() { preYaw = mc.player.getYRot(); prePitch = mc.player.getXRot(); + } - mc.player.setYRot((float) rotation.yaw); - mc.player.setXRot((float) rotation.pitch); + private static void setClientRotation(float yaw, float pitch) { + mc.player.setYRot(yaw); + mc.player.setXRot(pitch); } - @EventHandler - private static void onSendMovementPacketsPost(SendMovementPacketsEvent.Post event) { - if (!rotations.isEmpty()) { - if (mc.getCameraEntity() == mc.player) { - rotations.get(i - 1).runCallback(); + private static void setServerRotation(float yaw, float pitch) { + serverYaw = yaw; + serverPitch = pitch; + } - if (rotations.size() == 1) lastRotation = rotations.get(i - 1); + @EventHandler(priority = EventPriority.LOWEST - 700) + private static void onTickPre(TickEvent.Pre event) { + rotating = false; - resetPreRotation(); + if (lastRotation != null) { + if (lastRotationTimer++ >= Config.get().rotationHoldTicks.get()) { + lastRotation = null; + lastRotationTimer = 0; + } else if (queue.isEmpty()) { + queue.add(lastRotation); } + } - for (; i < rotations.size(); i++) { - Rotation rotation = rotations.get(i); - - setCamRotation(rotation.yaw, rotation.pitch); - if (rotation.clientSide) setClientRotation(rotation); - rotation.sendPacket(); - if (rotation.clientSide) resetPreRotation(); + if (!queue.isEmpty()) { + rotating = true; - if (i == rotations.size() - 1) lastRotation = rotation; - else rotationPool.free(rotation); - } + // Set the server rotation early so movefix inputs compensate. + Rotation last = queue.get(queue.size() - 1); + setServerRotation(last.yaw, last.pitch); - rotations.clear(); - i = 0; - } else if (sentLastRotation) { - resetPreRotation(); + rotations.addAll(queue); + queue.clear(); } } - private static void resetPreRotation() { - mc.player.setYRot(preYaw); - mc.player.setXRot(prePitch); + @EventHandler + private static void onSendMovementPacketsPre(SendMovementPacketsEvent.Pre event) { + if (rotations.isEmpty()) return; + + // Recalculate yaw and pitch due to entities moving around. + if (!Config.get().rotationMovementCorrection.get()) { + rotations.forEach(Rotation::recalculate); + } + + // The first rotation is sent within our normal movement packets + Rotation first = rotations.get(0); + setPreRotation(); + setServerRotation(first.yaw, first.pitch); + setClientRotation(first.yaw, first.pitch); } @EventHandler - private static void onTick(TickEvent.Pre event) { - rotationTimer++; + private static void onSendMovementPacketsPost(SendMovementPacketsEvent.Post event) { + if (rotations.isEmpty()) return; + + // First rotation packet was already sent to server naturally. + Rotation first = rotations.get(0); + setClientRotation(first.yaw, first.pitch); + first.runCallback(); + + // All subsequent rotations require a packet sent + for (int i = 1; i < rotations.size(); i++) { + Rotation rotation = rotations.get(i); + setServerRotation(rotation.yaw, rotation.pitch); + setClientRotation(rotation.yaw, rotation.pitch); + rotation.sendPacket(); + rotation.runCallback(); + } + + setClientRotation(preYaw, prePitch); + + Rotation lastIndexRotation = rotations.get(rotations.size() - 1); + if (lastIndexRotation != lastRotation) { + lastRotation = lastIndexRotation; + lastRotation.callback = null; + lastRotationTimer = 0; + rotating = Config.get().rotationHoldTicks.get() > 0; + } + + rotations.clear(); } - public static double getYaw(Entity entity) { - return mc.player.getYRot() + Mth.wrapDegrees((float) Math.toDegrees(Math.atan2(entity.getZ() - mc.player.getZ(), entity.getX() - mc.player.getX())) - 90f - mc.player.getYRot()); + // Utilities + + public static double getYaw(double x, double z) { + return Mth.wrapDegrees((float) Math.toDegrees(Math.atan2(z - mc.player.getZ(), x - mc.player.getX())) - 90f); + } + + public static double getPitch(double x, double y, double z) { + double diffX = x - mc.player.getX(); + double diffY = y - mc.player.getEyeY(); + double diffZ = z - mc.player.getZ(); + + double diffXZ = Math.hypot(diffX, diffZ); + + return Mth.wrapDegrees((float) -Math.toDegrees(Math.atan2(diffY, diffXZ))); } public static double getYaw(Vec3 pos) { - return mc.player.getYRot() + Mth.wrapDegrees((float) Math.toDegrees(Math.atan2(pos.z() - mc.player.getZ(), pos.x() - mc.player.getX())) - 90f - mc.player.getYRot()); + return getYaw(pos.x(), pos.z()); } public static double getPitch(Vec3 pos) { - double diffX = pos.x() - mc.player.getX(); - double diffY = pos.y() - (mc.player.getY() + mc.player.getEyeHeight(mc.player.getPose())); - double diffZ = pos.z() - mc.player.getZ(); - - double diffXZ = Math.sqrt(diffX * diffX + diffZ * diffZ); + return getPitch(pos.x(), pos.y(), pos.z()); + } - return mc.player.getXRot() + Mth.wrapDegrees((float) -Math.toDegrees(Math.atan2(diffY, diffXZ)) - mc.player.getXRot()); + public static double getYaw(Entity entity) { + return getYaw(entity.getX(), entity.getZ()); } public static double getPitch(Entity entity, Target target) { @@ -189,13 +231,7 @@ public static double getPitch(Entity entity, Target target) { case Feet -> entity.getY(); }; - double diffX = entity.getX() - mc.player.getX(); - double diffY = y - (mc.player.getY() + mc.player.getEyeHeight(mc.player.getPose())); - double diffZ = entity.getZ() - mc.player.getZ(); - - double diffXZ = Math.sqrt(diffX * diffX + diffZ * diffZ); - - return mc.player.getXRot() + Mth.wrapDegrees((float) -Math.toDegrees(Math.atan2(diffY, diffXZ)) - mc.player.getXRot()); + return getPitch(entity.getX(), y, entity.getZ()); } public static double getPitch(Entity entity) { @@ -203,42 +239,119 @@ public static double getPitch(Entity entity) { } public static double getYaw(BlockPos pos) { - return mc.player.getYRot() + Mth.wrapDegrees((float) Math.toDegrees(Math.atan2(pos.getZ() + 0.5 - mc.player.getZ(), pos.getX() + 0.5 - mc.player.getX())) - 90f - mc.player.getYRot()); + return getYaw(pos.getX() + 0.5, pos.getZ() + 0.5); } public static double getPitch(BlockPos pos) { - double diffX = pos.getX() + 0.5 - mc.player.getX(); - double diffY = pos.getY() + 0.5 - (mc.player.getY() + mc.player.getEyeHeight(mc.player.getPose())); - double diffZ = pos.getZ() + 0.5 - mc.player.getZ(); + return getPitch(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); + } - double diffXZ = Math.sqrt(diffX * diffX + diffZ * diffZ); + /** + * setCorrectedPlayerInput and getInputFromRelativeYaw are derived from Lambda Client. + * @see https://github.com/lambda-client/lambda/blob/1.21.11/src/main/kotlin/com/lambda/util/player/RotationUtils.kt + * @author dinosaurwizard + * + * Implementation of Grim movefix logic. + **/ - return mc.player.getXRot() + Mth.wrapDegrees((float) -Math.toDegrees(Math.atan2(diffY, diffXZ)) - mc.player.getXRot()); + public static boolean shouldCorrectPlayerInput() { + return rotating && Config.get().rotationMovementCorrection.get(); } - public static void setCamRotation(double yaw, double pitch) { - serverYaw = (float) yaw; - serverPitch = (float) pitch; - rotationTimer = 0; + public static Vec3 getServerDirection() { + return Vec3.directionFromRotation(serverPitch, serverYaw); + } + + public static void setCorrectedPlayerInput(ClientInput input) { + input.keyPresses = getInputFromRelativeYaw(input.keyPresses); + + // The movement vector must be adjusted according to the new input. + int x = (input.keyPresses.left() ? 1 : 0) - (input.keyPresses.right() ? 1 : 0); + int y = (input.keyPresses.forward() ? 1 : 0) - (input.keyPresses.backward() ? 1 : 0); + input.moveVector = new Vec2(x, y).normalized(); + } + + private static Input getInputFromRelativeYaw(Input input) { + int strafe = (input.left() ? 1 : 0) - (input.right() ? 1 : 0); + int forward = (input.forward() ? 1 : 0) - (input.backward() ? 1 : 0); + if (strafe == 0 && forward == 0) return input; + + double deltaYawRad = Math.toRadians(mc.player.getYRot() - serverYaw); + double cos = Math.cos(deltaYawRad); + double sin = Math.sin(deltaYawRad); + double newStrafe = strafe * cos - forward * sin; + double newForward = strafe * sin + forward * cos; + double angle = Math.atan2(newStrafe, newForward); + double sector = Math.PI / 4.0; + double boundary = Math.PI / 8.0; + + boolean pressForward = false; + boolean pressBackward = false; + boolean pressLeft = false; + boolean pressRight = false; + + if (angle > -boundary && angle <= boundary) { + pressForward = true; + } else if (angle > boundary && angle <= boundary + sector) { + pressForward = true; + pressLeft = true; + } else if (angle > boundary + sector && angle <= boundary + 2 * sector) { + pressLeft = true; + } else if (angle > boundary + 2 * sector && angle <= boundary + 3 * sector) { + pressBackward = true; + pressLeft = true; + } else if (angle > boundary + 3 * sector || angle <= -(boundary + 3 * sector)) { + pressBackward = true; + } else if (angle > -(boundary + 3 * sector) && angle <= -(boundary + 2 * sector)) { + pressBackward = true; + pressRight = true; + } else if (angle > -(boundary + 2 * sector) && angle <= -(boundary + sector)) { + pressRight = true; + } else if (angle > -(boundary + sector) && angle <= -boundary) { + pressForward = true; + pressRight = true; + } + + return new Input(pressForward, pressBackward, pressLeft, pressRight, input.jump(), input.shift(), input.sprint()); } private static class Rotation { - public double yaw, pitch; + public float yaw, pitch; public int priority; - public boolean clientSide; public Runnable callback; + public Supplier calculate; - public void set(double yaw, double pitch, int priority, boolean clientSide, Runnable callback) { - this.yaw = yaw; - this.pitch = pitch; + public Rotation(double yaw, double pitch, int priority, Runnable callback) { + this.yaw = (float) yaw; + this.pitch = (float) pitch; this.priority = priority; - this.clientSide = clientSide; this.callback = callback; } + public Rotation(int priority, Supplier calculate, Runnable callback) { + this(0, 0, priority, callback); + this.calculate = calculate; + recalculate(); + } + + public Rotation(Vec3 position, int priority, Runnable callback) { + this(priority, () -> new Vec2((float) getYaw(position), (float) getPitch(position)), callback); + } + + public Rotation(Entity entity, int priority, Runnable callback) { + this(priority, () -> new Vec2((float) getYaw(entity), (float) getPitch(entity)), callback); + } + + public void recalculate() { + if (calculate != null) { + Vec2 result = calculate.get(); + this.yaw = result.x; + this.pitch = result.y; + } + } + public void sendPacket() { - mc.getConnection().send(new ServerboundMovePlayerPacket.Rot((float) yaw, (float) pitch, mc.player.onGround(), mc.player.horizontalCollision)); - runCallback(); + mc.getConnection().send(new ServerboundMovePlayerPacket.Rot(yaw, pitch, mc.player.onGround(), mc.player.horizontalCollision)); } public void runCallback() { diff --git a/src/main/resources/meteor-client.classtweaker b/src/main/resources/meteor-client.classtweaker index 66a57a1d0bc..890905d8af7 100644 --- a/src/main/resources/meteor-client.classtweaker +++ b/src/main/resources/meteor-client.classtweaker @@ -43,3 +43,6 @@ accessible method com/mojang/renderpearl/api/pipeline/RenderPipeline$Builder