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

Вопрос FTHelper не возвращает предмет после использования | Zenith norec

Начинающий
Начинающий
Статус
Оффлайн
Регистрация
17 Дек 2023
Сообщения
29
Реакции
1
Привет форум. Хотел написать короче FTHelper обычный, вроде бы даже получается. Столкнулся с такой проблемой что после того как предмет использовался(не хотбар), то делается двойной свап и предмет, допустим, трапка остаётся в хотбаре но если бить, то это фантомная трапка а в оригинале меч. Всё безуспешно. Надеюсь вы поможете мне.


FTHelper:
Expand Collapse Copy
package ru.okak.implement.features.modules.misc;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import ru.okak.api.event.EventHandler;
import ru.okak.api.feature.module.Module;
import ru.okak.api.feature.module.ModuleCategory;
import ru.okak.api.feature.module.setting.implement.BindSetting;
import ru.okak.api.feature.module.setting.implement.MultiSelectSetting;
import ru.okak.common.util.entity.PlayerInventoryComponent;
import ru.okak.common.util.entity.PlayerInventoryUtil;
import ru.okak.common.util.other.StopWatch;
import ru.okak.implement.events.keyboard.KeyEvent;
import ru.okak.implement.events.player.TickEvent;
import ru.okak.implement.features.draggables.Notifications;

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

@Getter
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class FTHelper extends Module {

    private static final String BIND_MODE = "Bind Mode";

    MultiSelectSetting mode = new MultiSelectSetting("Mode", "Select usage mode")
            .value("Bind Mode", "Close Inventory");

    BindSetting disorientationKey = new BindSetting("Disorientation", "Key for disorientation item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting trapKey = new BindSetting("Trap", "Key for trap item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting sugarKey = new BindSetting("Sugar", "Key for sugar item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting plastKey = new BindSetting("Plast", "Key for plast item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting freezeKey = new BindSetting("Freeze", "Key for freeze item")
            .visible(() -> mode.isSelected("Bind Mode"));

    List<Ability> abilities = new ArrayList<>();
    StopWatch delayStopWatch = new StopWatch();
    private static final long DELAY_MS = 200L;
    private static final int TICKS_BETWEEN_ACTIONS = 2;

    public FTHelper() {
        super("FTHelper", "FTHelper", ModuleCategory.MISC);
        setup(mode, disorientationKey, trapKey, sugarKey, plastKey, freezeKey);

        abilities.add(new Ability("трапка", Items.NETHERITE_SCRAP, trapKey,
                "Trap item not found!", "Used trap item!", true));
        abilities.add(new Ability("дезориентация", Items.ENDER_EYE, disorientationKey,
                "Disorientation item not found!", "Used disorientation item!", true));
        abilities.add(new Ability("явная пыль", Items.SUGAR, sugarKey,
                "Sugar item not found!", "Used sugar item!", true));
        abilities.add(new Ability("пласт", Items.DRIED_KELP, plastKey,
                "Plast item not found!", "Used plast item!", true));
        abilities.add(new Ability("снежок заморозка", Items.SNOWBALL, freezeKey,
                "Freeze item not found!", "Used freeze item!", true));
    }

    @EventHandler
    public void onKey(KeyEvent e) {
        if (!isBindModeActive() || mc.player == null) return;

        for (Ability ability : abilities) {
            if (e.isKeyDown(ability.getBindSetting().getKey())) {
                ability.queueUse();
            }
        }
    }

    @EventHandler
    public void onTick(TickEvent e) {
        if (!isBindModeActive() || mc.player == null) return;

        if (!delayStopWatch.finished(DELAY_MS)) return;

        for (Ability ability : abilities) {
            if (ability.consumeQueuedUse()) {
                triggerAbility(ability);
                delayStopWatch.reset();
                return;
            }
        }

        for (Ability ability : abilities) {
            ItemStack stack = new ItemStack(ability.getItem());
            float cooldown = mc.player.getItemCooldownManager().getCooldownProgress(stack, 0);
            ability.updateCooldown(cooldown);
        }
    }

    private void triggerAbility(Ability ability) {
        ItemStack stack = new ItemStack(ability.getItem());
        if (mc.player.getItemCooldownManager().isCoolingDown(stack)) {
            sendNotification("Item on cooldown!", Formatting.RED);
            return;
        }

        Slot slot = findItemByName(ability.getInventoryKeyword());

        if (slot == null) {
            sendNotification(ability.getNotFoundMessage(), Formatting.RED);
            return;
        }

        sendNotification(ability.getSuccessMessage(), Formatting.GREEN);
        useItemWithSlot(slot, ability);
    }

    private void useItemWithSlot(Slot slot, Ability ability) {
        int originalSlot = mc.player.getInventory().selectedSlot;
        int slotId = slot.id;
        boolean isInHotbar = slotId < 9;

        ItemStack oldHandItem = mc.player.getMainHandStack().copy();

        PlayerInventoryComponent.addTask(() -> {
            PlayerInventoryComponent.disableMoveKeys();

            if (isInHotbar) {
                if (originalSlot != slotId) {
                    mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(slotId));
                    mc.player.getInventory().selectedSlot = slotId;

                    PlayerInventoryComponent.script.addTickStep(1, () -> {
                        useCurrentItem();

                        if (ability.isRestoreSelectedSlot()) {
                            PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                                mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(originalSlot));
                                mc.player.getInventory().selectedSlot = originalSlot;
                                PlayerInventoryComponent.enableMoveKeys();
                            });
                        } else {
                            PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, PlayerInventoryComponent::enableMoveKeys);
                        }
                    });
                } else {
                    useCurrentItem();
                    PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, PlayerInventoryComponent::enableMoveKeys);
                }
            } else {
                if (oldHandItem.isEmpty()) {
                    PlayerInventoryUtil.clickSlot(slot.id, originalSlot, SlotActionType.SWAP, false);

                    PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                        useCurrentItem();

                        PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                            PlayerInventoryUtil.clickSlot(originalSlot, slot.id, SlotActionType.SWAP, false);

                            if (ability.isRestoreSelectedSlot()) {
                                mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(originalSlot));
                                mc.player.getInventory().selectedSlot = originalSlot;
                            }

                            PlayerInventoryComponent.script.addTickStep(1, PlayerInventoryComponent::enableMoveKeys);
                        });
                    });
                } else {
                    PlayerInventoryUtil.clickSlot(slot.id, originalSlot, SlotActionType.SWAP, false);

                    PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                        useCurrentItem();

                        PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                            PlayerInventoryUtil.clickSlot(slot.id, originalSlot, SlotActionType.SWAP, false);

                            if (ability.isRestoreSelectedSlot()) {
                                mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(originalSlot));
                                mc.player.getInventory().selectedSlot = originalSlot;
                            }

                            PlayerInventoryComponent.script.addTickStep(1, PlayerInventoryComponent::enableMoveKeys);
                        });
                    });
                }
            }
        });
    }

    private void useCurrentItem() {
        if (mc.player == null || mc.interactionManager == null) return;

        mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND);
        mc.player.swingHand(Hand.MAIN_HAND);
    }

    private Slot findItemByName(String name, boolean inHotBar) {
        if (mc.player == null) return null;

        int firstSlot = inHotBar ? 0 : 9;
        int lastSlot = inHotBar ? 9 : 36;

        for (int i = firstSlot; i < lastSlot; i++) {
            ItemStack itemStack = mc.player.getInventory().getStack(i);
            if (itemStack.isEmpty()) continue;

            String displayName = Formatting.strip(itemStack.getName().getString()).toLowerCase();
            if (displayName.contains(name.toLowerCase())) {
                return mc.player.currentScreenHandler.slots.get(i);
            }
        }
        return null;
    }

    private Slot findItemByName(String name) {
        Slot hotbarSlot = findItemByName(name, true);
        return hotbarSlot != null ? hotbarSlot : findItemByName(name, false);
    }

    private void sendNotification(String message, Formatting color) {
        Text notification = Text.literal("")
                .append(Text.literal("[FTHelper] ").formatted(Formatting.GRAY))
                .append(Text.literal(message).formatted(color));

        Notifications.getInstance().addList(notification.getString(), 2000);
    }

    private boolean isBindModeActive() {
        return mode.isSelected("Bind Mode");
    }

    @Override
    public void deactivate() {
        for (Ability ability : abilities) {
            ability.reset();
        }
        super.deactivate();
    }

    public List<HudAbilityState> getHudData(float partialTicks) {
        if (!isBindModeActive() || mc.player == null) {
            abilities.forEach(Ability::resetCooldown);
            return List.of();
        }

        List<HudAbilityState> states = new ArrayList<>();
        for (Ability ability : abilities) {
            int keyCode = ability.getBindSetting().getKey();
            if (keyCode == -1) {
                ability.resetCooldown();
                continue;
            }

            String keyName = resolveKeyName(keyCode);
            double cooldown = ability.getCooldownSeconds();

            states.add(new HudAbilityState(
                    new ItemStack(ability.getItem()),
                    keyName,
                    cooldown > 0 ? cooldown : null
            ));
        }

        return states;
    }

    private String resolveKeyName(int keyCode) {
        return "Key_" + keyCode;
    }

    @Getter
    public static class Ability {
        private final String inventoryKeyword;
        private final Item item;
        private final BindSetting bindSetting;
        private final String notFoundMessage;
        private final String successMessage;
        private final boolean restoreSelectedSlot;
        private boolean queuedUse;
        private final CooldownWatch cooldownWatch = new CooldownWatch();

        public Ability(String inventoryKeyword, Item item, BindSetting bindSetting,
                       String notFoundMessage, String successMessage, boolean restoreSelectedSlot) {
            this.inventoryKeyword = inventoryKeyword;
            this.item = item;
            this.bindSetting = bindSetting;
            this.notFoundMessage = notFoundMessage;
            this.successMessage = successMessage;
            this.restoreSelectedSlot = restoreSelectedSlot;
        }

        public void queueUse() {
            this.queuedUse = true;
        }

        public boolean consumeQueuedUse() {
            if (!queuedUse) return false;
            this.queuedUse = false;
            return true;
        }

        public void reset() {
            this.queuedUse = false;
            this.cooldownWatch.reset();
        }

        public void updateCooldown(float progress) {
            cooldownWatch.update(progress);
        }

        public double getCooldownSeconds() {
            return cooldownWatch.getRemainingSeconds();
        }

        public void resetCooldown() {
            cooldownWatch.reset();
        }
    }

    public static class CooldownWatch {
        private boolean active;
        private long startTime;
        private long totalDuration;

        public void update(float progress) {
            long now = System.currentTimeMillis();

            if (progress <= 0) {
                reset();
                return;
            }

            if (!active) {
                active = true;
                startTime = now;
                totalDuration = 0;
                return;
            }

            if (totalDuration == 0 && progress < 0.98f) {
                long elapsed = now - startTime;
                if (elapsed > 0 && progress > 0) {
                    totalDuration = (long)(elapsed / (1.0 - progress));
                }
            }
        }

        public double getRemainingSeconds() {
            if (!active || totalDuration == 0) return 0;

            long elapsed = System.currentTimeMillis() - startTime;
            long remaining = totalDuration - elapsed;

            if (remaining <= 0) {
                reset();
                return 0;
            }

            return remaining / 1000.0;
        }

        public void reset() {
            active = false;
            totalDuration = 0;
        }
    }

    public record HudAbilityState(ItemStack itemStack, String keyName, Double cooldownSeconds) {
        public boolean hasCooldown() {
            return cooldownSeconds != null && cooldownSeconds > 0;
        }
    }
}
 
Последнее редактирование:
Привет форум. Хотел написать короче FTHelper обычный, вроде бы даже получается. Столкнулся с такой проблемой что после того как предмет использовался(не хотбар), то делается двойной свап и предмет, допустим, трапка остаётся в хотбаре но если бить, то это фантомная трапка а в оригинале меч. Всё безуспешно. Надеюсь вы поможете мне.


FTHelper:
Expand Collapse Copy
package ru.okak.implement.features.modules.misc;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import ru.okak.api.event.EventHandler;
import ru.okak.api.feature.module.Module;
import ru.okak.api.feature.module.ModuleCategory;
import ru.okak.api.feature.module.setting.implement.BindSetting;
import ru.okak.api.feature.module.setting.implement.MultiSelectSetting;
import ru.okak.common.util.entity.PlayerInventoryComponent;
import ru.okak.common.util.entity.PlayerInventoryUtil;
import ru.okak.common.util.other.StopWatch;
import ru.okak.implement.events.keyboard.KeyEvent;
import ru.okak.implement.events.player.TickEvent;
import ru.okak.implement.features.draggables.Notifications;

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

@Getter
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class FTHelper extends Module {

    private static final String BIND_MODE = "Bind Mode";

    MultiSelectSetting mode = new MultiSelectSetting("Mode", "Select usage mode")
            .value("Bind Mode", "Close Inventory");

    BindSetting disorientationKey = new BindSetting("Disorientation", "Key for disorientation item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting trapKey = new BindSetting("Trap", "Key for trap item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting sugarKey = new BindSetting("Sugar", "Key for sugar item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting plastKey = new BindSetting("Plast", "Key for plast item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting freezeKey = new BindSetting("Freeze", "Key for freeze item")
            .visible(() -> mode.isSelected("Bind Mode"));

    List<Ability> abilities = new ArrayList<>();
    StopWatch delayStopWatch = new StopWatch();
    private static final long DELAY_MS = 200L;
    private static final int TICKS_BETWEEN_ACTIONS = 2;

    public FTHelper() {
        super("FTHelper", "FTHelper", ModuleCategory.MISC);
        setup(mode, disorientationKey, trapKey, sugarKey, plastKey, freezeKey);

        abilities.add(new Ability("трапка", Items.NETHERITE_SCRAP, trapKey,
                "Trap item not found!", "Used trap item!", true));
        abilities.add(new Ability("дезориентация", Items.ENDER_EYE, disorientationKey,
                "Disorientation item not found!", "Used disorientation item!", true));
        abilities.add(new Ability("явная пыль", Items.SUGAR, sugarKey,
                "Sugar item not found!", "Used sugar item!", true));
        abilities.add(new Ability("пласт", Items.DRIED_KELP, plastKey,
                "Plast item not found!", "Used plast item!", true));
        abilities.add(new Ability("снежок заморозка", Items.SNOWBALL, freezeKey,
                "Freeze item not found!", "Used freeze item!", true));
    }

    @EventHandler
    public void onKey(KeyEvent e) {
        if (!isBindModeActive() || mc.player == null) return;

        for (Ability ability : abilities) {
            if (e.isKeyDown(ability.getBindSetting().getKey())) {
                ability.queueUse();
            }
        }
    }

    @EventHandler
    public void onTick(TickEvent e) {
        if (!isBindModeActive() || mc.player == null) return;

        if (!delayStopWatch.finished(DELAY_MS)) return;

        for (Ability ability : abilities) {
            if (ability.consumeQueuedUse()) {
                triggerAbility(ability);
                delayStopWatch.reset();
                return;
            }
        }

        for (Ability ability : abilities) {
            ItemStack stack = new ItemStack(ability.getItem());
            float cooldown = mc.player.getItemCooldownManager().getCooldownProgress(stack, 0);
            ability.updateCooldown(cooldown);
        }
    }

    private void triggerAbility(Ability ability) {
        ItemStack stack = new ItemStack(ability.getItem());
        if (mc.player.getItemCooldownManager().isCoolingDown(stack)) {
            sendNotification("Item on cooldown!", Formatting.RED);
            return;
        }

        Slot slot = findItemByName(ability.getInventoryKeyword());

        if (slot == null) {
            sendNotification(ability.getNotFoundMessage(), Formatting.RED);
            return;
        }

        sendNotification(ability.getSuccessMessage(), Formatting.GREEN);
        useItemWithSlot(slot, ability);
    }

    private void useItemWithSlot(Slot slot, Ability ability) {
        int originalSlot = mc.player.getInventory().selectedSlot;
        int slotId = slot.id;
        boolean isInHotbar = slotId < 9;

        ItemStack oldHandItem = mc.player.getMainHandStack().copy();

        PlayerInventoryComponent.addTask(() -> {
            PlayerInventoryComponent.disableMoveKeys();

            if (isInHotbar) {
                if (originalSlot != slotId) {
                    mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(slotId));
                    mc.player.getInventory().selectedSlot = slotId;

                    PlayerInventoryComponent.script.addTickStep(1, () -> {
                        useCurrentItem();

                        if (ability.isRestoreSelectedSlot()) {
                            PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                                mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(originalSlot));
                                mc.player.getInventory().selectedSlot = originalSlot;
                                PlayerInventoryComponent.enableMoveKeys();
                            });
                        } else {
                            PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, PlayerInventoryComponent::enableMoveKeys);
                        }
                    });
                } else {
                    useCurrentItem();
                    PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, PlayerInventoryComponent::enableMoveKeys);
                }
            } else {
                if (oldHandItem.isEmpty()) {
                    PlayerInventoryUtil.clickSlot(slot.id, originalSlot, SlotActionType.SWAP, false);

                    PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                        useCurrentItem();

                        PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                            PlayerInventoryUtil.clickSlot(originalSlot, slot.id, SlotActionType.SWAP, false);

                            if (ability.isRestoreSelectedSlot()) {
                                mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(originalSlot));
                                mc.player.getInventory().selectedSlot = originalSlot;
                            }

                            PlayerInventoryComponent.script.addTickStep(1, PlayerInventoryComponent::enableMoveKeys);
                        });
                    });
                } else {
                    PlayerInventoryUtil.clickSlot(slot.id, originalSlot, SlotActionType.SWAP, false);

                    PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                        useCurrentItem();

                        PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                            PlayerInventoryUtil.clickSlot(slot.id, originalSlot, SlotActionType.SWAP, false);

                            if (ability.isRestoreSelectedSlot()) {
                                mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(originalSlot));
                                mc.player.getInventory().selectedSlot = originalSlot;
                            }

                            PlayerInventoryComponent.script.addTickStep(1, PlayerInventoryComponent::enableMoveKeys);
                        });
                    });
                }
            }
        });
    }

    private void useCurrentItem() {
        if (mc.player == null || mc.interactionManager == null) return;

        mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND);
        mc.player.swingHand(Hand.MAIN_HAND);
    }

    private Slot findItemByName(String name, boolean inHotBar) {
        if (mc.player == null) return null;

        int firstSlot = inHotBar ? 0 : 9;
        int lastSlot = inHotBar ? 9 : 36;

        for (int i = firstSlot; i < lastSlot; i++) {
            ItemStack itemStack = mc.player.getInventory().getStack(i);
            if (itemStack.isEmpty()) continue;

            String displayName = Formatting.strip(itemStack.getName().getString()).toLowerCase();
            if (displayName.contains(name.toLowerCase())) {
                return mc.player.currentScreenHandler.slots.get(i);
            }
        }
        return null;
    }

    private Slot findItemByName(String name) {
        Slot hotbarSlot = findItemByName(name, true);
        return hotbarSlot != null ? hotbarSlot : findItemByName(name, false);
    }

    private void sendNotification(String message, Formatting color) {
        Text notification = Text.literal("")
                .append(Text.literal("[FTHelper] ").formatted(Formatting.GRAY))
                .append(Text.literal(message).formatted(color));

        Notifications.getInstance().addList(notification.getString(), 2000);
    }

    private boolean isBindModeActive() {
        return mode.isSelected("Bind Mode");
    }

    @Override
    public void deactivate() {
        for (Ability ability : abilities) {
            ability.reset();
        }
        super.deactivate();
    }

    public List<HudAbilityState> getHudData(float partialTicks) {
        if (!isBindModeActive() || mc.player == null) {
            abilities.forEach(Ability::resetCooldown);
            return List.of();
        }

        List<HudAbilityState> states = new ArrayList<>();
        for (Ability ability : abilities) {
            int keyCode = ability.getBindSetting().getKey();
            if (keyCode == -1) {
                ability.resetCooldown();
                continue;
            }

            String keyName = resolveKeyName(keyCode);
            double cooldown = ability.getCooldownSeconds();

            states.add(new HudAbilityState(
                    new ItemStack(ability.getItem()),
                    keyName,
                    cooldown > 0 ? cooldown : null
            ));
        }

        return states;
    }

    private String resolveKeyName(int keyCode) {
        return "Key_" + keyCode;
    }

    @Getter
    public static class Ability {
        private final String inventoryKeyword;
        private final Item item;
        private final BindSetting bindSetting;
        private final String notFoundMessage;
        private final String successMessage;
        private final boolean restoreSelectedSlot;
        private boolean queuedUse;
        private final CooldownWatch cooldownWatch = new CooldownWatch();

        public Ability(String inventoryKeyword, Item item, BindSetting bindSetting,
                       String notFoundMessage, String successMessage, boolean restoreSelectedSlot) {
            this.inventoryKeyword = inventoryKeyword;
            this.item = item;
            this.bindSetting = bindSetting;
            this.notFoundMessage = notFoundMessage;
            this.successMessage = successMessage;
            this.restoreSelectedSlot = restoreSelectedSlot;
        }

        public void queueUse() {
            this.queuedUse = true;
        }

        public boolean consumeQueuedUse() {
            if (!queuedUse) return false;
            this.queuedUse = false;
            return true;
        }

        public void reset() {
            this.queuedUse = false;
            this.cooldownWatch.reset();
        }

        public void updateCooldown(float progress) {
            cooldownWatch.update(progress);
        }

        public double getCooldownSeconds() {
            return cooldownWatch.getRemainingSeconds();
        }

        public void resetCooldown() {
            cooldownWatch.reset();
        }
    }

    public static class CooldownWatch {
        private boolean active;
        private long startTime;
        private long totalDuration;

        public void update(float progress) {
            long now = System.currentTimeMillis();

            if (progress <= 0) {
                reset();
                return;
            }

            if (!active) {
                active = true;
                startTime = now;
                totalDuration = 0;
                return;
            }

            if (totalDuration == 0 && progress < 0.98f) {
                long elapsed = now - startTime;
                if (elapsed > 0 && progress > 0) {
                    totalDuration = (long)(elapsed / (1.0 - progress));
                }
            }
        }

        public double getRemainingSeconds() {
            if (!active || totalDuration == 0) return 0;

            long elapsed = System.currentTimeMillis() - startTime;
            long remaining = totalDuration - elapsed;

            if (remaining <= 0) {
                reset();
                return 0;
            }

            return remaining / 1000.0;
        }

        public void reset() {
            active = false;
            totalDuration = 0;
        }
    }

    public record HudAbilityState(ItemStack itemStack, String keyName, Double cooldownSeconds) {
        public boolean hasCooldown() {
            return cooldownSeconds != null && cooldownSeconds > 0;
        }
    }
}
нада зделать так чтоби сервер видел каждий шаг, откритие инв - перемещение предмета - закритие инв - переключение слота - юз - откритие инв - вертать предмет - закрить инв, типа чтоби пакети отправляло норм, но пакета откритие инв нету, он для сервера "всегда" открит
 
Привет форум. Хотел написать короче FTHelper обычный, вроде бы даже получается. Столкнулся с такой проблемой что после того как предмет использовался(не хотбар), то делается двойной свап и предмет, допустим, трапка остаётся в хотбаре но если бить, то это фантомная трапка а в оригинале меч. Всё безуспешно. Надеюсь вы поможете мне.


FTHelper:
Expand Collapse Copy
package ru.okak.implement.features.modules.misc;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import ru.okak.api.event.EventHandler;
import ru.okak.api.feature.module.Module;
import ru.okak.api.feature.module.ModuleCategory;
import ru.okak.api.feature.module.setting.implement.BindSetting;
import ru.okak.api.feature.module.setting.implement.MultiSelectSetting;
import ru.okak.common.util.entity.PlayerInventoryComponent;
import ru.okak.common.util.entity.PlayerInventoryUtil;
import ru.okak.common.util.other.StopWatch;
import ru.okak.implement.events.keyboard.KeyEvent;
import ru.okak.implement.events.player.TickEvent;
import ru.okak.implement.features.draggables.Notifications;

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

@Getter
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class FTHelper extends Module {

    private static final String BIND_MODE = "Bind Mode";

    MultiSelectSetting mode = new MultiSelectSetting("Mode", "Select usage mode")
            .value("Bind Mode", "Close Inventory");

    BindSetting disorientationKey = new BindSetting("Disorientation", "Key for disorientation item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting trapKey = new BindSetting("Trap", "Key for trap item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting sugarKey = new BindSetting("Sugar", "Key for sugar item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting plastKey = new BindSetting("Plast", "Key for plast item")
            .visible(() -> mode.isSelected("Bind Mode"));
    BindSetting freezeKey = new BindSetting("Freeze", "Key for freeze item")
            .visible(() -> mode.isSelected("Bind Mode"));

    List<Ability> abilities = new ArrayList<>();
    StopWatch delayStopWatch = new StopWatch();
    private static final long DELAY_MS = 200L;
    private static final int TICKS_BETWEEN_ACTIONS = 2;

    public FTHelper() {
        super("FTHelper", "FTHelper", ModuleCategory.MISC);
        setup(mode, disorientationKey, trapKey, sugarKey, plastKey, freezeKey);

        abilities.add(new Ability("трапка", Items.NETHERITE_SCRAP, trapKey,
                "Trap item not found!", "Used trap item!", true));
        abilities.add(new Ability("дезориентация", Items.ENDER_EYE, disorientationKey,
                "Disorientation item not found!", "Used disorientation item!", true));
        abilities.add(new Ability("явная пыль", Items.SUGAR, sugarKey,
                "Sugar item not found!", "Used sugar item!", true));
        abilities.add(new Ability("пласт", Items.DRIED_KELP, plastKey,
                "Plast item not found!", "Used plast item!", true));
        abilities.add(new Ability("снежок заморозка", Items.SNOWBALL, freezeKey,
                "Freeze item not found!", "Used freeze item!", true));
    }

    @EventHandler
    public void onKey(KeyEvent e) {
        if (!isBindModeActive() || mc.player == null) return;

        for (Ability ability : abilities) {
            if (e.isKeyDown(ability.getBindSetting().getKey())) {
                ability.queueUse();
            }
        }
    }

    @EventHandler
    public void onTick(TickEvent e) {
        if (!isBindModeActive() || mc.player == null) return;

        if (!delayStopWatch.finished(DELAY_MS)) return;

        for (Ability ability : abilities) {
            if (ability.consumeQueuedUse()) {
                triggerAbility(ability);
                delayStopWatch.reset();
                return;
            }
        }

        for (Ability ability : abilities) {
            ItemStack stack = new ItemStack(ability.getItem());
            float cooldown = mc.player.getItemCooldownManager().getCooldownProgress(stack, 0);
            ability.updateCooldown(cooldown);
        }
    }

    private void triggerAbility(Ability ability) {
        ItemStack stack = new ItemStack(ability.getItem());
        if (mc.player.getItemCooldownManager().isCoolingDown(stack)) {
            sendNotification("Item on cooldown!", Formatting.RED);
            return;
        }

        Slot slot = findItemByName(ability.getInventoryKeyword());

        if (slot == null) {
            sendNotification(ability.getNotFoundMessage(), Formatting.RED);
            return;
        }

        sendNotification(ability.getSuccessMessage(), Formatting.GREEN);
        useItemWithSlot(slot, ability);
    }

    private void useItemWithSlot(Slot slot, Ability ability) {
        int originalSlot = mc.player.getInventory().selectedSlot;
        int slotId = slot.id;
        boolean isInHotbar = slotId < 9;

        ItemStack oldHandItem = mc.player.getMainHandStack().copy();

        PlayerInventoryComponent.addTask(() -> {
            PlayerInventoryComponent.disableMoveKeys();

            if (isInHotbar) {
                if (originalSlot != slotId) {
                    mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(slotId));
                    mc.player.getInventory().selectedSlot = slotId;

                    PlayerInventoryComponent.script.addTickStep(1, () -> {
                        useCurrentItem();

                        if (ability.isRestoreSelectedSlot()) {
                            PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                                mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(originalSlot));
                                mc.player.getInventory().selectedSlot = originalSlot;
                                PlayerInventoryComponent.enableMoveKeys();
                            });
                        } else {
                            PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, PlayerInventoryComponent::enableMoveKeys);
                        }
                    });
                } else {
                    useCurrentItem();
                    PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, PlayerInventoryComponent::enableMoveKeys);
                }
            } else {
                if (oldHandItem.isEmpty()) {
                    PlayerInventoryUtil.clickSlot(slot.id, originalSlot, SlotActionType.SWAP, false);

                    PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                        useCurrentItem();

                        PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                            PlayerInventoryUtil.clickSlot(originalSlot, slot.id, SlotActionType.SWAP, false);

                            if (ability.isRestoreSelectedSlot()) {
                                mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(originalSlot));
                                mc.player.getInventory().selectedSlot = originalSlot;
                            }

                            PlayerInventoryComponent.script.addTickStep(1, PlayerInventoryComponent::enableMoveKeys);
                        });
                    });
                } else {
                    PlayerInventoryUtil.clickSlot(slot.id, originalSlot, SlotActionType.SWAP, false);

                    PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                        useCurrentItem();

                        PlayerInventoryComponent.script.addTickStep(TICKS_BETWEEN_ACTIONS, () -> {
                            PlayerInventoryUtil.clickSlot(slot.id, originalSlot, SlotActionType.SWAP, false);

                            if (ability.isRestoreSelectedSlot()) {
                                mc.player.networkHandler.sendPacket(new UpdateSelectedSlotC2SPacket(originalSlot));
                                mc.player.getInventory().selectedSlot = originalSlot;
                            }

                            PlayerInventoryComponent.script.addTickStep(1, PlayerInventoryComponent::enableMoveKeys);
                        });
                    });
                }
            }
        });
    }

    private void useCurrentItem() {
        if (mc.player == null || mc.interactionManager == null) return;

        mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND);
        mc.player.swingHand(Hand.MAIN_HAND);
    }

    private Slot findItemByName(String name, boolean inHotBar) {
        if (mc.player == null) return null;

        int firstSlot = inHotBar ? 0 : 9;
        int lastSlot = inHotBar ? 9 : 36;

        for (int i = firstSlot; i < lastSlot; i++) {
            ItemStack itemStack = mc.player.getInventory().getStack(i);
            if (itemStack.isEmpty()) continue;

            String displayName = Formatting.strip(itemStack.getName().getString()).toLowerCase();
            if (displayName.contains(name.toLowerCase())) {
                return mc.player.currentScreenHandler.slots.get(i);
            }
        }
        return null;
    }

    private Slot findItemByName(String name) {
        Slot hotbarSlot = findItemByName(name, true);
        return hotbarSlot != null ? hotbarSlot : findItemByName(name, false);
    }

    private void sendNotification(String message, Formatting color) {
        Text notification = Text.literal("")
                .append(Text.literal("[FTHelper] ").formatted(Formatting.GRAY))
                .append(Text.literal(message).formatted(color));

        Notifications.getInstance().addList(notification.getString(), 2000);
    }

    private boolean isBindModeActive() {
        return mode.isSelected("Bind Mode");
    }

    @Override
    public void deactivate() {
        for (Ability ability : abilities) {
            ability.reset();
        }
        super.deactivate();
    }

    public List<HudAbilityState> getHudData(float partialTicks) {
        if (!isBindModeActive() || mc.player == null) {
            abilities.forEach(Ability::resetCooldown);
            return List.of();
        }

        List<HudAbilityState> states = new ArrayList<>();
        for (Ability ability : abilities) {
            int keyCode = ability.getBindSetting().getKey();
            if (keyCode == -1) {
                ability.resetCooldown();
                continue;
            }

            String keyName = resolveKeyName(keyCode);
            double cooldown = ability.getCooldownSeconds();

            states.add(new HudAbilityState(
                    new ItemStack(ability.getItem()),
                    keyName,
                    cooldown > 0 ? cooldown : null
            ));
        }

        return states;
    }

    private String resolveKeyName(int keyCode) {
        return "Key_" + keyCode;
    }

    @Getter
    public static class Ability {
        private final String inventoryKeyword;
        private final Item item;
        private final BindSetting bindSetting;
        private final String notFoundMessage;
        private final String successMessage;
        private final boolean restoreSelectedSlot;
        private boolean queuedUse;
        private final CooldownWatch cooldownWatch = new CooldownWatch();

        public Ability(String inventoryKeyword, Item item, BindSetting bindSetting,
                       String notFoundMessage, String successMessage, boolean restoreSelectedSlot) {
            this.inventoryKeyword = inventoryKeyword;
            this.item = item;
            this.bindSetting = bindSetting;
            this.notFoundMessage = notFoundMessage;
            this.successMessage = successMessage;
            this.restoreSelectedSlot = restoreSelectedSlot;
        }

        public void queueUse() {
            this.queuedUse = true;
        }

        public boolean consumeQueuedUse() {
            if (!queuedUse) return false;
            this.queuedUse = false;
            return true;
        }

        public void reset() {
            this.queuedUse = false;
            this.cooldownWatch.reset();
        }

        public void updateCooldown(float progress) {
            cooldownWatch.update(progress);
        }

        public double getCooldownSeconds() {
            return cooldownWatch.getRemainingSeconds();
        }

        public void resetCooldown() {
            cooldownWatch.reset();
        }
    }

    public static class CooldownWatch {
        private boolean active;
        private long startTime;
        private long totalDuration;

        public void update(float progress) {
            long now = System.currentTimeMillis();

            if (progress <= 0) {
                reset();
                return;
            }

            if (!active) {
                active = true;
                startTime = now;
                totalDuration = 0;
                return;
            }

            if (totalDuration == 0 && progress < 0.98f) {
                long elapsed = now - startTime;
                if (elapsed > 0 && progress > 0) {
                    totalDuration = (long)(elapsed / (1.0 - progress));
                }
            }
        }

        public double getRemainingSeconds() {
            if (!active || totalDuration == 0) return 0;

            long elapsed = System.currentTimeMillis() - startTime;
            long remaining = totalDuration - elapsed;

            if (remaining <= 0) {
                reset();
                return 0;
            }

            return remaining / 1000.0;
        }

        public void reset() {
            active = false;
            totalDuration = 0;
        }
    }

    public record HudAbilityState(ItemStack itemStack, String keyName, Double cooldownSeconds) {
        public boolean hasCooldown() {
            return cooldownSeconds != null && cooldownSeconds > 0;
        }
    }
}
слушай короче я тут вгляделся в твой код это конечно монументально ты прям замахнулся на создание экосистемы внутри майнкрафта но давай будем честны тут ситуация такая что пахнет глобальным метафизическим дисконнектом между твоим сознанием и логикой сетевого протокола моджанг во-первых сама концепция fthelper подразумевает под собой некую синергию между аппаратным обеспечением твоего пк и виртуальным пространством сервера а у тебя получается что они как бы живут в параллельных вселенных и даже не здороваются когда проходят мимо друг друга в потоке пакетов вот ты пишешь про двойной свап и фантомную трапку и это на самом деле очень глубокая философская проблема потому что трапка в данном контексте выступает как Шрёдингеровский объект она одновременно и существует в твоем хотбаре и не существует в реальности сервера и пока ты не кликнешь по ней левой кнопкой мыши ты находишься в суперпозиции бага и фичи твой PlayerInventoryComponent это конечно попытка обуздать хаос через абстракцию но кажется что абстракция в данном случае решила абстрагироваться от реальности и уйти в монастырь потому что когда ты вызываешь addTickStep ты фактически играешь в рулетку с тикрейтом сервера а сервер майнкрафта это такая штука которая не любит когда за нее решают когда и какой слот должен быть активным особенно когда ты пытаешься впихнуть невпихуемое в один пакетный интервал и вот этот твой меч который на самом деле трапка но выглядит как меч это же чистой воды киберпанк который мы заслужили это как если бы ты купил кока-колу а внутри оказался квас но на этикетке все равно написано кока-кола и ты такой пьешь и не понимаешь почему у тебя во рту вкус ржаного хлеба вместо бодрости капитализма тебе нужно осознать что UpdateSelectedSlotC2SPacket это не просто просьба к серверу это сакральный акт передачи воли игрока в цифровой эфир и если ты шлешь его вперемешку с SlotActionType.SWAP то сервер начинает сомневаться в твоей адекватности и на всякий случай блокирует обновление стейта чтобы ты сам себя не перехитрил в итоге мы имеем дело с классическим экзистенциальным кризисом клиентской части которая думает что она главная в то время как серверный поток исполнения смотрит на это все свысока и просто удаляет твои пакеты как спам в телеграме короче если ты не начнешь уважать тайминги сетевого взаимодействия и не перестанешь мучить бедный StopWatch своими микросекундными задержками то фантомные предметы станут твоими единственными друзьями в этом жестоком кубическом мире
 
нада зделать так чтоби сервер видел каждий шаг, откритие инв - перемещение предмета - закритие инв - переключение слота - юз - откритие инв - вертать предмет - закрить инв, типа чтоби пакети отправляло норм, но пакета откритие инв нету, он для сервера "всегда" открит
В коде как написать
 
Назад
Сверху Снизу