From f4fcb7bca91e63b23254917844a03cc2f3b4756f Mon Sep 17 00:00:00 2001 From: sectersion Date: Sun, 11 Oct 2026 16:07:35 +0100 Subject: [PATCH] Refactor: extract MacroDurability from MacroExecutor (upstream #4) Move 21 durability-use-next methods plus DurabilityUseNextSession and DurabilityCandidate verbatim into MacroDurability. Only outside references are isCurrentRunActive, callOnClientThread and waitOneTick (already package-visible), qualified as MacroExecutor.xxx / MacroDurability.xxx at the 2 staying call sites. All bodies verified identical against HEAD modulo that qualification. --- .../util/macro/MacroDurability.java | 318 ++++++++++++++++++ .../util/macro/MacroExecutor.java | 311 +---------------- 2 files changed, 322 insertions(+), 307 deletions(-) create mode 100644 src/main/java/autismclient/util/macro/MacroDurability.java diff --git a/src/main/java/autismclient/util/macro/MacroDurability.java b/src/main/java/autismclient/util/macro/MacroDurability.java new file mode 100644 index 0000000..c3be011 --- /dev/null +++ b/src/main/java/autismclient/util/macro/MacroDurability.java @@ -0,0 +1,318 @@ +package autismclient.util.macro; + +import autismclient.util.AutismInventoryHelper; +import net.minecraft.client.Minecraft; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.world.item.ItemStack; + +import java.util.Locale; +import java.util.concurrent.atomic.AtomicReference; + +final class MacroDurability { + private MacroDurability() { + } + public static void waitForDurabilityUseNext(Minecraft mc, WaitDurabilityAction action) throws InterruptedException { + AtomicReference sessionRef = new AtomicReference<>(); + long start = System.currentTimeMillis(); + while (MacroExecutor.isCurrentRunActive()) { + if (Boolean.TRUE.equals(MacroExecutor.callOnClientThread(mc, () -> tickDurabilityUseNext(mc, action, sessionRef), Boolean.FALSE))) { + return; + } + if (action.timeoutMs > 0 && System.currentTimeMillis() - start >= action.timeoutMs) return; + MacroExecutor.waitOneTick(); + } + } + + public static boolean durabilityObservationMatches(Minecraft mc, WaitDurabilityAction action) { + if (action == null || !action.useNext) return action != null && action.matches(mc); + DurabilityUseNextSession session = createDurabilityUseNextSession(mc, action); + if (session == null || session.fallbackOnly) return action.matches(mc); + if (session.firstSlotPending) { + ItemStack stack = stackForVisibleSlot(mc, session.activeSlot); + if (session.lockedRegistryId.isBlank() && canLockDurabilityInitialStack(action, session, stack)) { + session.lockedRegistryId = stackRegistryId(stack); + } + if (session.lockedRegistryId.isBlank()) return false; + return isSameDamageableItem(stack, session.lockedRegistryId) + && action.matchesStack(stack) + && findDurabilityReplacement(mc, action, session, session.activeSlot) == null; + } + if (session.lockedRegistryId.isBlank()) return action.matches(mc); + return findDurabilityReplacement(mc, action, session, -1) == null; + } + + private static boolean tickDurabilityUseNext(Minecraft mc, WaitDurabilityAction action, AtomicReference sessionRef) { + if (mc == null || mc.player == null || action == null) return false; + DurabilityUseNextSession session = sessionRef.get(); + if (session == null) { + session = createDurabilityUseNextSession(mc, action); + sessionRef.set(session); + } + if (session == null || session.fallbackOnly) return action.matches(mc); + + if (session.firstSlotPending) { + return tickDurabilityInitialSlot(mc, action, session); + } + + adoptCurrentSameItem(mc, session); + ItemStack active = stackForVisibleSlot(mc, session.activeSlot); + if (isSameDamageableItem(active, session.lockedRegistryId) && !action.matchesStack(active)) { + return false; + } + return switchToNextDurabilityItemOrComplete(mc, action, session, session.activeSlot); + } + + private static boolean tickDurabilityInitialSlot(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session) { + ItemStack stack = stackForVisibleSlot(mc, session.activeSlot); + if (session.lockedRegistryId.isBlank() && canLockDurabilityInitialStack(action, session, stack)) { + session.lockedRegistryId = stackRegistryId(stack); + } + + if (!session.lockedRegistryId.isBlank() && isSameDamageableItem(stack, session.lockedRegistryId)) { + session.observedInitialSlot = true; + if (!action.matchesStack(stack)) return false; + session.firstSlotPending = false; + return switchToNextDurabilityItemOrComplete(mc, action, session, session.activeSlot); + } + + if (session.observedInitialSlot) { + session.firstSlotPending = false; + return switchToNextDurabilityItemOrComplete(mc, action, session, session.activeSlot); + } + return false; + } + + private static boolean switchToNextDurabilityItemOrComplete(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session, int excludeSlot) { + DurabilityCandidate replacement = findDurabilityReplacement(mc, action, session, excludeSlot); + if (replacement == null) return true; + if (!switchToDurabilityReplacement(mc, session, replacement)) return false; + return false; + } + + private static DurabilityUseNextSession createDurabilityUseNextSession(Minecraft mc, WaitDurabilityAction action) { + if (mc == null || mc.player == null || action == null) return null; + DurabilityUseNextSession session = new DurabilityUseNextSession(); + session.preferredHotbarSlot = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); + ItemTarget target = action.targetMode == WaitDurabilityAction.TargetMode.ITEM + ? ItemTarget.fromLegacyEntry(action.itemName) + : new ItemTarget(); + session.initialTarget = target; + + switch (action.targetMode) { + case SLOT -> { + session.activeSlot = Math.max(0, Math.min(40, action.slot)); + session.firstSlotPending = true; + ItemStack stack = stackForVisibleSlot(mc, session.activeSlot); + if (canLockDurabilityInitialStack(action, session, stack)) { + session.lockedRegistryId = stackRegistryId(stack); + session.observedInitialSlot = true; + } + } + case HELD -> initializeHeldDurabilitySession(mc, action, session); + case ITEM -> initializeItemDurabilitySession(mc, action, session, target); + } + + if (session.lockedRegistryId == null) session.lockedRegistryId = ""; + if (session.lockedRegistryId.isBlank() && !session.firstSlotPending) { + session.fallbackOnly = true; + } + return session; + } + + private static void initializeHeldDurabilitySession(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session) { + int selected = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); + ItemStack main = mc.player.getInventory().getItem(selected); + if (main != null && !main.isEmpty() && main.isDamageableItem()) { + session.activeSlot = selected; + session.lockedRegistryId = stackRegistryId(main); + return; + } + ItemStack offhand = mc.player.getOffhandItem(); + if (offhand != null && !offhand.isEmpty() && offhand.isDamageableItem()) { + session.activeSlot = 40; + session.lockedRegistryId = stackRegistryId(offhand); + return; + } + session.fallbackOnly = true; + } + + private static void initializeItemDurabilitySession(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session, ItemTarget target) { + if (target != null && target.hasSlot()) { + session.activeSlot = Math.max(0, Math.min(40, target.slot)); + session.firstSlotPending = true; + String explicitId = target.hasRegistryId() ? canonicalItemRegistryId(target.registryId) : ""; + if (!explicitId.isBlank()) session.lockedRegistryId = explicitId; + ItemStack stack = stackForVisibleSlot(mc, session.activeSlot); + if (canLockDurabilityInitialStack(action, session, stack)) { + session.lockedRegistryId = stackRegistryId(stack); + session.observedInitialSlot = true; + } + return; + } + + if (target != null && target.hasRegistryId()) { + session.lockedRegistryId = canonicalItemRegistryId(target.registryId); + session.activeSlot = findCurrentUsableDurabilitySlot(mc, action, session.lockedRegistryId, target); + return; + } + + if (target == null || !target.hasIdentity()) { + initializeHeldDurabilitySession(mc, action, session); + return; + } + + int slot = findInitialDurabilitySlot(mc, action, "", target); + if (slot >= 0) { + session.activeSlot = slot; + session.lockedRegistryId = stackRegistryId(stackForVisibleSlot(mc, slot)); + } else { + session.fallbackOnly = true; + } + } + + private static int findCurrentUsableDurabilitySlot(Minecraft mc, WaitDurabilityAction action, String lockedRegistryId, ItemTarget target) { + if (mc == null || mc.player == null) return -1; + int selected = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); + if (initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, selected)) return selected; + return initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, 40) ? 40 : -1; + } + + private static boolean canLockDurabilityInitialStack(WaitDurabilityAction action, DurabilityUseNextSession session, ItemStack stack) { + if (stack == null || stack.isEmpty() || !stack.isDamageableItem()) return false; + if (action != null && action.targetMode == WaitDurabilityAction.TargetMode.ITEM + && session != null && session.initialTarget != null && session.initialTarget.hasIdentity()) { + return session.initialTarget.score(stack, session.activeSlot) >= 0; + } + return true; + } + + private static int findInitialDurabilitySlot(Minecraft mc, WaitDurabilityAction action, String lockedRegistryId, ItemTarget target) { + if (mc == null || mc.player == null) return -1; + int selected = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); + if (initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, selected)) return selected; + for (int slot = 0; slot < 9; slot++) { + if (slot != selected && initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, slot)) return slot; + } + for (int slot = 9; slot < Math.min(36, mc.player.getInventory().getContainerSize()); slot++) { + if (initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, slot)) return slot; + } + return initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, 40) ? 40 : -1; + } + + private static boolean initialDurabilitySlotMatches(Minecraft mc, WaitDurabilityAction action, String lockedRegistryId, ItemTarget target, int slot) { + ItemStack stack = stackForVisibleSlot(mc, slot); + if (stack == null || stack.isEmpty() || !stack.isDamageableItem()) return false; + if (lockedRegistryId != null && !lockedRegistryId.isBlank() && !isSameDamageableItem(stack, lockedRegistryId)) return false; + return target == null || !target.hasIdentity() || target.score(stack, slot) >= 0; + } + + private static void adoptCurrentSameItem(Minecraft mc, DurabilityUseNextSession session) { + if (mc == null || mc.player == null || session == null || session.lockedRegistryId.isBlank()) return; + int selected = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); + if (isSameDamageableItem(mc.player.getInventory().getItem(selected), session.lockedRegistryId)) { + session.activeSlot = selected; + session.preferredHotbarSlot = selected; + return; + } + if (isSameDamageableItem(mc.player.getOffhandItem(), session.lockedRegistryId)) { + ItemStack active = stackForVisibleSlot(mc, session.activeSlot); + if (!isSameDamageableItem(active, session.lockedRegistryId)) session.activeSlot = 40; + } + } + + private static DurabilityCandidate findDurabilityReplacement(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session, int excludeSlot) { + if (mc == null || mc.player == null || action == null || session == null || session.lockedRegistryId.isBlank()) return null; + DurabilityCandidate hotbar = null; + DurabilityCandidate inventory = null; + DurabilityCandidate offhand = null; + for (int slot = 0; slot < 9; slot++) { + DurabilityCandidate candidate = durabilityCandidate(mc, action, session.lockedRegistryId, slot, excludeSlot); + if (candidate != null && (hotbar == null || candidate.score() > hotbar.score())) hotbar = candidate; + } + int size = Math.min(36, mc.player.getInventory().getContainerSize()); + for (int slot = 9; slot < size; slot++) { + DurabilityCandidate candidate = durabilityCandidate(mc, action, session.lockedRegistryId, slot, excludeSlot); + if (candidate != null && (inventory == null || candidate.score() > inventory.score())) inventory = candidate; + } + offhand = durabilityCandidate(mc, action, session.lockedRegistryId, 40, excludeSlot); + if (hotbar != null) return hotbar; + if (inventory != null) return inventory; + return offhand; + } + + private static DurabilityCandidate durabilityCandidate(Minecraft mc, WaitDurabilityAction action, String registryId, int slot, int excludeSlot) { + if (slot == excludeSlot) return null; + ItemStack stack = stackForVisibleSlot(mc, slot); + if (!isSameDamageableItem(stack, registryId)) return null; + if (action.matchesStack(stack)) return null; + return new DurabilityCandidate(slot, remainingDurability(stack)); + } + + private static boolean switchToDurabilityReplacement(Minecraft mc, DurabilityUseNextSession session, DurabilityCandidate replacement) { + if (mc == null || mc.player == null || replacement == null) return false; + int slot = replacement.slot(); + if (slot >= 0 && slot < 9) { + AutismInventoryHelper.selectHotbarSlot(mc, slot); + session.activeSlot = slot; + session.preferredHotbarSlot = slot; + return true; + } + if (slot >= 9 && slot < 36) { + int target = Math.max(0, Math.min(8, session.preferredHotbarSlot)); + if (!AutismInventoryHelper.swapInventorySlots(mc, slot, target)) return false; + AutismInventoryHelper.selectHotbarSlot(mc, target); + session.activeSlot = target; + session.preferredHotbarSlot = target; + return true; + } + if (slot == 40) { + session.activeSlot = 40; + return true; + } + return false; + } + + private static ItemStack stackForVisibleSlot(Minecraft mc, int visibleSlot) { + if (mc == null || mc.player == null) return ItemStack.EMPTY; + if (visibleSlot == 40) return mc.player.getOffhandItem(); + if (visibleSlot < 0 || visibleSlot >= mc.player.getInventory().getContainerSize()) return ItemStack.EMPTY; + return mc.player.getInventory().getItem(visibleSlot); + } + + private static boolean isSameDamageableItem(ItemStack stack, String registryId) { + return stack != null && !stack.isEmpty() && stack.isDamageableItem() && stackRegistryId(stack).equals(registryId); + } + + private static int remainingDurability(ItemStack stack) { + return stack == null || !stack.isDamageableItem() ? 0 : Math.max(0, stack.getMaxDamage() - stack.getDamageValue()); + } + + private static String stackRegistryId(ItemStack stack) { + if (stack == null || stack.isEmpty()) return ""; + Identifier id = BuiltInRegistries.ITEM.getKey(stack.getItem()); + return id == null ? "" : id.toString(); + } + + private static String canonicalItemRegistryId(String raw) { + if (raw == null || raw.isBlank()) return ""; + String trimmed = raw.trim().toLowerCase(Locale.ROOT); + Identifier parsed = Identifier.tryParse(trimmed.contains(":") ? trimmed : "minecraft:" + trimmed); + if (parsed == null) return ""; + return BuiltInRegistries.ITEM.getOptional(parsed) + .map(item -> BuiltInRegistries.ITEM.getKey(item).toString()) + .orElse(""); + } + + private static final class DurabilityUseNextSession { + private String lockedRegistryId = ""; + private int activeSlot = -1; + private int preferredHotbarSlot = 0; + private boolean firstSlotPending = false; + private boolean observedInitialSlot = false; + private boolean fallbackOnly = false; + private ItemTarget initialTarget = new ItemTarget(); + } + + private record DurabilityCandidate(int slot, int score) {} +} diff --git a/src/main/java/autismclient/util/macro/MacroExecutor.java b/src/main/java/autismclient/util/macro/MacroExecutor.java index 63da1f4..b4807ce 100644 --- a/src/main/java/autismclient/util/macro/MacroExecutor.java +++ b/src/main/java/autismclient/util/macro/MacroExecutor.java @@ -1187,7 +1187,7 @@ public class MacroExecutor { } else if (action instanceof WaitDurabilityAction wda) { setCurrentStatus(wda.getDisplayName()); - if (wda.useNext) waitForDurabilityUseNext(mc, wda); + if (wda.useNext) MacroDurability.waitForDurabilityUseNext(mc, wda); else if (!waitForPrearmed(wda, wda.timeoutMs)) waitForPredicate(() -> wda.matches(mc), wda.timeoutMs); } else if (action instanceof WaitFreeSlotsAction wfsa) { @@ -2113,309 +2113,6 @@ public class MacroExecutor { }; } - private static void waitForDurabilityUseNext(Minecraft mc, WaitDurabilityAction action) throws InterruptedException { - AtomicReference sessionRef = new AtomicReference<>(); - long start = System.currentTimeMillis(); - while (isCurrentRunActive()) { - if (Boolean.TRUE.equals(callOnClientThread(mc, () -> tickDurabilityUseNext(mc, action, sessionRef), Boolean.FALSE))) { - return; - } - if (action.timeoutMs > 0 && System.currentTimeMillis() - start >= action.timeoutMs) return; - waitOneTick(); - } - } - - private static boolean durabilityObservationMatches(Minecraft mc, WaitDurabilityAction action) { - if (action == null || !action.useNext) return action != null && action.matches(mc); - DurabilityUseNextSession session = createDurabilityUseNextSession(mc, action); - if (session == null || session.fallbackOnly) return action.matches(mc); - if (session.firstSlotPending) { - ItemStack stack = stackForVisibleSlot(mc, session.activeSlot); - if (session.lockedRegistryId.isBlank() && canLockDurabilityInitialStack(action, session, stack)) { - session.lockedRegistryId = stackRegistryId(stack); - } - if (session.lockedRegistryId.isBlank()) return false; - return isSameDamageableItem(stack, session.lockedRegistryId) - && action.matchesStack(stack) - && findDurabilityReplacement(mc, action, session, session.activeSlot) == null; - } - if (session.lockedRegistryId.isBlank()) return action.matches(mc); - return findDurabilityReplacement(mc, action, session, -1) == null; - } - - private static boolean tickDurabilityUseNext(Minecraft mc, WaitDurabilityAction action, AtomicReference sessionRef) { - if (mc == null || mc.player == null || action == null) return false; - DurabilityUseNextSession session = sessionRef.get(); - if (session == null) { - session = createDurabilityUseNextSession(mc, action); - sessionRef.set(session); - } - if (session == null || session.fallbackOnly) return action.matches(mc); - - if (session.firstSlotPending) { - return tickDurabilityInitialSlot(mc, action, session); - } - - adoptCurrentSameItem(mc, session); - ItemStack active = stackForVisibleSlot(mc, session.activeSlot); - if (isSameDamageableItem(active, session.lockedRegistryId) && !action.matchesStack(active)) { - return false; - } - return switchToNextDurabilityItemOrComplete(mc, action, session, session.activeSlot); - } - - private static boolean tickDurabilityInitialSlot(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session) { - ItemStack stack = stackForVisibleSlot(mc, session.activeSlot); - if (session.lockedRegistryId.isBlank() && canLockDurabilityInitialStack(action, session, stack)) { - session.lockedRegistryId = stackRegistryId(stack); - } - - if (!session.lockedRegistryId.isBlank() && isSameDamageableItem(stack, session.lockedRegistryId)) { - session.observedInitialSlot = true; - if (!action.matchesStack(stack)) return false; - session.firstSlotPending = false; - return switchToNextDurabilityItemOrComplete(mc, action, session, session.activeSlot); - } - - if (session.observedInitialSlot) { - session.firstSlotPending = false; - return switchToNextDurabilityItemOrComplete(mc, action, session, session.activeSlot); - } - return false; - } - - private static boolean switchToNextDurabilityItemOrComplete(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session, int excludeSlot) { - DurabilityCandidate replacement = findDurabilityReplacement(mc, action, session, excludeSlot); - if (replacement == null) return true; - if (!switchToDurabilityReplacement(mc, session, replacement)) return false; - return false; - } - - private static DurabilityUseNextSession createDurabilityUseNextSession(Minecraft mc, WaitDurabilityAction action) { - if (mc == null || mc.player == null || action == null) return null; - DurabilityUseNextSession session = new DurabilityUseNextSession(); - session.preferredHotbarSlot = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); - ItemTarget target = action.targetMode == WaitDurabilityAction.TargetMode.ITEM - ? ItemTarget.fromLegacyEntry(action.itemName) - : new ItemTarget(); - session.initialTarget = target; - - switch (action.targetMode) { - case SLOT -> { - session.activeSlot = Math.max(0, Math.min(40, action.slot)); - session.firstSlotPending = true; - ItemStack stack = stackForVisibleSlot(mc, session.activeSlot); - if (canLockDurabilityInitialStack(action, session, stack)) { - session.lockedRegistryId = stackRegistryId(stack); - session.observedInitialSlot = true; - } - } - case HELD -> initializeHeldDurabilitySession(mc, action, session); - case ITEM -> initializeItemDurabilitySession(mc, action, session, target); - } - - if (session.lockedRegistryId == null) session.lockedRegistryId = ""; - if (session.lockedRegistryId.isBlank() && !session.firstSlotPending) { - session.fallbackOnly = true; - } - return session; - } - - private static void initializeHeldDurabilitySession(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session) { - int selected = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); - ItemStack main = mc.player.getInventory().getItem(selected); - if (main != null && !main.isEmpty() && main.isDamageableItem()) { - session.activeSlot = selected; - session.lockedRegistryId = stackRegistryId(main); - return; - } - ItemStack offhand = mc.player.getOffhandItem(); - if (offhand != null && !offhand.isEmpty() && offhand.isDamageableItem()) { - session.activeSlot = 40; - session.lockedRegistryId = stackRegistryId(offhand); - return; - } - session.fallbackOnly = true; - } - - private static void initializeItemDurabilitySession(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session, ItemTarget target) { - if (target != null && target.hasSlot()) { - session.activeSlot = Math.max(0, Math.min(40, target.slot)); - session.firstSlotPending = true; - String explicitId = target.hasRegistryId() ? canonicalItemRegistryId(target.registryId) : ""; - if (!explicitId.isBlank()) session.lockedRegistryId = explicitId; - ItemStack stack = stackForVisibleSlot(mc, session.activeSlot); - if (canLockDurabilityInitialStack(action, session, stack)) { - session.lockedRegistryId = stackRegistryId(stack); - session.observedInitialSlot = true; - } - return; - } - - if (target != null && target.hasRegistryId()) { - session.lockedRegistryId = canonicalItemRegistryId(target.registryId); - session.activeSlot = findCurrentUsableDurabilitySlot(mc, action, session.lockedRegistryId, target); - return; - } - - if (target == null || !target.hasIdentity()) { - initializeHeldDurabilitySession(mc, action, session); - return; - } - - int slot = findInitialDurabilitySlot(mc, action, "", target); - if (slot >= 0) { - session.activeSlot = slot; - session.lockedRegistryId = stackRegistryId(stackForVisibleSlot(mc, slot)); - } else { - session.fallbackOnly = true; - } - } - - private static int findCurrentUsableDurabilitySlot(Minecraft mc, WaitDurabilityAction action, String lockedRegistryId, ItemTarget target) { - if (mc == null || mc.player == null) return -1; - int selected = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); - if (initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, selected)) return selected; - return initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, 40) ? 40 : -1; - } - - private static boolean canLockDurabilityInitialStack(WaitDurabilityAction action, DurabilityUseNextSession session, ItemStack stack) { - if (stack == null || stack.isEmpty() || !stack.isDamageableItem()) return false; - if (action != null && action.targetMode == WaitDurabilityAction.TargetMode.ITEM - && session != null && session.initialTarget != null && session.initialTarget.hasIdentity()) { - return session.initialTarget.score(stack, session.activeSlot) >= 0; - } - return true; - } - - private static int findInitialDurabilitySlot(Minecraft mc, WaitDurabilityAction action, String lockedRegistryId, ItemTarget target) { - if (mc == null || mc.player == null) return -1; - int selected = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); - if (initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, selected)) return selected; - for (int slot = 0; slot < 9; slot++) { - if (slot != selected && initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, slot)) return slot; - } - for (int slot = 9; slot < Math.min(36, mc.player.getInventory().getContainerSize()); slot++) { - if (initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, slot)) return slot; - } - return initialDurabilitySlotMatches(mc, action, lockedRegistryId, target, 40) ? 40 : -1; - } - - private static boolean initialDurabilitySlotMatches(Minecraft mc, WaitDurabilityAction action, String lockedRegistryId, ItemTarget target, int slot) { - ItemStack stack = stackForVisibleSlot(mc, slot); - if (stack == null || stack.isEmpty() || !stack.isDamageableItem()) return false; - if (lockedRegistryId != null && !lockedRegistryId.isBlank() && !isSameDamageableItem(stack, lockedRegistryId)) return false; - return target == null || !target.hasIdentity() || target.score(stack, slot) >= 0; - } - - private static void adoptCurrentSameItem(Minecraft mc, DurabilityUseNextSession session) { - if (mc == null || mc.player == null || session == null || session.lockedRegistryId.isBlank()) return; - int selected = Math.max(0, Math.min(8, mc.player.getInventory().getSelectedSlot())); - if (isSameDamageableItem(mc.player.getInventory().getItem(selected), session.lockedRegistryId)) { - session.activeSlot = selected; - session.preferredHotbarSlot = selected; - return; - } - if (isSameDamageableItem(mc.player.getOffhandItem(), session.lockedRegistryId)) { - ItemStack active = stackForVisibleSlot(mc, session.activeSlot); - if (!isSameDamageableItem(active, session.lockedRegistryId)) session.activeSlot = 40; - } - } - - private static DurabilityCandidate findDurabilityReplacement(Minecraft mc, WaitDurabilityAction action, DurabilityUseNextSession session, int excludeSlot) { - if (mc == null || mc.player == null || action == null || session == null || session.lockedRegistryId.isBlank()) return null; - DurabilityCandidate hotbar = null; - DurabilityCandidate inventory = null; - DurabilityCandidate offhand = null; - for (int slot = 0; slot < 9; slot++) { - DurabilityCandidate candidate = durabilityCandidate(mc, action, session.lockedRegistryId, slot, excludeSlot); - if (candidate != null && (hotbar == null || candidate.score() > hotbar.score())) hotbar = candidate; - } - int size = Math.min(36, mc.player.getInventory().getContainerSize()); - for (int slot = 9; slot < size; slot++) { - DurabilityCandidate candidate = durabilityCandidate(mc, action, session.lockedRegistryId, slot, excludeSlot); - if (candidate != null && (inventory == null || candidate.score() > inventory.score())) inventory = candidate; - } - offhand = durabilityCandidate(mc, action, session.lockedRegistryId, 40, excludeSlot); - if (hotbar != null) return hotbar; - if (inventory != null) return inventory; - return offhand; - } - - private static DurabilityCandidate durabilityCandidate(Minecraft mc, WaitDurabilityAction action, String registryId, int slot, int excludeSlot) { - if (slot == excludeSlot) return null; - ItemStack stack = stackForVisibleSlot(mc, slot); - if (!isSameDamageableItem(stack, registryId)) return null; - if (action.matchesStack(stack)) return null; - return new DurabilityCandidate(slot, remainingDurability(stack)); - } - - private static boolean switchToDurabilityReplacement(Minecraft mc, DurabilityUseNextSession session, DurabilityCandidate replacement) { - if (mc == null || mc.player == null || replacement == null) return false; - int slot = replacement.slot(); - if (slot >= 0 && slot < 9) { - AutismInventoryHelper.selectHotbarSlot(mc, slot); - session.activeSlot = slot; - session.preferredHotbarSlot = slot; - return true; - } - if (slot >= 9 && slot < 36) { - int target = Math.max(0, Math.min(8, session.preferredHotbarSlot)); - if (!AutismInventoryHelper.swapInventorySlots(mc, slot, target)) return false; - AutismInventoryHelper.selectHotbarSlot(mc, target); - session.activeSlot = target; - session.preferredHotbarSlot = target; - return true; - } - if (slot == 40) { - session.activeSlot = 40; - return true; - } - return false; - } - - private static ItemStack stackForVisibleSlot(Minecraft mc, int visibleSlot) { - if (mc == null || mc.player == null) return ItemStack.EMPTY; - if (visibleSlot == 40) return mc.player.getOffhandItem(); - if (visibleSlot < 0 || visibleSlot >= mc.player.getInventory().getContainerSize()) return ItemStack.EMPTY; - return mc.player.getInventory().getItem(visibleSlot); - } - - private static boolean isSameDamageableItem(ItemStack stack, String registryId) { - return stack != null && !stack.isEmpty() && stack.isDamageableItem() && stackRegistryId(stack).equals(registryId); - } - - private static int remainingDurability(ItemStack stack) { - return stack == null || !stack.isDamageableItem() ? 0 : Math.max(0, stack.getMaxDamage() - stack.getDamageValue()); - } - - private static String stackRegistryId(ItemStack stack) { - if (stack == null || stack.isEmpty()) return ""; - Identifier id = BuiltInRegistries.ITEM.getKey(stack.getItem()); - return id == null ? "" : id.toString(); - } - - private static String canonicalItemRegistryId(String raw) { - if (raw == null || raw.isBlank()) return ""; - String trimmed = raw.trim().toLowerCase(Locale.ROOT); - Identifier parsed = Identifier.tryParse(trimmed.contains(":") ? trimmed : "minecraft:" + trimmed); - if (parsed == null) return ""; - return BuiltInRegistries.ITEM.getOptional(parsed) - .map(item -> BuiltInRegistries.ITEM.getKey(item).toString()) - .orElse(""); - } - - private static final class DurabilityUseNextSession { - private String lockedRegistryId = ""; - private int activeSlot = -1; - private int preferredHotbarSlot = 0; - private boolean firstSlotPending = false; - private boolean observedInitialSlot = false; - private boolean fallbackOnly = false; - private ItemTarget initialTarget = new ItemTarget(); - } - - private record DurabilityCandidate(int slot, int score) {} private static CompletableFuture predicateFuture(java.util.function.BooleanSupplier predicate, int timeoutMs) { CompletableFuture future = new CompletableFuture<>(); @@ -2523,7 +2220,7 @@ public class MacroExecutor { } if (action instanceof WaitDurabilityAction durability) { if (durability.useNext) return null; - return predicatePrearm(action, () -> durabilityObservationMatches(mc, durability), durability.timeoutMs); + return predicatePrearm(action, () -> MacroDurability.durabilityObservationMatches(mc, durability), durability.timeoutMs); } if (action instanceof WaitFreeSlotsAction freeSlots) { return predicatePrearm(action, () -> freeSlots.matches(mc), freeSlots.timeoutMs); @@ -3720,7 +3417,7 @@ public class MacroExecutor { return MacroConditionRegistry.waitForSlotChange(slot); } if (trigger instanceof WaitDurabilityAction durability) { - return predicateFuture(() -> durabilityObservationMatches(Minecraft.getInstance(), durability), durability.timeoutMs); + return predicateFuture(() -> MacroDurability.durabilityObservationMatches(Minecraft.getInstance(), durability), durability.timeoutMs); } if (trigger instanceof WaitFreeSlotsAction freeSlots) { return predicateFuture(() -> freeSlots.matches(Minecraft.getInstance()), freeSlots.timeoutMs); @@ -3866,7 +3563,7 @@ public class MacroExecutor { waitForPredicate(() -> wipa.matches(mc), wipa.timeoutMs); } else if (action instanceof WaitDurabilityAction wda) { setCurrentStatus(wda.getDisplayName()); - if (wda.useNext) waitForDurabilityUseNext(mc, wda); + if (wda.useNext) MacroDurability.waitForDurabilityUseNext(mc, wda); else waitForPredicate(() -> wda.matches(mc), wda.timeoutMs); } else if (action instanceof WaitFreeSlotsAction wfsa) { setCurrentStatus(wfsa.getDisplayName());