diff --git a/src/main/java/appeng/crafting/v2/resolvers/CraftableItemResolver.java b/src/main/java/appeng/crafting/v2/resolvers/CraftableItemResolver.java index a88d7e4a0..69de4a64b 100644 --- a/src/main/java/appeng/crafting/v2/resolvers/CraftableItemResolver.java +++ b/src/main/java/appeng/crafting/v2/resolvers/CraftableItemResolver.java @@ -144,6 +144,18 @@ public class CraftableItemResolver implements CraftingRequestResolver { if (matchingOutput == null) { state = State.FAILURE; } + + // Complex patterns are calculated per slot, so condensation can hide an invalid stack size. + if (hasZeroSizedStack(isComplex ? pattern.getAEInputs() : this.patternInputs) + || hasZeroSizedStack(this.patternRecursionInputs) + || hasZeroSizedStack(isComplex ? pattern.getAEOutputs() : this.patternOutputs)) { + // 0-sized stacks can only come from malformed pattern data; they divide by zero in Platform.ceilDiv + // and in calculateOneStep. Refuse the pattern instead of cancelling the entire crafting job. + AELog.error( + "Refusing malformed pattern with a 0-sized stack, outputs: %s", + Arrays.toString(this.patternOutputs)); + state = State.FAILURE; + } } @SuppressWarnings("unused") @@ -176,6 +188,16 @@ public class CraftableItemResolver implements CraftingRequestResolver { } } + private static boolean hasZeroSizedStack(IAEStack[] stacks) { + for (IAEStack stack : stacks) { + if (stack != null && stack.getStackSize() <= 0) { + return true; + } + } + + return false; + } + private static IAEStack[] calculateRecursiveInputs(IAEStack[] pInputs, IAEStack[] pOutputs) { IAEStack[] recInputs = null; // While this is a O(n*m) algorithm, no allocations are needed except for the actual output.