diff --git a/src/main/java/meteordevelopment/meteorclient/mixin/MinecraftClientMixin.java b/src/main/java/meteordevelopment/meteorclient/mixin/MinecraftClientMixin.java index 47c3ab99dd..3fa3f63072 100644 --- a/src/main/java/meteordevelopment/meteorclient/mixin/MinecraftClientMixin.java +++ b/src/main/java/meteordevelopment/meteorclient/mixin/MinecraftClientMixin.java @@ -23,6 +23,7 @@ import meteordevelopment.meteorclient.mixininterface.IMinecraftClient; import meteordevelopment.meteorclient.systems.config.Config; import meteordevelopment.meteorclient.systems.modules.Modules; +import meteordevelopment.meteorclient.systems.modules.combat.NoMissDelay; import meteordevelopment.meteorclient.systems.modules.movement.GUIMove; import meteordevelopment.meteorclient.systems.modules.player.FastUse; import meteordevelopment.meteorclient.systems.modules.player.Multitask; @@ -43,8 +44,11 @@ import net.minecraft.client.util.Window; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.util.Hand; import net.minecraft.util.hit.HitResult; import net.minecraft.util.profiler.Profilers; import org.jetbrains.annotations.Nullable; @@ -57,6 +61,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; @Mixin(value = MinecraftClient.class, priority = 1001) public abstract class MinecraftClientMixin implements IMinecraftClient { @@ -84,6 +89,9 @@ public abstract class MinecraftClientMixin implements IMinecraftClient { @Nullable public ClientPlayerEntity player; + @Shadow + private HitResult crosshairTarget; + @Shadow @Final @Mutable @@ -116,9 +124,53 @@ private void onTick(CallbackInfo info) { Profilers.get().pop(); } - @Inject(method = "doAttack", at = @At("HEAD")) + // NoMissDelay + + @Inject(method = "doAttack", at = @At("HEAD"), cancellable = true) private void onAttack(CallbackInfoReturnable cir) { CPSUtils.onAttack(); + + NoMissDelay noMissDelay = Modules.get().get(NoMissDelay.class); + + if (!noMissDelay.isActive()) return; + if (noMissDelay.getOnlywithsword() && !hasAttackCooldown(player.getMainHandStack())) return; + + if (noMissDelay.getCancelnoncrits() && player.getAttackCooldownProgress(0.5F) < 1.0F) { + cir.setReturnValue(false); + if (noMissDelay.shouldSwing()) player.swingHand(Hand.MAIN_HAND, false); + return; + } + + if (crosshairTarget == null) { + cir.setReturnValue(false); + if (noMissDelay.shouldSwing()) player.swingHand(Hand.MAIN_HAND, false); + return; + } + + HitResult.Type type = crosshairTarget.getType(); + + if (type == HitResult.Type.MISS || + (noMissDelay.getCancelblockattacks() && type == HitResult.Type.BLOCK)) { + cir.setReturnValue(false); + if (noMissDelay.shouldSwing()) player.swingHand(Hand.MAIN_HAND, false); + } + } + + private static boolean hasAttackCooldown(ItemStack stack) { + if (stack.isEmpty()) return false; + + AtomicBoolean hasAttack = new AtomicBoolean(false); //Thread safe just in case + + stack.applyAttributeModifiers(EquipmentSlot.MAINHAND, + (attribute, modifier) -> { + if (attribute.equals(EntityAttributes.ATTACK_DAMAGE) || + attribute.equals(EntityAttributes.ATTACK_SPEED)) { + hasAttack.set(true); + } + } + ); + + return hasAttack.get(); } @Inject(method = "doItemUse", at = @At("HEAD")) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java index 43a1bb1136..9bc47ed370 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java @@ -385,6 +385,7 @@ private void initCombat() { add(new AnchorAura()); add(new AntiAnvil()); add(new AntiBed()); + add(new NoMissDelay()); add(new ArrowDodge()); add(new AutoAnvil()); add(new AutoArmor()); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/NoMissDelay.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/NoMissDelay.java new file mode 100644 index 0000000000..8d35a48c1d --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/NoMissDelay.java @@ -0,0 +1,64 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.systems.modules.combat; + +import meteordevelopment.meteorclient.settings.BoolSetting; +import meteordevelopment.meteorclient.settings.Setting; +import meteordevelopment.meteorclient.settings.SettingGroup; +import meteordevelopment.meteorclient.systems.modules.Categories; +import meteordevelopment.meteorclient.systems.modules.Module; + +public class NoMissDelay extends Module { + private final SettingGroup sgGeneral = settings.getDefaultGroup(); + + public NoMissDelay() { + super(Categories.Combat, "no-miss-delay", "Cancels attacks that would miss"); + } + + private final Setting cancelblockattacks = sgGeneral.add(new BoolSetting.Builder() + .name("block-attacks") + .description("Cancels attacks that hit a block.") + .defaultValue(false) + .build() + ); + + private final Setting onlywithsword = sgGeneral.add(new BoolSetting.Builder() + .name("only-weapons") + .description("Cancels attacks only when holding a weapon") + .defaultValue(false) + .build() + ); + + private final Setting swing = sgGeneral.add(new BoolSetting.Builder() + .name("swing") + .description("Wheter to swing the hand when cancelling attacks") + .defaultValue(false) + .build() + ); + + private final Setting cancelnoncrits = sgGeneral.add(new BoolSetting.Builder() + .name("cancel-non-crits") + .description("Whether to cancel attacks that are not critical hits") + .defaultValue(false) + .build() + ); + + public boolean shouldSwing() { + return swing.get(); + } + + public boolean getCancelblockattacks() { + return cancelblockattacks.get(); + } + + public boolean getOnlywithsword() { + return onlywithsword.get(); + } + + public boolean getCancelnoncrits() { + return cancelnoncrits.get(); + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/NoInteract.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/NoInteract.java index 4bea37d511..e737f80fcc 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/NoInteract.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/NoInteract.java @@ -30,7 +30,6 @@ public class NoInteract extends Module { private final SettingGroup sgBlocks = settings.createGroup("Blocks"); private final SettingGroup sgEntities = settings.createGroup("Entities"); - // Blocks private final Setting> blockMine = sgBlocks.add(new BlockListSetting.Builder()