From 81da32be793942549781bd571ea55a0824b85dfa Mon Sep 17 00:00:00 2001 From: Moxvallix Mox Date: Thu, 21 May 2026 05:02:28 +0930 Subject: [PATCH 1/4] feat(module): implement initial version of better farming --- .../meteorclient/systems/modules/Modules.java | 1 + .../systems/modules/player/BetterFarming.java | 155 ++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java index 2e7aaf7500..cdb331f44c 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/Modules.java @@ -435,6 +435,7 @@ private void initPlayer() { add(new AutoRespawn()); add(new AutoTool()); add(new BreakDelay()); + add(new BetterFarming()); add(new ChestSwap()); add(new EXPThrower()); add(new FakePlayer()); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java new file mode 100644 index 0000000000..327655ab68 --- /dev/null +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java @@ -0,0 +1,155 @@ +/* + * This file is part of the Meteor Client distribution (https://github.com/MeteorDevelopment/meteor-client). + * Copyright (c) Meteor Development. + */ + +package meteordevelopment.meteorclient.systems.modules.player; + +import meteordevelopment.meteorclient.MeteorClient; +import meteordevelopment.meteorclient.events.entity.player.StartBreakingBlockEvent; +import meteordevelopment.meteorclient.events.packets.PacketEvent; +import meteordevelopment.meteorclient.events.world.TickEvent; +import meteordevelopment.meteorclient.mixin.ServerboundMovePlayerPacketAccessor; +import meteordevelopment.meteorclient.mixininterface.IServerboundMovePlayerPacket; +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; +import meteordevelopment.meteorclient.utils.player.FindItemResult; +import meteordevelopment.meteorclient.utils.player.InvUtils; +import meteordevelopment.meteorclient.utils.world.BlockUtils; +import meteordevelopment.orbit.EventHandler; +import meteordevelopment.orbit.EventPriority; +import net.minecraft.core.BlockPos; +import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.ArrayList; + +public class BetterFarming extends Module { + private final SettingGroup sgGeneral = settings.getDefaultGroup(); + + // General + + private final Setting cropReplace = sgGeneral.add(new BoolSetting.Builder() + .name("crop-replace") + .description("Automatically plant new crop on harvest.") + .defaultValue(true) + .build() + ); + + private final Setting suppressTrample = sgGeneral.add(new BoolSetting.Builder() + .name("suppress-trample") + .description("Attempts to prevent player from trampling crops.") + .defaultValue(true) + .build() + ); + + public BetterFarming() { + super(Categories.Player, "better-farming", "Improvements for crop farming."); + } + + private Item placeItem = null; + private ArrayList placements = new ArrayList<>(); + private int mineCooldown = 0; + + @EventHandler(priority = EventPriority.HIGH) + private void onTick(TickEvent.Pre event) { + if (cropReplace.get()) autoPlaceTick(); + } + + @EventHandler(priority = EventPriority.HIGH) + private void onStartBreakingBlockEvent(StartBreakingBlockEvent event) { + if (cropReplace.get()) autoPlaceBreakEvent(event); + } + + @EventHandler + private void onSendPacket(PacketEvent.Send event) { + if (suppressTrample.get()) trampleSuppressionSendEvent(event); + } + + private void autoPlaceTick() { + if (mineCooldown > 0) { + mineCooldown--; + } + + if (placeItem == null) return; + if (placements.isEmpty()) { + placeItem = null; + return; + } + + BlockPos blockPos = placements.getFirst(); + placements.removeFirst(); + + FindItemResult foundItem = InvUtils.find(placeItem); + + // If the found item is not in mainhand yet, its not ready + if (!foundItem.isMainHand()) return; + + BlockState blockState = mc.level.getBlockState(blockPos); + + if (blockState.is(BlockTags.AIR)) { + BlockUtils.place(blockPos, foundItem, 0); + } else { + BlockUtils.breakBlock(blockPos, true); + } + } + + private void autoPlaceBreakEvent(StartBreakingBlockEvent event) { + MeteorClient.LOG.info("BREAK"); + BlockState blockState = mc.level.getBlockState(event.blockPos); + + if (!blockState.is(BlockTags.CROPS)) return; + + if (mineCooldown > 0) { + event.cancel(); + return; + } + + Item blockItem = blockState.getBlock().asItem(); + FindItemResult foundItem = InvUtils.find(blockItem); + + if (!foundItem.found()) return; + + placeItem = blockItem; + placements.add(event.blockPos); + mineCooldown = 3; + + if (foundItem.isMainHand()) return; + + mc.gameMode.handlePickItemFromBlock(event.blockPos, false); + + // Cancel the event to allow pickblock to do its thing + event.cancel(); + } + + private void trampleSuppressionSendEvent(PacketEvent.Send event) { + if (mc.player == null) return; + if (!(event.packet instanceof ServerboundMovePlayerPacket) + || ((IServerboundMovePlayerPacket) event.packet).meteor$getTag() == 1337) return; + + ServerboundMovePlayerPacket packet = (ServerboundMovePlayerPacket) event.packet; + + // Only suppress fall if current block or block beneath is farmland + BlockPos blockPos = new BlockPos( + (int) packet.getX(0d), + (int) packet.getY(0d), + (int) packet.getZ(0d) + ); + + BlockState blockState1 = mc.level.getBlockState(blockPos.offset(0, -1, 0)); + // Check block one over as well to fix edge jumping + BlockState blockState2 = mc.level.getBlockState(blockPos.offset(-1, -1, 1)); + + MeteorClient.LOG.info(blockPos.toShortString()); + + if (blockState1.is(Blocks.FARMLAND) || blockState2.is(Blocks.FARMLAND)) { + ((ServerboundMovePlayerPacketAccessor) event.packet).meteor$setOnGround(true); + } + } +} From bc455049ab23abeef63c4e4e0421f7e80804266d Mon Sep 17 00:00:00 2001 From: Moxvallix Mox Date: Thu, 21 May 2026 14:45:12 +0930 Subject: [PATCH 2/4] feat(module): add no break unripe to better farming --- .../systems/modules/player/BetterFarming.java | 77 +++++++++++++------ 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java index 327655ab68..6fc4ed0ef3 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java @@ -5,7 +5,6 @@ package meteordevelopment.meteorclient.systems.modules.player; -import meteordevelopment.meteorclient.MeteorClient; import meteordevelopment.meteorclient.events.entity.player.StartBreakingBlockEvent; import meteordevelopment.meteorclient.events.packets.PacketEvent; import meteordevelopment.meteorclient.events.world.TickEvent; @@ -26,6 +25,7 @@ import net.minecraft.tags.BlockTags; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.CropBlock; import net.minecraft.world.level.block.state.BlockState; import java.util.ArrayList; @@ -49,13 +49,20 @@ public class BetterFarming extends Module { .build() ); + private final Setting noBreakUnripe = sgGeneral.add(new BoolSetting.Builder() + .name("no-break-unripe") + .description("Prevents player from breaking unripe crops.") + .defaultValue(true) + .build() + ); + public BetterFarming() { super(Categories.Player, "better-farming", "Improvements for crop farming."); } private Item placeItem = null; - private ArrayList placements = new ArrayList<>(); - private int mineCooldown = 0; + private ArrayList cropPlacements = new ArrayList<>(); + private int blockBreakCooldown = 0; @EventHandler(priority = EventPriority.HIGH) private void onTick(TickEvent.Pre event) { @@ -64,6 +71,7 @@ private void onTick(TickEvent.Pre event) { @EventHandler(priority = EventPriority.HIGH) private void onStartBreakingBlockEvent(StartBreakingBlockEvent event) { + if (noBreakUnripe.get()) noBreakUnripeBreakEvent(event); if (cropReplace.get()) autoPlaceBreakEvent(event); } @@ -72,19 +80,29 @@ private void onSendPacket(PacketEvent.Send event) { if (suppressTrample.get()) trampleSuppressionSendEvent(event); } + private void noBreakUnripeBreakEvent(StartBreakingBlockEvent event) { + BlockState blockState = mc.level.getBlockState(event.blockPos); + + if (blockState.getBlock() instanceof CropBlock cropBlock) { + if (cropBlock.isMaxAge(blockState)) return; + + event.cancel(); + } + } + private void autoPlaceTick() { - if (mineCooldown > 0) { - mineCooldown--; + if (blockBreakCooldown > 0) { + blockBreakCooldown--; } if (placeItem == null) return; - if (placements.isEmpty()) { + if (cropPlacements.isEmpty()) { placeItem = null; return; } - BlockPos blockPos = placements.getFirst(); - placements.removeFirst(); + BlockPos blockPos = cropPlacements.getFirst(); + cropPlacements.removeFirst(); FindItemResult foundItem = InvUtils.find(placeItem); @@ -101,12 +119,13 @@ private void autoPlaceTick() { } private void autoPlaceBreakEvent(StartBreakingBlockEvent event) { - MeteorClient.LOG.info("BREAK"); + if (event.isCancelled()) return; + BlockState blockState = mc.level.getBlockState(event.blockPos); if (!blockState.is(BlockTags.CROPS)) return; - if (mineCooldown > 0) { + if (blockBreakCooldown > 0) { event.cancel(); return; } @@ -117,10 +136,12 @@ private void autoPlaceBreakEvent(StartBreakingBlockEvent event) { if (!foundItem.found()) return; placeItem = blockItem; - placements.add(event.blockPos); - mineCooldown = 3; + cropPlacements.add(event.blockPos); - if (foundItem.isMainHand()) return; + if (foundItem.isMainHand()) { + blockBreakCooldown = 3; + return; + }; mc.gameMode.handlePickItemFromBlock(event.blockPos, false); @@ -135,21 +156,33 @@ private void trampleSuppressionSendEvent(PacketEvent.Send event) { ServerboundMovePlayerPacket packet = (ServerboundMovePlayerPacket) event.packet; - // Only suppress fall if current block or block beneath is farmland BlockPos blockPos = new BlockPos( (int) packet.getX(0d), (int) packet.getY(0d), (int) packet.getZ(0d) ); - BlockState blockState1 = mc.level.getBlockState(blockPos.offset(0, -1, 0)); - // Check block one over as well to fix edge jumping - BlockState blockState2 = mc.level.getBlockState(blockPos.offset(-1, -1, 1)); - - MeteorClient.LOG.info(blockPos.toShortString()); - - if (blockState1.is(Blocks.FARMLAND) || blockState2.is(Blocks.FARMLAND)) { - ((ServerboundMovePlayerPacketAccessor) event.packet).meteor$setOnGround(true); + // Only suppress fall if blocks beneath are farmland + // Check 3x3 area beneath player, for if the player jumps on the edge + BlockPos[] posChecks = { + blockPos.offset(-1, -1, -1), + blockPos.offset(-1, -1, 0), + blockPos.offset(-1, -1, 1), + blockPos.offset(0, -1, -1), + blockPos.offset(0, -1, 0), + blockPos.offset(0, -1, 1), + blockPos.offset(1, -1, -1), + blockPos.offset(1, -1, 0), + blockPos.offset(1, -1, 1), + }; + + for (BlockPos pos : posChecks) { + BlockState blockState = mc.level.getBlockState(pos); + + if (blockState.is(Blocks.FARMLAND)) { + ((ServerboundMovePlayerPacketAccessor) event.packet).meteor$setOnGround(true); + break; + } } } } From 7abf97ee0463f322729551793a51b11a04524dc7 Mon Sep 17 00:00:00 2001 From: Moxvallix Mox Date: Thu, 21 May 2026 15:51:34 +0930 Subject: [PATCH 3/4] feat(module): add no break for cane blocks, also unripe nether wart --- .../systems/modules/player/BetterFarming.java | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java index 6fc4ed0ef3..5d298c89a9 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java @@ -26,12 +26,14 @@ import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.CropBlock; +import net.minecraft.world.level.block.NetherWartBlock; import net.minecraft.world.level.block.state.BlockState; import java.util.ArrayList; public class BetterFarming extends Module { private final SettingGroup sgGeneral = settings.getDefaultGroup(); + private final SettingGroup sgNoBreak = settings.createGroup("No Break"); // General @@ -49,19 +51,26 @@ public class BetterFarming extends Module { .build() ); - private final Setting noBreakUnripe = sgGeneral.add(new BoolSetting.Builder() + private final Setting noBreakUnripe = sgNoBreak.add(new BoolSetting.Builder() .name("no-break-unripe") .description("Prevents player from breaking unripe crops.") .defaultValue(true) .build() ); + private final Setting noBreakCaneBase = sgNoBreak.add(new BoolSetting.Builder() + .name("no-break-cane-base") + .description("Prevents player from breaking the base of sugarcane and bamboo blocks.") + .defaultValue(true) + .build() + ); + public BetterFarming() { super(Categories.Player, "better-farming", "Improvements for crop farming."); } private Item placeItem = null; - private ArrayList cropPlacements = new ArrayList<>(); + private final ArrayList cropPlacements = new ArrayList<>(); private int blockBreakCooldown = 0; @EventHandler(priority = EventPriority.HIGH) @@ -72,6 +81,7 @@ private void onTick(TickEvent.Pre event) { @EventHandler(priority = EventPriority.HIGH) private void onStartBreakingBlockEvent(StartBreakingBlockEvent event) { if (noBreakUnripe.get()) noBreakUnripeBreakEvent(event); + if (noBreakCaneBase.get()) noBreakCaneBaseEvent(event); if (cropReplace.get()) autoPlaceBreakEvent(event); } @@ -88,6 +98,24 @@ private void noBreakUnripeBreakEvent(StartBreakingBlockEvent event) { event.cancel(); } + + if (blockState.getBlock() instanceof NetherWartBlock) { + if (blockState.getValue(NetherWartBlock.AGE) >= NetherWartBlock.MAX_AGE) return; + + event.cancel(); + } + } + + private void noBreakCaneBaseEvent(StartBreakingBlockEvent event) { + BlockState blockState = mc.level.getBlockState(event.blockPos); + BlockState bsBelow = mc.level.getBlockState(event.blockPos.offset(0, -1, 0)); + + boolean bsIsCane = (blockState.is(Blocks.SUGAR_CANE) || blockState.is(Blocks.BAMBOO)); + boolean bsBelowIsCane = (bsBelow.is(Blocks.SUGAR_CANE) || bsBelow.is(Blocks.BAMBOO)); + + if (!bsIsCane || bsBelowIsCane) return; + + event.cancel(); } private void autoPlaceTick() { @@ -123,7 +151,7 @@ private void autoPlaceBreakEvent(StartBreakingBlockEvent event) { BlockState blockState = mc.level.getBlockState(event.blockPos); - if (!blockState.is(BlockTags.CROPS)) return; + if (!(blockState.is(BlockTags.CROPS) || blockState.is(Blocks.NETHER_WART))) return; if (blockBreakCooldown > 0) { event.cancel(); From 935a8a0d1c1f28bb02f424701c1421d524c5fae9 Mon Sep 17 00:00:00 2001 From: Moxvallix Mox Date: Fri, 22 May 2026 01:26:36 +0930 Subject: [PATCH 4/4] feat(module): implement no break supporting blocks in to better farming --- .../systems/modules/player/BetterFarming.java | 85 +++++++++++++++++-- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java index 5d298c89a9..581491ba22 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/player/BetterFarming.java @@ -21,6 +21,7 @@ import meteordevelopment.orbit.EventHandler; import meteordevelopment.orbit.EventPriority; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.tags.BlockTags; import net.minecraft.world.item.Item; @@ -28,6 +29,7 @@ import net.minecraft.world.level.block.CropBlock; import net.minecraft.world.level.block.NetherWartBlock; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; import java.util.ArrayList; @@ -60,7 +62,14 @@ public class BetterFarming extends Module { private final Setting noBreakCaneBase = sgNoBreak.add(new BoolSetting.Builder() .name("no-break-cane-base") - .description("Prevents player from breaking the base of sugarcane and bamboo blocks.") + .description("Prevents player from breaking the base of sugarcane, bamboo and cactus blocks.") + .defaultValue(true) + .build() + ); + + private final Setting noBreakSupportingBlocks = sgNoBreak.add(new BoolSetting.Builder() + .name("no-break-supporting-blocks") + .description("Prevents player from breaking the block supporting a crop, eg. Jungle log with cocoa, budding amethyst, sand under cactus.") .defaultValue(true) .build() ); @@ -81,7 +90,8 @@ private void onTick(TickEvent.Pre event) { @EventHandler(priority = EventPriority.HIGH) private void onStartBreakingBlockEvent(StartBreakingBlockEvent event) { if (noBreakUnripe.get()) noBreakUnripeBreakEvent(event); - if (noBreakCaneBase.get()) noBreakCaneBaseEvent(event); + if (noBreakCaneBase.get()) noBreakCaneBaseBreakEvent(event); + if (noBreakSupportingBlocks.get()) noBreakSupportingBlocksBreakEvent(event); if (cropReplace.get()) autoPlaceBreakEvent(event); } @@ -106,18 +116,81 @@ private void noBreakUnripeBreakEvent(StartBreakingBlockEvent event) { } } - private void noBreakCaneBaseEvent(StartBreakingBlockEvent event) { + private boolean isCaneBlock(BlockState blockState) { + return blockState.is(Blocks.SUGAR_CANE) + || blockState.is(Blocks.BAMBOO) + || blockState.is(Blocks.CACTUS); + } + + private boolean isSupportedBelowCrop(BlockState blockState) { + return blockState.is(BlockTags.CROPS) + || blockState.is(Blocks.NETHER_WART) + || isCaneBlock(blockState); + } + + private boolean isReplaceableCrop(BlockState blockState) { + return blockState.is(BlockTags.CROPS) + || blockState.is(Blocks.NETHER_WART); + } + + private boolean checkForCocoa(BlockPos blockPos) { + Direction[] checkDirections = { + Direction.NORTH, + Direction.SOUTH, + Direction.EAST, + Direction.WEST, + }; + + for (Direction direction : checkDirections) { + // Check block at blockPos offset by the direction normal vector. + BlockState bsCheck = mc.level.getBlockState(blockPos.offset(direction.getUnitVec3i())); + + if (bsCheck.is(Blocks.COCOA)) { + + Direction facingDirection = bsCheck.getValue(BlockStateProperties.HORIZONTAL_FACING); + + if (facingDirection == direction.getOpposite()) { + return true; + } + } + + } + + return false; + } + + private void noBreakCaneBaseBreakEvent(StartBreakingBlockEvent event) { BlockState blockState = mc.level.getBlockState(event.blockPos); BlockState bsBelow = mc.level.getBlockState(event.blockPos.offset(0, -1, 0)); - boolean bsIsCane = (blockState.is(Blocks.SUGAR_CANE) || blockState.is(Blocks.BAMBOO)); - boolean bsBelowIsCane = (bsBelow.is(Blocks.SUGAR_CANE) || bsBelow.is(Blocks.BAMBOO)); + boolean bsIsCane = isCaneBlock(blockState); + boolean bsBelowIsCane = isCaneBlock(bsBelow); if (!bsIsCane || bsBelowIsCane) return; event.cancel(); } + private void noBreakSupportingBlocksBreakEvent(StartBreakingBlockEvent event) { + BlockPos blockPos = event.blockPos; + BlockState blockState = mc.level.getBlockState(blockPos); + BlockState bsAbove = mc.level.getBlockState(blockPos.offset(0, 1, 0)); + + if (!isSupportedBelowCrop(blockState) && isSupportedBelowCrop(bsAbove)) { + event.cancel(); + return; + } + + if (blockState.is(BlockTags.SUPPORTS_COCOA) && checkForCocoa(blockPos)) { + event.cancel(); + return; + } + + if (blockState.is(Blocks.BUDDING_AMETHYST)) { + event.cancel(); + } + } + private void autoPlaceTick() { if (blockBreakCooldown > 0) { blockBreakCooldown--; @@ -151,7 +224,7 @@ private void autoPlaceBreakEvent(StartBreakingBlockEvent event) { BlockState blockState = mc.level.getBlockState(event.blockPos); - if (!(blockState.is(BlockTags.CROPS) || blockState.is(Blocks.NETHER_WART))) return; + if (!isReplaceableCrop(blockState)) return; if (blockBreakCooldown > 0) { event.cancel();