Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Часть функционала AutoLes+FastBreak upgrade | EXOSWARE 1.21.4

Начинающий
Начинающий
Статус
Оффлайн
Регистрация
8 Авг 2025
Сообщения
41
Реакции
0
Выберите загрузчик игры
  1. Fabric
Салем YouGame!
Доделал тему https://yougame.biz/threads/371203/ (noad)
=============================================
Что было сделано(ChangeLog):
Перенёс на 1.21.4 exosware.

сделал 85монет/c
Добавил fast режим(улучшил)
Лучше работает с FastBreak(код тоже указал)
=============================================
Так ну а вот и сам код:

(AUTOLES)О великий Rastyshka спасибо тебе!!!:
Expand Collapse Copy
package Spooky.fun.modules.player;

import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import Spooky.fun.events.Event;
import Spooky.fun.events.impl.EventUpdate;
import Spooky.fun.modules.Function;
import Spooky.fun.modules.FunctionAnnotation;
import Spooky.fun.modules.Type;
import Spooky.fun.modules.setting.BooleanSetting;
import Spooky.fun.modules.setting.ModeSetting;
import Spooky.fun.modules.setting.SliderSetting;
import Spooky.fun.modules.setting.TextSetting;
import Spooky.fun.util.player.TimerUtil;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * AutoLes
 * Автоматически ломает брёвна вокруг игрока + авто /sellwood и /pay.
 */
@FunctionAnnotation(
        name = "AutoLes",
        type = Type.Player,
        desc = "Автоматически ломает брёвна и сдаёт дерево"
)
public class AutoLes extends Function {

    private BlockPos targetPos;

    private final ModeSetting breakMode = new ModeSetting("Режим", "Default", "Default", "Fast");
    // Здесь воспринимаем значение как ОПЕРАЦИИ В ТИК (а не пакеты/сек), поэтому можно ставить 50–150 для очень быстрого фарма
    private final SliderSetting packetsPerSecond = new SliderSetting("Скорость (оп/тик)", 60.0f, 1.0f, 200.0f, 1.0f,
            () -> breakMode.is("Fast"));
    private final SliderSetting breakRadius = new SliderSetting("Радиус", 4.0f, 1.0f, 6.0f, 0.5f);

    private final BooleanSetting inf = new BooleanSetting("Чудеса", false, "Для баг. дерева (ломает один и тот же блок)");
    private final BooleanSetting swing = new BooleanSetting("Махать рукой", true);
    private final BooleanSetting autoWood = new BooleanSetting("Авто-сдача", true);
    private final BooleanSetting autoPay = new BooleanSetting("AutoPay", false);

    private final TextSetting namePay = new TextSetting("Ник для перевода", "name", () -> autoPay.get());
    private final SliderSetting valuePay = new SliderSetting("Кол-во монет для перевода", 1000, 500, 25000, 1000,
            () -> autoPay.get());
    private final SliderSetting timer = new SliderSetting("Расписание/c", 20, 1, 60, 1,
            () -> autoPay.get() || autoWood.get());

    private final TimerUtil sellTimer = new TimerUtil();
    private final TimerUtil payTimer = new TimerUtil();
    private final TimerUtil breakTimer = new TimerUtil();

    public AutoLes() {
        addSettings(breakMode, breakRadius, packetsPerSecond, inf, swing, autoWood, autoPay, namePay, valuePay, timer);
    }

    @Override
    public void onEvent(Event event) {
        if (!(event instanceof EventUpdate)) return;

        updateNuker();
        autoSell();
        autoPay();
    }

    private void autoSell() {
        if (!autoWood.get() || mc.player == null) return;

        long intervalMs = (long) (timer.get().doubleValue() * 500L);
        if (sellTimer.hasTimeElapsed(intervalMs)) {
            mc.player.networkHandler.sendChatCommand("sellwood");
            sellTimer.reset();
        }
    }

    private void autoPay() {
        if (!autoPay.get() || mc.player == null) return;

        long intervalMs = (long) (timer.get().doubleValue() * 500L) + 200L;
        if (payTimer.hasTimeElapsed(intervalMs)) {
            String nick = namePay.getValue();
            int amount = valuePay.get().intValue();
            mc.player.networkHandler.sendChatCommand("pay " + nick + " " + amount);
            payTimer.reset();
        }
    }

    private void updateNuker() {
        if (mc.player == null || mc.world == null || mc.interactionManager == null) {
            targetPos = null;
            return;
        }

        if (breakMode.is("Fast")) {
            runFastMode();
        } else {
            runDefaultMode();
        }
    }

    private void runDefaultMode() {
        // инвалидируем текущую цель
        if (targetPos != null &&
                (!isLog(targetPos) || !isInRange(targetPos) || !isVisible(targetPos))) {
            targetPos = null;
        }

        if (targetPos != null) {
            breakBlockDefault();
        } else {
            findAndBreakNewTargetDefault();
        }
    }

    private void findAndBreakNewTargetDefault() {
        BlockPos playerPos = mc.player.getBlockPos();

        int radius = (int) breakRadius.get().doubleValue();
        BlockPos from = playerPos.add(-radius, -radius, -radius);
        BlockPos to = playerPos.add(radius, radius, radius);

        List<BlockPos> blocks = getAllInBox(from, to);

        targetPos = blocks.stream()
                .filter(this::isLog)
                .filter(this::isInRange)
                .filter(this::isVisible)
                .min(Comparator.comparing(pos ->
                        mc.player.squaredDistanceTo(Vec3d.ofCenter(pos))
                ))
                .orElse(null);

        if (targetPos != null) {
            breakBlockDefault();
        }
    }

    private void breakBlockDefault() {
        if (targetPos == null) return;

        if (breakMode.is("Default")) {
            if (breakTimer.hasTimeElapsed(3L)) {
                mc.interactionManager.attackBlock(targetPos, Direction.UP);
                mc.interactionManager.updateBlockBreakingProgress(targetPos, Direction.UP);
                if (swing.get()) {
                    mc.player.swingHand(Hand.MAIN_HAND);
                }
                breakTimer.reset();
            }
        }
    }

    private boolean isInRange(BlockPos pos) {
        if (mc.player == null) return false;

        double distanceSq = mc.player.squaredDistanceTo(
                pos.getX() + 0.5,
                pos.getY() + 0.5,
                pos.getZ() + 0.5
        );
        double r = breakRadius.get().doubleValue();
        double maxSq = r * r;

        return distanceSq <= maxSq;
    }

    private boolean isVisible(BlockPos pos) {
        if (mc.world == null || mc.player == null) return false;
        // В исходнике всегда true, здесь оставляем так же
        return true;
    }

    private boolean isLog(BlockPos pos) {
        if (mc.world == null) return false;

        BlockState state = mc.world.getBlockState(pos);
        if (state.isIn(BlockTags.LOGS)) return true;
        String key = state.getBlock().getTranslationKey().toLowerCase();
        return key.contains("log") || key.contains("wood") || key.contains("stem");
    }

    private List<BlockPos> getAllInBox(BlockPos from, BlockPos to) {
        List<BlockPos> result = new ArrayList<>();
        int minX = Math.min(from.getX(), to.getX());
        int minY = Math.min(from.getY(), to.getY());
        int minZ = Math.min(from.getZ(), to.getZ());
        int maxX = Math.max(from.getX(), to.getX());
        int maxY = Math.max(from.getY(), to.getY());
        int maxZ = Math.max(from.getZ(), to.getZ());

        for (int x = minX; x <= maxX; x++) {
            for (int y = minY; y <= maxY; y++) {
                for (int z = minZ; z <= maxZ; z++) {
                    result.add(new BlockPos(x, y, z));
                }
            }
        }
        return result;
    }

    // ------------ Fast режим (пакетный нукер) ------------

    private void runFastMode() {
        if (mc.player == null || mc.world == null || mc.player.networkHandler == null) return;

        // В Fast режиме воспринимаем слайдер как количество операций за ТИК.
        // Например, 60 = до 60 блоков за тик (до ~120 пакетов START/STOP).
        int ops = Math.max(1, (int) packetsPerSecond.get().doubleValue());
        boolean acted = false;

        // инвалидируем текущую цель для inf-режима
        if (targetPos != null && (!isLog(targetPos) || !isInRange(targetPos) || !isVisible(targetPos))) {
            targetPos = null;
        }

        for (int i = 0; i < ops; i++) {
            if (inf.get()) {
                if (targetPos == null || !isInRange(targetPos) || !isLog(targetPos)) {
                    targetPos = findClosestLog();
                }
                if (targetPos != null) {
                    sendBreakPacket(targetPos);
                    acted = true;
                }
            } else {
                BlockPos t = findClosestLog();
                if (t != null) {
                    sendBreakPacket(t);
                    // визуально сразу очищаем блок
                    mc.world.setBlockState(t, Blocks.AIR.getDefaultState());
                    acted = true;
                } else {
                    break;
                }
            }
        }

        if (acted && swing.get()) {
            mc.player.swingHand(Hand.MAIN_HAND);
        }
    }

    private BlockPos findClosestLog() {
        BlockPos playerPos = mc.player.getBlockPos();

        int radius = (int) breakRadius.get().doubleValue();
        BlockPos from = playerPos.add(-radius, -radius, -radius);
        BlockPos to = playerPos.add(radius, radius, radius);

        List<BlockPos> blocks = getAllInBox(from, to);

        return blocks.stream()
                .filter(this::isLog)
                .filter(this::isInRange)
                .filter(this::isVisible)
                .min(Comparator.comparing(pos ->
                        mc.player.squaredDistanceTo(Vec3d.ofCenter(pos))
                ))
                .orElse(null);
    }

    private void sendBreakPacket(BlockPos pos) {
        mc.player.networkHandler.sendPacket(
                new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, pos, Direction.UP)
        );
        mc.player.networkHandler.sendPacket(
                new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, pos, Direction.UP)
        );
    }

    @Override
    protected void onDisable() {
        targetPos = null;
        super.onDisable();
    }
}

(FastBreak)О великий Rastyshka спасибо тебе!!!:
Expand Collapse Copy
package Spooky.fun.modules.player;

import net.minecraft.block.BlockState;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import Spooky.fun.events.Event;
import Spooky.fun.events.impl.EventUpdate;
import Spooky.fun.modules.Function;
import Spooky.fun.modules.FunctionAnnotation;
import Spooky.fun.modules.Type;
import Spooky.fun.modules.setting.SliderSetting;

/**
 * FastBreak(CREDITS: @multipays)
 * Ускоряет ломание блоков за счёт многократного вызова updateBlockBreakingProgress за тик.
 */
@FunctionAnnotation(name = "FastBreak", type = Type.Player, desc = "Ускоряет ломание блоков")
public class FastBreak extends Function {

    // 0.3f–1.0f, по дефолту 0.7f — как в оригинале
    public final SliderSetting speed = new SliderSetting("Ускорение", 0.7f, 0.3f, 1.0f, 0.1f);

    public FastBreak() {
        addSettings(speed);
    }

    @Override
    public void onEvent(Event event) {
        if (!(event instanceof EventUpdate)) return;
        if (mc.player == null || mc.world == null || mc.interactionManager == null) return;
        if (!(mc.crosshairTarget instanceof BlockHitResult)) return;

        BlockHitResult hit = (BlockHitResult) mc.crosshairTarget;
        BlockPos pos = hit.getBlockPos();
        if (pos == null) return;

        BlockState state = mc.world.getBlockState(pos);
        if (state == null || state.isAir()) return;

        // Ломаем только когда реально жмём ЛКМ
        if (!mc.options.attackKey.isPressed()) return;

        ClientPlayerInteractionManager im = mc.interactionManager;
        Direction side = hit.getSide();

        // Кол-во "дополнительных тиков" ломания за один реальный тик
        // 0.3 → 1, 0.7 → ~2, 1.0 → ~3
        float mul = speed.get().floatValue();
        int extraTicks = Math.max(1, Math.round(mul / 0.35f));

        for (int i = 0; i < extraTicks; i++) {
            im.updateBlockBreakingProgress(pos, side);
        }

        // Лёгкая анимация удара (чтоб визуально было понятно, что мод работает)
        mc.player.swingHand(Hand.MAIN_HAND);
    }
}

За чат джбт код не судите, если чёт надо будет докинуть - пишите.
 
Салем YouGame!
Доделал тему https://yougame.biz/threads/371203/ (noad)
=============================================
Что было сделано(ChangeLog):
Перенёс на 1.21.4 exosware.

сделал 85монет/c
Добавил fast режим(улучшил)
Лучше работает с FastBreak(код тоже указал)
=============================================
Так ну а вот и сам код:

(AUTOLES)О великий Rastyshka спасибо тебе!!!:
Expand Collapse Copy
package Spooky.fun.modules.player;

import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import Spooky.fun.events.Event;
import Spooky.fun.events.impl.EventUpdate;
import Spooky.fun.modules.Function;
import Spooky.fun.modules.FunctionAnnotation;
import Spooky.fun.modules.Type;
import Spooky.fun.modules.setting.BooleanSetting;
import Spooky.fun.modules.setting.ModeSetting;
import Spooky.fun.modules.setting.SliderSetting;
import Spooky.fun.modules.setting.TextSetting;
import Spooky.fun.util.player.TimerUtil;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * AutoLes
 * Автоматически ломает брёвна вокруг игрока + авто /sellwood и /pay.
 */
@FunctionAnnotation(
        name = "AutoLes",
        type = Type.Player,
        desc = "Автоматически ломает брёвна и сдаёт дерево"
)
public class AutoLes extends Function {

    private BlockPos targetPos;

    private final ModeSetting breakMode = new ModeSetting("Режим", "Default", "Default", "Fast");
    // Здесь воспринимаем значение как ОПЕРАЦИИ В ТИК (а не пакеты/сек), поэтому можно ставить 50–150 для очень быстрого фарма
    private final SliderSetting packetsPerSecond = new SliderSetting("Скорость (оп/тик)", 60.0f, 1.0f, 200.0f, 1.0f,
            () -> breakMode.is("Fast"));
    private final SliderSetting breakRadius = new SliderSetting("Радиус", 4.0f, 1.0f, 6.0f, 0.5f);

    private final BooleanSetting inf = new BooleanSetting("Чудеса", false, "Для баг. дерева (ломает один и тот же блок)");
    private final BooleanSetting swing = new BooleanSetting("Махать рукой", true);
    private final BooleanSetting autoWood = new BooleanSetting("Авто-сдача", true);
    private final BooleanSetting autoPay = new BooleanSetting("AutoPay", false);

    private final TextSetting namePay = new TextSetting("Ник для перевода", "name", () -> autoPay.get());
    private final SliderSetting valuePay = new SliderSetting("Кол-во монет для перевода", 1000, 500, 25000, 1000,
            () -> autoPay.get());
    private final SliderSetting timer = new SliderSetting("Расписание/c", 20, 1, 60, 1,
            () -> autoPay.get() || autoWood.get());

    private final TimerUtil sellTimer = new TimerUtil();
    private final TimerUtil payTimer = new TimerUtil();
    private final TimerUtil breakTimer = new TimerUtil();

    public AutoLes() {
        addSettings(breakMode, breakRadius, packetsPerSecond, inf, swing, autoWood, autoPay, namePay, valuePay, timer);
    }

    @Override
    public void onEvent(Event event) {
        if (!(event instanceof EventUpdate)) return;

        updateNuker();
        autoSell();
        autoPay();
    }

    private void autoSell() {
        if (!autoWood.get() || mc.player == null) return;

        long intervalMs = (long) (timer.get().doubleValue() * 500L);
        if (sellTimer.hasTimeElapsed(intervalMs)) {
            mc.player.networkHandler.sendChatCommand("sellwood");
            sellTimer.reset();
        }
    }

    private void autoPay() {
        if (!autoPay.get() || mc.player == null) return;

        long intervalMs = (long) (timer.get().doubleValue() * 500L) + 200L;
        if (payTimer.hasTimeElapsed(intervalMs)) {
            String nick = namePay.getValue();
            int amount = valuePay.get().intValue();
            mc.player.networkHandler.sendChatCommand("pay " + nick + " " + amount);
            payTimer.reset();
        }
    }

    private void updateNuker() {
        if (mc.player == null || mc.world == null || mc.interactionManager == null) {
            targetPos = null;
            return;
        }

        if (breakMode.is("Fast")) {
            runFastMode();
        } else {
            runDefaultMode();
        }
    }

    private void runDefaultMode() {
        // инвалидируем текущую цель
        if (targetPos != null &&
                (!isLog(targetPos) || !isInRange(targetPos) || !isVisible(targetPos))) {
            targetPos = null;
        }

        if (targetPos != null) {
            breakBlockDefault();
        } else {
            findAndBreakNewTargetDefault();
        }
    }

    private void findAndBreakNewTargetDefault() {
        BlockPos playerPos = mc.player.getBlockPos();

        int radius = (int) breakRadius.get().doubleValue();
        BlockPos from = playerPos.add(-radius, -radius, -radius);
        BlockPos to = playerPos.add(radius, radius, radius);

        List<BlockPos> blocks = getAllInBox(from, to);

        targetPos = blocks.stream()
                .filter(this::isLog)
                .filter(this::isInRange)
                .filter(this::isVisible)
                .min(Comparator.comparing(pos ->
                        mc.player.squaredDistanceTo(Vec3d.ofCenter(pos))
                ))
                .orElse(null);

        if (targetPos != null) {
            breakBlockDefault();
        }
    }

    private void breakBlockDefault() {
        if (targetPos == null) return;

        if (breakMode.is("Default")) {
            if (breakTimer.hasTimeElapsed(3L)) {
                mc.interactionManager.attackBlock(targetPos, Direction.UP);
                mc.interactionManager.updateBlockBreakingProgress(targetPos, Direction.UP);
                if (swing.get()) {
                    mc.player.swingHand(Hand.MAIN_HAND);
                }
                breakTimer.reset();
            }
        }
    }

    private boolean isInRange(BlockPos pos) {
        if (mc.player == null) return false;

        double distanceSq = mc.player.squaredDistanceTo(
                pos.getX() + 0.5,
                pos.getY() + 0.5,
                pos.getZ() + 0.5
        );
        double r = breakRadius.get().doubleValue();
        double maxSq = r * r;

        return distanceSq <= maxSq;
    }

    private boolean isVisible(BlockPos pos) {
        if (mc.world == null || mc.player == null) return false;
        // В исходнике всегда true, здесь оставляем так же
        return true;
    }

    private boolean isLog(BlockPos pos) {
        if (mc.world == null) return false;

        BlockState state = mc.world.getBlockState(pos);
        if (state.isIn(BlockTags.LOGS)) return true;
        String key = state.getBlock().getTranslationKey().toLowerCase();
        return key.contains("log") || key.contains("wood") || key.contains("stem");
    }

    private List<BlockPos> getAllInBox(BlockPos from, BlockPos to) {
        List<BlockPos> result = new ArrayList<>();
        int minX = Math.min(from.getX(), to.getX());
        int minY = Math.min(from.getY(), to.getY());
        int minZ = Math.min(from.getZ(), to.getZ());
        int maxX = Math.max(from.getX(), to.getX());
        int maxY = Math.max(from.getY(), to.getY());
        int maxZ = Math.max(from.getZ(), to.getZ());

        for (int x = minX; x <= maxX; x++) {
            for (int y = minY; y <= maxY; y++) {
                for (int z = minZ; z <= maxZ; z++) {
                    result.add(new BlockPos(x, y, z));
                }
            }
        }
        return result;
    }

    // ------------ Fast режим (пакетный нукер) ------------

    private void runFastMode() {
        if (mc.player == null || mc.world == null || mc.player.networkHandler == null) return;

        // В Fast режиме воспринимаем слайдер как количество операций за ТИК.
        // Например, 60 = до 60 блоков за тик (до ~120 пакетов START/STOP).
        int ops = Math.max(1, (int) packetsPerSecond.get().doubleValue());
        boolean acted = false;

        // инвалидируем текущую цель для inf-режима
        if (targetPos != null && (!isLog(targetPos) || !isInRange(targetPos) || !isVisible(targetPos))) {
            targetPos = null;
        }

        for (int i = 0; i < ops; i++) {
            if (inf.get()) {
                if (targetPos == null || !isInRange(targetPos) || !isLog(targetPos)) {
                    targetPos = findClosestLog();
                }
                if (targetPos != null) {
                    sendBreakPacket(targetPos);
                    acted = true;
                }
            } else {
                BlockPos t = findClosestLog();
                if (t != null) {
                    sendBreakPacket(t);
                    // визуально сразу очищаем блок
                    mc.world.setBlockState(t, Blocks.AIR.getDefaultState());
                    acted = true;
                } else {
                    break;
                }
            }
        }

        if (acted && swing.get()) {
            mc.player.swingHand(Hand.MAIN_HAND);
        }
    }

    private BlockPos findClosestLog() {
        BlockPos playerPos = mc.player.getBlockPos();

        int radius = (int) breakRadius.get().doubleValue();
        BlockPos from = playerPos.add(-radius, -radius, -radius);
        BlockPos to = playerPos.add(radius, radius, radius);

        List<BlockPos> blocks = getAllInBox(from, to);

        return blocks.stream()
                .filter(this::isLog)
                .filter(this::isInRange)
                .filter(this::isVisible)
                .min(Comparator.comparing(pos ->
                        mc.player.squaredDistanceTo(Vec3d.ofCenter(pos))
                ))
                .orElse(null);
    }

    private void sendBreakPacket(BlockPos pos) {
        mc.player.networkHandler.sendPacket(
                new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, pos, Direction.UP)
        );
        mc.player.networkHandler.sendPacket(
                new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, pos, Direction.UP)
        );
    }

    @Override
    protected void onDisable() {
        targetPos = null;
        super.onDisable();
    }
}

(FastBreak)О великий Rastyshka спасибо тебе!!!:
Expand Collapse Copy
package Spooky.fun.modules.player;

import net.minecraft.block.BlockState;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import Spooky.fun.events.Event;
import Spooky.fun.events.impl.EventUpdate;
import Spooky.fun.modules.Function;
import Spooky.fun.modules.FunctionAnnotation;
import Spooky.fun.modules.Type;
import Spooky.fun.modules.setting.SliderSetting;

/**
 * FastBreak(CREDITS: @multipays)
 * Ускоряет ломание блоков за счёт многократного вызова updateBlockBreakingProgress за тик.
 */
@FunctionAnnotation(name = "FastBreak", type = Type.Player, desc = "Ускоряет ломание блоков")
public class FastBreak extends Function {

    // 0.3f–1.0f, по дефолту 0.7f — как в оригинале
    public final SliderSetting speed = new SliderSetting("Ускорение", 0.7f, 0.3f, 1.0f, 0.1f);

    public FastBreak() {
        addSettings(speed);
    }

    @Override
    public void onEvent(Event event) {
        if (!(event instanceof EventUpdate)) return;
        if (mc.player == null || mc.world == null || mc.interactionManager == null) return;
        if (!(mc.crosshairTarget instanceof BlockHitResult)) return;

        BlockHitResult hit = (BlockHitResult) mc.crosshairTarget;
        BlockPos pos = hit.getBlockPos();
        if (pos == null) return;

        BlockState state = mc.world.getBlockState(pos);
        if (state == null || state.isAir()) return;

        // Ломаем только когда реально жмём ЛКМ
        if (!mc.options.attackKey.isPressed()) return;

        ClientPlayerInteractionManager im = mc.interactionManager;
        Direction side = hit.getSide();

        // Кол-во "дополнительных тиков" ломания за один реальный тик
        // 0.3 → 1, 0.7 → ~2, 1.0 → ~3
        float mul = speed.get().floatValue();
        int extraTicks = Math.max(1, Math.round(mul / 0.35f));

        for (int i = 0; i < extraTicks; i++) {
            im.updateBlockBreakingProgress(pos, side);
        }

        // Лёгкая анимация удара (чтоб визуально было понятно, что мод работает)
        mc.player.swingHand(Hand.MAIN_HAND);
    }
}

За чат джбт код не судите, если чёт надо будет докинуть - пишите.
Там на рв есть 2 дерева рядом там можно фармить сделай чтоб он сразу 2 дерева фармил и больше монет будет
 
в чём это дрочибельно?)
Там на рв есть 2 дерева рядом там можно фармить сделай чтоб он сразу 2 дерева фармил и больше монет будет
там не всегда 2 дерева
Там на рв есть 2 дерева рядом там можно фармить сделай чтоб он сразу 2 дерева фармил и больше монет будет
Перенеси на Javelin 1.21.4 👌
зачем? Я на миксины перенес, тебе тут только переделать импорты.
 
Назад
Сверху Снизу