From cda0b3687349307b8b3dfafed738c9b54c776f63 Mon Sep 17 00:00:00 2001 From: Apertyotis <1947517504@qq.com> Date: Wed, 10 Jun 2026 16:03:18 +0800 Subject: [PATCH 1/4] Optimize funnel item extraction, especially in EXACTLY mode --- .../create/foundation/item/ItemHelper.java | 83 +++++++------------ .../utility/VisitedItemStackTracker.java | 54 ++++++++++++ 2 files changed, 82 insertions(+), 55 deletions(-) create mode 100644 src/main/java/com/simibubi/create/foundation/utility/VisitedItemStackTracker.java diff --git a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java index 569193efb9..88e81524f2 100644 --- a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java +++ b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java @@ -5,6 +5,8 @@ import java.util.function.Function; import java.util.function.Predicate; +import com.simibubi.create.foundation.utility.VisitedItemStackTracker; + import org.jetbrains.annotations.Nullable; import org.apache.commons.lang3.mutable.MutableInt; @@ -182,70 +184,41 @@ public static ItemStack extract(IItemHandler inv, Predicate test, int public static ItemStack extract(IItemHandler inv, Predicate test, ExtractionCountMode mode, int amount, boolean simulate) { - ItemStack extracting = ItemStack.EMPTY; - boolean amountRequired = mode == ExtractionCountMode.EXACTLY; - boolean checkHasEnoughItems = amountRequired; - boolean hasEnoughItems = !checkHasEnoughItems; - boolean potentialOtherMatch = false; - int maxExtractionCount = amount; - - Extraction: - do { - extracting = ItemStack.EMPTY; - - for (int slot = 0; slot < inv.getSlots(); slot++) { - ItemStack slotStack = inv.getStackInSlot(slot); - if (slotStack.isEmpty()) + if (mode == ItemHelper.ExtractionCountMode.EXACTLY) { + VisitedItemStackTracker tracker = new VisitedItemStackTracker(); + for (int i = 0; i < inv.getSlots(); i++) { + ItemStack stackIn = inv.getStackInSlot(i); + if (stackIn.isEmpty() || !test.test(stackIn)) continue; - int amountToExtractFromThisSlot = - Math.min(maxExtractionCount - extracting.getCount(), slotStack.getMaxStackSize()); - ItemStack stack = inv.extractItem(slot, amountToExtractFromThisSlot, true); - if (stack.isEmpty()) - continue; - if (!test.test(stack)) - continue; - if (!extracting.isEmpty() && !canItemStackAmountsStack(stack, extracting)) { - potentialOtherMatch = true; + ItemStack extracted = inv.extractItem(i, amount, true); + if (extracted.isEmpty()) continue; - } - if (extracting.isEmpty()) - extracting = stack.copy(); - else - extracting.grow(stack.getCount()); - - if (!simulate && hasEnoughItems) - inv.extractItem(slot, stack.getCount(), false); - - if (extracting.getCount() >= maxExtractionCount) { - if (checkHasEnoughItems) { - hasEnoughItems = true; - checkHasEnoughItems = false; - continue Extraction; - } else { - break Extraction; + VisitedItemStackTracker.SlotAmountRecord slotRecord = tracker.update(stackIn, i); + if (slotRecord.totalAmount >= amount) { + ItemStack result = extracted.copyWithCount(amount); + if (!simulate) { + for (int slot: slotRecord.slots) { + amount -= inv.extractItem(slot, amount, false).getCount(); + } } + return result; } } + } else { + for (int i = 0; i < inv.getSlots(); i++) { + ItemStack stackIn = inv.getStackInSlot(i); + if (stackIn.isEmpty() || !test.test(stackIn)) + continue; - if (!extracting.isEmpty() && !hasEnoughItems && potentialOtherMatch) { - ItemStack blackListed = extracting.copy(); - test = test.and(i -> !ItemStack.isSameItemSameComponents(i, blackListed)); - continue; + ItemStack extracted = inv.extractItem(i, amount, simulate); + if (!extracted.isEmpty()) { + return extracted.copy(); + } } - - if (checkHasEnoughItems) - checkHasEnoughItems = false; - else - break Extraction; - - } while (true); - - if (amountRequired && extracting.getCount() < amount) - return ItemStack.EMPTY; - - return extracting; + } + return ItemStack.EMPTY; } public static ItemStack extract(IItemHandler inv, Predicate test, diff --git a/src/main/java/com/simibubi/create/foundation/utility/VisitedItemStackTracker.java b/src/main/java/com/simibubi/create/foundation/utility/VisitedItemStackTracker.java new file mode 100644 index 0000000000..3faa335e94 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/utility/VisitedItemStackTracker.java @@ -0,0 +1,54 @@ +package com.simibubi.create.foundation.utility; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import net.minecraft.world.item.ItemStack; + +import java.util.HashMap; +import java.util.Map; + +public class VisitedItemStackTracker { + Map itemStackMap = new HashMap<>(); + + public SlotAmountRecord update(ItemStack itemStack, int slot) { + SlotAmountRecord record = itemStackMap.computeIfAbsent(new AbstractItemStack(itemStack), k -> new SlotAmountRecord()); + record.add(slot, itemStack.getCount()); + return record; + } + + public static class AbstractItemStack { + private final ItemStack stack; + private boolean initialized = false; + private int hashCode; + + AbstractItemStack(ItemStack stack) { + this.stack = stack; + } + + @Override + public int hashCode() { + if (!initialized) { + initialized = true; + hashCode = stack.getItem().hashCode(); + hashCode = hashCode * 31 + stack.getComponents().hashCode(); + } + return hashCode; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof AbstractItemStack otherStack)) + return false; + return ItemStack.isSameItemSameComponents(stack, otherStack.stack); + } + } + + public static class SlotAmountRecord { + public final IntArrayList slots = new IntArrayList(); + public int totalAmount = 0; + + void add(int slot, int amount) { + slots.add(slot); + totalAmount += amount; + } + } +} From 550b9d9c192abd5e5836da027c88ba7f8256738b Mon Sep 17 00:00:00 2001 From: Apertyotis <1947517504@qq.com> Date: Wed, 10 Jun 2026 17:39:04 +0800 Subject: [PATCH 2/4] consistent behaviour --- .../create/foundation/item/ItemHelper.java | 42 +++++++++++++++---- .../utility/VisitedItemStackTracker.java | 2 +- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java index 88e81524f2..e1ae166ef8 100644 --- a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java +++ b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java @@ -7,6 +7,8 @@ import com.simibubi.create.foundation.utility.VisitedItemStackTracker; +import com.simibubi.create.foundation.utility.VisitedItemStackTracker.SlotAmountRecord; + import org.jetbrains.annotations.Nullable; import org.apache.commons.lang3.mutable.MutableInt; @@ -188,34 +190,58 @@ public static ItemStack extract(IItemHandler inv, Predicate test, Ext VisitedItemStackTracker tracker = new VisitedItemStackTracker(); for (int i = 0; i < inv.getSlots(); i++) { ItemStack stackIn = inv.getStackInSlot(i); - if (stackIn.isEmpty() || !test.test(stackIn)) + if (stackIn.isEmpty() || stackIn.getMaxStackSize() < amount || !test.test(stackIn)) continue; - ItemStack extracted = inv.extractItem(i, amount, true); + ItemStack extracted = inv.extractItem(i, stackIn.getCount(), true); if (extracted.isEmpty()) continue; - VisitedItemStackTracker.SlotAmountRecord slotRecord = tracker.update(stackIn, i); + VisitedItemStackTracker.SlotAmountRecord slotRecord = tracker.update(extracted.copy(), i); if (slotRecord.totalAmount >= amount) { ItemStack result = extracted.copyWithCount(amount); if (!simulate) { for (int slot: slotRecord.slots) { - amount -= inv.extractItem(slot, amount, false).getCount(); + int extractAmount = Math.min(inv.getStackInSlot(slot).getCount(), amount); + amount -= inv.extractItem(slot, extractAmount, false).getCount(); } } return result; } } } else { + VisitedItemStackTracker.SlotAmountRecord slotRecord = new SlotAmountRecord(); + ItemStack result = ItemStack.EMPTY; + int maxExtractAmount = amount; for (int i = 0; i < inv.getSlots(); i++) { ItemStack stackIn = inv.getStackInSlot(i); - if (stackIn.isEmpty() || !test.test(stackIn)) + if (stackIn.isEmpty() || (!result.isEmpty() && !ItemStack.isSameItemSameComponents(result, stackIn)) || + !test.test(stackIn)) + continue; + + ItemStack extracted = inv.extractItem(i, stackIn.getCount(), true); + if (extracted.isEmpty()) continue; - ItemStack extracted = inv.extractItem(i, amount, simulate); - if (!extracted.isEmpty()) { - return extracted.copy(); + if (result.isEmpty()) { + result = extracted.copy(); + maxExtractAmount = Math.min(amount, extracted.getMaxStackSize()); + } + + slotRecord.add(i, extracted.getCount()); + if (slotRecord.totalAmount >= maxExtractAmount) + break; + } + if (!result.isEmpty()) { + amount = Math.min(slotRecord.totalAmount, result.getMaxStackSize()); + result.setCount(amount); + if (!simulate) { + for (int slot: slotRecord.slots) { + int extractAmount = Math.min(inv.getStackInSlot(slot).getCount(), amount); + amount -= inv.extractItem(slot, extractAmount, false).getCount(); + } } + return result; } } return ItemStack.EMPTY; diff --git a/src/main/java/com/simibubi/create/foundation/utility/VisitedItemStackTracker.java b/src/main/java/com/simibubi/create/foundation/utility/VisitedItemStackTracker.java index 3faa335e94..dabf5b290b 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/VisitedItemStackTracker.java +++ b/src/main/java/com/simibubi/create/foundation/utility/VisitedItemStackTracker.java @@ -46,7 +46,7 @@ public static class SlotAmountRecord { public final IntArrayList slots = new IntArrayList(); public int totalAmount = 0; - void add(int slot, int amount) { + public void add(int slot, int amount) { slots.add(slot); totalAmount += amount; } From e17381669716dba5413edacc66e023991f3e01ec Mon Sep 17 00:00:00 2001 From: Apertyotis <1947517504@qq.com> Date: Wed, 10 Jun 2026 18:05:57 +0800 Subject: [PATCH 3/4] consistent behaviour --- .../java/com/simibubi/create/foundation/item/ItemHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java index e1ae166ef8..1acb8c2814 100644 --- a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java +++ b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java @@ -233,7 +233,7 @@ public static ItemStack extract(IItemHandler inv, Predicate test, Ext break; } if (!result.isEmpty()) { - amount = Math.min(slotRecord.totalAmount, result.getMaxStackSize()); + amount = Math.min(slotRecord.totalAmount, maxExtractAmount); result.setCount(amount); if (!simulate) { for (int slot: slotRecord.slots) { From 773e84478d598da64b19986a5206a99d39bfee8f Mon Sep 17 00:00:00 2001 From: Apertyotis <1947517504@qq.com> Date: Sun, 21 Jun 2026 14:46:23 +0800 Subject: [PATCH 4/4] compat with storage drawer --- .../simibubi/create/foundation/item/ItemHelper.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java index 1acb8c2814..c6d9de99a6 100644 --- a/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java +++ b/src/main/java/com/simibubi/create/foundation/item/ItemHelper.java @@ -190,11 +190,11 @@ public static ItemStack extract(IItemHandler inv, Predicate test, Ext VisitedItemStackTracker tracker = new VisitedItemStackTracker(); for (int i = 0; i < inv.getSlots(); i++) { ItemStack stackIn = inv.getStackInSlot(i); - if (stackIn.isEmpty() || stackIn.getMaxStackSize() < amount || !test.test(stackIn)) + if (stackIn.isEmpty() || stackIn.getMaxStackSize() < amount) continue; - ItemStack extracted = inv.extractItem(i, stackIn.getCount(), true); - if (extracted.isEmpty()) + ItemStack extracted = inv.extractItem(i, Math.min(stackIn.getCount(), amount), true); + if (extracted.isEmpty() || !test.test(extracted)) continue; VisitedItemStackTracker.SlotAmountRecord slotRecord = tracker.update(extracted.copy(), i); @@ -215,12 +215,11 @@ public static ItemStack extract(IItemHandler inv, Predicate test, Ext int maxExtractAmount = amount; for (int i = 0; i < inv.getSlots(); i++) { ItemStack stackIn = inv.getStackInSlot(i); - if (stackIn.isEmpty() || (!result.isEmpty() && !ItemStack.isSameItemSameComponents(result, stackIn)) || - !test.test(stackIn)) + if (stackIn.isEmpty() || (!result.isEmpty() && !ItemStack.isSameItemSameComponents(result, stackIn))) continue; - ItemStack extracted = inv.extractItem(i, stackIn.getCount(), true); - if (extracted.isEmpty()) + ItemStack extracted = inv.extractItem(i, Math.min(stackIn.getCount(), maxExtractAmount), true); + if (extracted.isEmpty() || !test.test(extracted)) continue; if (result.isEmpty()) {