From 238401c7bba6968146dbbabec1225d48f165e2e5 Mon Sep 17 00:00:00 2001 From: Based Ricky <80427814+RickyTheRacc@users.noreply.github.com> Date: Thu, 15 Dec 2022 18:34:21 -0600 Subject: [PATCH] Improve Auto City (#3050) --- .../systems/modules/combat/AutoCity.java | 386 +++++++++++------- .../systems/modules/world/PacketMine.java | 47 +-- .../utils/entity/EntityUtils.java | 49 ++- .../meteorclient/utils/world/BlockUtils.java | 51 +++ .../resources/meteor-client.accesswidener | 1 + 5 files changed, 332 insertions(+), 202 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/AutoCity.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/AutoCity.java index 766bcc2d80..d8d61fe404 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/AutoCity.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/combat/AutoCity.java @@ -1,146 +1,240 @@ -/* - * 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.events.world.TickEvent; -import meteordevelopment.meteorclient.settings.BoolSetting; -import meteordevelopment.meteorclient.settings.DoubleSetting; -import meteordevelopment.meteorclient.settings.Setting; -import meteordevelopment.meteorclient.settings.SettingGroup; -import meteordevelopment.meteorclient.systems.modules.Categories; -import meteordevelopment.meteorclient.systems.modules.Module; -import meteordevelopment.meteorclient.utils.entity.EntityUtils; -import meteordevelopment.meteorclient.utils.entity.SortPriority; -import meteordevelopment.meteorclient.utils.entity.TargetUtils; -import meteordevelopment.meteorclient.utils.player.FindItemResult; -import meteordevelopment.meteorclient.utils.player.InvUtils; -import meteordevelopment.meteorclient.utils.player.PlayerUtils; -import meteordevelopment.meteorclient.utils.player.Rotations; -import meteordevelopment.meteorclient.utils.world.BlockUtils; -import meteordevelopment.orbit.EventHandler; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.Items; -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket; -import net.minecraft.util.Hand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; - -public class AutoCity extends Module { - private final SettingGroup sgGeneral = settings.getDefaultGroup(); - - private final Setting targetRange = sgGeneral.add(new DoubleSetting.Builder() - .name("target-range") - .description("The radius in which players get targeted.") - .defaultValue(4) - .min(0) - .sliderMax(5) - .build() - ); - - private final Setting autoSwitch = sgGeneral.add(new BoolSetting.Builder() - .name("auto-switch") - .description("Auto switches to a pickaxe when AutoCity is enabled.") - .defaultValue(true) - .build() - ); - - private final Setting support = sgGeneral.add(new BoolSetting.Builder() - .name("support") - .description("If there is no block below a city block it will place one before mining.") - .defaultValue(true) - .build() - ); - - private final Setting rotate = sgGeneral.add(new BoolSetting.Builder() - .name("rotate") - .description("Automatically rotates you towards the city block.") - .defaultValue(true) - .build() - ); - - private final Setting selfToggle = sgGeneral.add(new BoolSetting.Builder() - .name("self-toggle") - .description("Automatically toggles off after activation.") - .defaultValue(true) - .build() - ); - - private PlayerEntity target; - private BlockPos blockPosTarget; - private boolean sentMessage; - - public AutoCity() { - super(Categories.Combat, "auto-city", "Automatically cities a target by mining the nearest obsidian next to them."); - } - - @EventHandler - private void onTick(TickEvent.Pre event) { - if (TargetUtils.isBadTarget(target, targetRange.get())) { - PlayerEntity search = TargetUtils.getPlayerTarget(targetRange.get(), SortPriority.LowestDistance); - if (search != target) sentMessage = false; - target = search; - - if (TargetUtils.isBadTarget(target, targetRange.get())) { - target = null; - blockPosTarget = null; - if (selfToggle.get()) toggle(); - return; - } - } - - blockPosTarget = EntityUtils.getCityBlock(target); - - if (blockPosTarget == null) { - if (selfToggle.get()) { - error("No target block found... disabling."); - toggle(); - } - target = null; - return; - } - - if (!PlayerUtils.isWithinReach(blockPosTarget) && selfToggle.get()) { - error("Target block out of reach... disabling."); - toggle(); - return; - } - - if (!sentMessage) { - info("Attempting to city %s.", target.getEntityName()); - sentMessage = true; - } - - FindItemResult pickaxe = InvUtils.find(itemStack -> itemStack.getItem() == Items.DIAMOND_PICKAXE || itemStack.getItem() == Items.NETHERITE_PICKAXE); - - if (!pickaxe.isHotbar()) { - if (selfToggle.get()) { - error("No pickaxe found... disabling."); - toggle(); - } - return; - } - - if (support.get()) BlockUtils.place(blockPosTarget.down(1), InvUtils.findInHotbar(Items.OBSIDIAN), rotate.get(), 0, true); - - if (autoSwitch.get()) InvUtils.swap(pickaxe.slot(), false); - - if (rotate.get()) Rotations.rotate(Rotations.getYaw(blockPosTarget), Rotations.getPitch(blockPosTarget), () -> mine(blockPosTarget)); - else mine(blockPosTarget); - - if (selfToggle.get()) toggle(); - } - - private void mine(BlockPos blockPos) { - mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, blockPos, Direction.UP)); - mc.player.swingHand(Hand.MAIN_HAND); - mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, blockPos, Direction.UP)); - } - - @Override - public String getInfoString() { - return EntityUtils.getName(target); - } -} +/* + * 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.events.render.Render3DEvent; +import meteordevelopment.meteorclient.events.world.TickEvent; +import meteordevelopment.meteorclient.renderer.ShapeMode; +import meteordevelopment.meteorclient.settings.*; +import meteordevelopment.meteorclient.systems.modules.Categories; +import meteordevelopment.meteorclient.systems.modules.Module; +import meteordevelopment.meteorclient.systems.modules.world.PacketMine; +import meteordevelopment.meteorclient.utils.entity.EntityUtils; +import meteordevelopment.meteorclient.utils.entity.SortPriority; +import meteordevelopment.meteorclient.utils.entity.TargetUtils; +import meteordevelopment.meteorclient.utils.player.FindItemResult; +import meteordevelopment.meteorclient.utils.player.InvUtils; +import meteordevelopment.meteorclient.utils.player.PlayerUtils; +import meteordevelopment.meteorclient.utils.player.Rotations; +import meteordevelopment.meteorclient.utils.render.color.SettingColor; +import meteordevelopment.meteorclient.utils.world.BlockUtils; +import meteordevelopment.orbit.EventHandler; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Items; +import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; + +public class AutoCity extends Module { + private final SettingGroup sgGeneral = settings.getDefaultGroup(); + private final SettingGroup sgRender = settings.createGroup("Render"); + + + private final Setting targetRange = sgGeneral.add(new DoubleSetting.Builder() + .name("target-range") + .description("The radius in which players get targeted.") + .defaultValue(5.5) + .min(0) + .sliderMax(7) + .build() + ); + + private final Setting breakRange = sgGeneral.add(new DoubleSetting.Builder() + .name("break-range") + .description("How close a block must be to you to be considered.") + .defaultValue(4.5) + .min(0) + .sliderMax(6) + .build() + ); + + private final Setting switchMode = sgGeneral.add(new EnumSetting.Builder() + .name("switch-mode") + .description("How to switch to a pickaxe.") + .defaultValue(SwitchMode.Normal) + .build() + ); + + private final Setting support = sgGeneral.add(new BoolSetting.Builder() + .name("support") + .description("If there is no block below a city block it will place one before mining.") + .defaultValue(true) + .build() + ); + + private final Setting placeRange = sgGeneral.add(new DoubleSetting.Builder() + .name("place-range") + .description("How far away to try and place a block.") + .defaultValue(4.5) + .min(0) + .sliderMax(6) + .visible(support::get) + .build() + ); + + private final Setting rotate = sgGeneral.add(new BoolSetting.Builder() + .name("rotate") + .description("Automatically rotates you towards the city block.") + .defaultValue(true) + .build() + ); + + private final Setting chatInfo = sgGeneral.add(new BoolSetting.Builder() + .name("chat-info") + .description("Whether the module should send messages in chat.") + .defaultValue(true) + .build() + ); + + // Render + + private final Setting swingHand = sgRender.add(new BoolSetting.Builder() + .name("swing-hand") + .description("Whether to render your hand swinging.") + .defaultValue(false) + .build() + ); + + private final Setting renderBlock = sgRender.add(new BoolSetting.Builder() + .name("render-block") + .description("Whether to render the block being broken.") + .defaultValue(true) + .build() + ); + + private final Setting shapeMode = sgRender.add(new EnumSetting.Builder() + .name("shape-mode") + .description("How the shapes are rendered.") + .defaultValue(ShapeMode.Both) + .visible(renderBlock::get) + .build() + ); + + private final Setting sideColor = sgRender.add(new ColorSetting.Builder() + .name("side-color") + .description("The side color of the rendering.") + .defaultValue(new SettingColor(225, 0, 0, 75)) + .visible(() -> renderBlock.get() && shapeMode.get().sides()) + .build() + ); + + private final Setting lineColor = sgRender.add(new ColorSetting.Builder() + .name("line-color") + .description("The line color of the rendering.") + .defaultValue(new SettingColor(225, 0, 0, 255)) + .visible(() -> renderBlock.get() && shapeMode.get().lines()) + .build() + ); + + private PlayerEntity target; + private BlockPos targetPos; + private FindItemResult pick; + private float progress; + + public AutoCity() { + super(Categories.Combat, "auto-city", "Automatically mine blocks next to someone's feet."); + } + + @Override + public void onActivate() { + target = TargetUtils.getPlayerTarget(targetRange.get(), SortPriority.ClosestAngle); + if (TargetUtils.isBadTarget(target, targetRange.get())) { + if (chatInfo.get()) error("Couldn't find a target, disabling."); + toggle(); + return; + } + + targetPos = EntityUtils.getCityBlock(target); + if (targetPos == null || PlayerUtils.distanceTo(targetPos) > breakRange.get()) { + if (chatInfo.get()) error("Couldn't find a target, disabling."); + toggle(); + return; + } + + if (support.get()) { + BlockPos supportPos = targetPos.down(); + if (!(PlayerUtils.distanceTo(supportPos) > placeRange.get())) { + BlockUtils.place(supportPos, InvUtils.findInHotbar(Items.OBSIDIAN), rotate.get(), 0, true); + } + } + + pick = InvUtils.find(itemStack -> itemStack.getItem() == Items.DIAMOND_PICKAXE || itemStack.getItem() == Items.NETHERITE_PICKAXE); + if (!pick.isHotbar()) { + error("No pickaxe found... disabling."); + toggle(); + return; + } + + progress = 0.0f; + mine(false); + } + + @Override + public void onDeactivate() { + target = null; + targetPos = null; + } + + @EventHandler + private void onTick(TickEvent.Pre event) { + if (TargetUtils.isBadTarget(target, targetRange.get())) { + toggle(); + return; + } + + if (PlayerUtils.distanceTo(targetPos) > breakRange.get()) { + if (chatInfo.get()) error("Couldn't find a target, disabling."); + toggle(); + return; + } + + if (progress < 1.0f) { + pick = InvUtils.find(itemStack -> itemStack.getItem() == Items.DIAMOND_PICKAXE || itemStack.getItem() == Items.NETHERITE_PICKAXE); + if (!pick.isHotbar()) { + error("No pickaxe found... disabling."); + toggle(); + return; + } + progress += BlockUtils.getBreakDelta(pick.slot(), mc.world.getBlockState(targetPos)); + if (progress < 1.0f) return; + } + + mine(true); + toggle(); + } + + public void mine(boolean done) { + InvUtils.swap(pick.slot(), switchMode.get() == SwitchMode.Silent); + if (rotate.get()) Rotations.rotate(Rotations.getYaw(targetPos), Rotations.getPitch(targetPos)); + + Direction direction = (mc.player.getY() > targetPos.getY()) ? Direction.UP : Direction.DOWN; + if (!done) mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, targetPos, direction)); + mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, targetPos, direction)); + + if (swingHand.get()) mc.player.swingHand(Hand.MAIN_HAND); + else mc.getNetworkHandler().sendPacket(new HandSwingC2SPacket(Hand.MAIN_HAND)); + + if (switchMode.get() == SwitchMode.Silent) InvUtils.swapBack(); + } + + @EventHandler + private void onRender3D(Render3DEvent event) { + if (targetPos == null || !renderBlock.get()) return; + event.renderer.box(targetPos, sideColor.get(), lineColor.get(), shapeMode.get(), 0); + } + + @Override + public String getInfoString() { + return EntityUtils.getName(target); + } + + public enum SwitchMode { + Normal, + Silent + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/world/PacketMine.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/world/PacketMine.java index 9d9489dbe1..73f2f8d3f8 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/world/PacketMine.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/world/PacketMine.java @@ -202,51 +202,6 @@ private void onRender(Render3DEvent event) { } } - private double getBreakDelta(int slot, BlockState state) { - float hardness = state.getHardness(null, null); - if (hardness == -1) return 0; - else { - return getBlockBreakingSpeed(slot, state) / hardness / (!state.isToolRequired() || mc.player.getInventory().main.get(slot).isSuitableFor(state) ? 30 : 100); - } - } - - private double getBlockBreakingSpeed(int slot, BlockState block) { - double speed = mc.player.getInventory().main.get(slot).getMiningSpeedMultiplier(block); - - if (speed > 1) { - ItemStack tool = mc.player.getInventory().getStack(slot); - - int efficiency = EnchantmentHelper.getLevel(Enchantments.EFFICIENCY, tool); - - if (efficiency > 0 && !tool.isEmpty()) speed += efficiency * efficiency + 1; - } - - if (StatusEffectUtil.hasHaste(mc.player)) { - speed *= 1 + (StatusEffectUtil.getHasteAmplifier(mc.player) + 1) * 0.2F; - } - - if (mc.player.hasStatusEffect(StatusEffects.MINING_FATIGUE)) { - float k = switch (mc.player.getStatusEffect(StatusEffects.MINING_FATIGUE).getAmplifier()) { - case 0 -> 0.3F; - case 1 -> 0.09F; - case 2 -> 0.0027F; - default -> 8.1E-4F; - }; - - speed *= k; - } - - if (mc.player.isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(mc.player)) { - speed /= 5.0F; - } - - if (!mc.player.isOnGround()) { - speed /= 5.0F; - } - - return speed; - } - public class MyBlock { public BlockPos blockPos; public BlockState blockState; @@ -301,7 +256,7 @@ public void mine() { } } - progress += getBreakDelta(bestSlot != -1 ? bestSlot : mc.player.getInventory().selectedSlot, blockState); + progress += BlockUtils.getBreakDelta(bestSlot != -1 ? bestSlot : mc.player.getInventory().selectedSlot, blockState); } private void sendMinePackets() { diff --git a/src/main/java/meteordevelopment/meteorclient/utils/entity/EntityUtils.java b/src/main/java/meteordevelopment/meteorclient/utils/entity/EntityUtils.java index de1f48ab54..7c309fd86e 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/entity/EntityUtils.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/entity/EntityUtils.java @@ -14,6 +14,8 @@ import meteordevelopment.meteorclient.mixin.WorldAccessor; import meteordevelopment.meteorclient.utils.player.PlayerUtils; import meteordevelopment.meteorclient.utils.render.color.Color; +import net.minecraft.block.AirBlock; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.block.entity.BlockEntity; @@ -35,8 +37,8 @@ import net.minecraft.world.entity.SectionedEntityCache; import net.minecraft.world.entity.SimpleEntityLookup; +import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -111,26 +113,53 @@ public static boolean isInRenderDistance(double posX, double posZ) { public static List getSurroundBlocks(PlayerEntity player) { if (player == null) return null; + BlockPos.Mutable testPos = new BlockPos.Mutable(); List positions = new ArrayList<>(); - for (Direction direction : Direction.values()) { - if (direction == Direction.UP || direction == Direction.DOWN) continue; + for (Direction direction : Direction.HORIZONTAL) { + testPos.set(player.getBlockPos()).offset(direction); - BlockPos pos = player.getBlockPos().offset(direction); - - if (mc.world.getBlockState(pos).getBlock() == Blocks.OBSIDIAN) { - positions.add(pos); + if (mc.world.getBlockState(testPos).getBlock() == Blocks.OBSIDIAN) { + positions.add(testPos); } } return positions; } + @Nullable public static BlockPos getCityBlock(PlayerEntity player) { - List posList = getSurroundBlocks(player); - posList.sort(Comparator.comparingDouble(PlayerUtils::squaredDistanceTo)); - return posList.isEmpty() ? null : posList.get(0); + BlockPos bestPos = null; + int bestScore = 0; + BlockPos.Mutable testPos = new BlockPos.Mutable(); + + for (BlockPos pos : getSurroundBlocks(player)) { + int score = 1; + + for (Direction direction : Direction.values()) { + testPos.set(pos).offset(direction); + Block block = mc.world.getBlockState(testPos).getBlock(); + + if (direction == Direction.DOWN && block == Blocks.OBSIDIAN || block == Blocks.BEDROCK) { + score+= 2; + continue; + } + + if (direction != Direction.DOWN && block instanceof AirBlock) { + score++; + } + } + + score -= PlayerUtils.distanceTo(pos); + + if (score >= bestScore) { + bestPos = pos; + bestScore = score; + } + } + + return bestPos; } public static String getName(Entity entity) { diff --git a/src/main/java/meteordevelopment/meteorclient/utils/world/BlockUtils.java b/src/main/java/meteordevelopment/meteorclient/utils/world/BlockUtils.java index 9b00a1984e..3d6b5c79c6 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/world/BlockUtils.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/world/BlockUtils.java @@ -17,7 +17,13 @@ import net.minecraft.block.*; import net.minecraft.block.enums.BlockHalf; import net.minecraft.block.enums.SlabType; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.enchantment.Enchantments; +import net.minecraft.entity.effect.StatusEffectUtil; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.item.ItemStack; import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket; +import net.minecraft.registry.tag.FluidTags; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; @@ -261,4 +267,49 @@ public static boolean isExposed(BlockPos blockPos) { return false; } + + public static double getBreakDelta(int slot, BlockState state) { + float hardness = state.getHardness(null, null); + if (hardness == -1) return 0; + else { + return getBlockBreakingSpeed(slot, state) / hardness / (!state.isToolRequired() || mc.player.getInventory().main.get(slot).isSuitableFor(state) ? 30 : 100); + } + } + + private static double getBlockBreakingSpeed(int slot, BlockState block) { + double speed = mc.player.getInventory().main.get(slot).getMiningSpeedMultiplier(block); + + if (speed > 1) { + ItemStack tool = mc.player.getInventory().getStack(slot); + + int efficiency = EnchantmentHelper.getLevel(Enchantments.EFFICIENCY, tool); + + if (efficiency > 0 && !tool.isEmpty()) speed += efficiency * efficiency + 1; + } + + if (StatusEffectUtil.hasHaste(mc.player)) { + speed *= 1 + (StatusEffectUtil.getHasteAmplifier(mc.player) + 1) * 0.2F; + } + + if (mc.player.hasStatusEffect(StatusEffects.MINING_FATIGUE)) { + float k = switch (mc.player.getStatusEffect(StatusEffects.MINING_FATIGUE).getAmplifier()) { + case 0 -> 0.3F; + case 1 -> 0.09F; + case 2 -> 0.0027F; + default -> 8.1E-4F; + }; + + speed *= k; + } + + if (mc.player.isSubmergedIn(FluidTags.WATER) && !EnchantmentHelper.hasAquaAffinity(mc.player)) { + speed /= 5.0F; + } + + if (!mc.player.isOnGround()) { + speed /= 5.0F; + } + + return speed; + } } diff --git a/src/main/resources/meteor-client.accesswidener b/src/main/resources/meteor-client.accesswidener index 8e2bb3ad5f..7c3d0c7ea1 100644 --- a/src/main/resources/meteor-client.accesswidener +++ b/src/main/resources/meteor-client.accesswidener @@ -70,5 +70,6 @@ accessible class net/minecraft/client/gui/screen/ingame/BeaconScreen$EffectB accessible class net/minecraft/client/resource/ResourceReloadLogger$ReloadState accessible field net/minecraft/block/AbstractBlock collidable Z +accessible field net/minecraft/util/math/Direction HORIZONTAL [Lnet/minecraft/util/math/Direction; accessible field net/minecraft/item/ItemGroups INVENTORY Lnet/minecraft/item/ItemGroup;