diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingManager.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingManager.java index 911ec7e14..d2a61f77f 100644 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingManager.java +++ b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingManager.java @@ -1,6 +1,7 @@ package com.refinedmods.refinedstorage.api.autocrafting; import com.refinedmods.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorListener; +import com.refinedmods.refinedstorage.api.autocrafting.task.ICalculationResult; import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTask; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; @@ -54,25 +55,18 @@ public interface ICraftingManager { * * @param stack the stack to craft * @param quantity the quantity to craft - * @return the crafting task, or null if no pattern was found for the given stack + * @return the calculation result */ - @Nullable - ICraftingTask create(ItemStack stack, int quantity); + ICalculationResult create(ItemStack stack, int quantity); /** * Creates a crafting task for a given stack, but doesn't add it to the list. * * @param stack the stack to craft * @param quantity the quantity to craft - * @return the crafting task, or null if no pattern was found for the given stack + * @return the calculation result */ - @Nullable - ICraftingTask create(FluidStack stack, int quantity); - - /** - * @return a new pattern chain list - */ - ICraftingPatternChainList createPatternChainList(); + ICalculationResult create(FluidStack stack, int quantity); /** * Schedules a crafting task if the task isn't scheduled yet. @@ -170,9 +164,8 @@ public interface ICraftingManager { void onTaskChanged(); /** - * @param pattern to look for - * @return a LinkedHashSet with all container that have this pattern + * @param pattern pattern to look for + * @return a set with all containers that have this pattern */ - - Set getAllContainer(ICraftingPattern pattern); + Set getAllContainers(ICraftingPattern pattern); } diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPattern.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPattern.java index 665185ec1..2b98e6743 100644 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPattern.java +++ b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPattern.java @@ -81,15 +81,4 @@ public interface ICraftingPattern { * @return the id of the factory that creates a crafting task for this pattern, as defined in the {@link ICraftingTaskRegistry} */ ResourceLocation getCraftingTaskFactoryId(); - - /** - * @param other the other pattern - * @return true if this pattern chain be in a chain with the other pattern, false otherwise - */ - boolean canBeInChainWith(ICraftingPattern other); - - /** - * @return the hashcode used to store the pattern chains - */ - int getChainHashCode(); } diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPatternChain.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPatternChain.java deleted file mode 100644 index 09d9c1d1a..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPatternChain.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.refinedmods.refinedstorage.api.autocrafting; - -/** - * A crafting pattern chain, which stores equivalent patterns. - */ -public interface ICraftingPatternChain { - /** - * @return the current pattern in the chain - */ - ICraftingPattern current(); - - /** - * Cycles the pattern in the chain. - * - * @return the cycled (and now current) pattern - */ - ICraftingPattern cycle(); -} diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPatternChainList.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPatternChainList.java deleted file mode 100644 index b18b0d040..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/ICraftingPatternChainList.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.refinedmods.refinedstorage.api.autocrafting; - -/** - * A list of pattern chains per pattern. - */ -public interface ICraftingPatternChainList { - /** - * @param pattern the pattern - * @return a chain for the pattern - */ - ICraftingPatternChain getChain(ICraftingPattern pattern); -} diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/craftingmonitor/ICraftingMonitorElementList.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/craftingmonitor/ICraftingMonitorElementList.java index ede581ae9..406b77dab 100644 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/craftingmonitor/ICraftingMonitorElementList.java +++ b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/craftingmonitor/ICraftingMonitorElementList.java @@ -22,8 +22,8 @@ public interface ICraftingMonitorElementList { * Add a element to the Processing or Crafting list, similar elements will be merged. * A {@link #commit()} will stop any following adds to be merged with previous ones. * - * @param element the {@link ICraftingMonitorElement} - * @param isProcessing wether to add to the processing list or the crafting list + * @param element the {@link ICraftingMonitorElement} + * @param isProcessing whether to add to the processing list or the crafting list */ void add(ICraftingMonitorElement element, boolean isProcessing); diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/CraftingTaskErrorType.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/CalculationResultType.java similarity index 54% rename from src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/CraftingTaskErrorType.java rename to src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/CalculationResultType.java index f1f351e9d..54acfe990 100644 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/CraftingTaskErrorType.java +++ b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/CalculationResultType.java @@ -1,9 +1,21 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; /** - * The error type. + * The result type. */ -public enum CraftingTaskErrorType { +public enum CalculationResultType { + /** + * No problems. + */ + OK, + /** + * Some requirements are missing. + */ + MISSING, + /** + * There is no pattern for the requested stack. + */ + NO_PATTERN, /** * When the crafting task would cause too much server strain or is too complex. */ @@ -11,5 +23,5 @@ public enum CraftingTaskErrorType { /** * When one of the used patterns during the calculation reuses itself again and would cause an infinite loop. */ - RECURSIVE + RECURSIVE; } diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/CraftingTaskState.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/CraftingTaskState.java deleted file mode 100644 index 4aff296d5..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/CraftingTaskState.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.refinedmods.refinedstorage.api.autocrafting.task; - -public enum CraftingTaskState { - UNKNOWN, - CALCULATING, - CALCULATED, - RUNNING, - DONE -} diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICalculationResult.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICalculationResult.java new file mode 100644 index 000000000..d0540b926 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICalculationResult.java @@ -0,0 +1,41 @@ +package com.refinedmods.refinedstorage.api.autocrafting.task; + +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * The result of the crafting calculation. + */ +public interface ICalculationResult { + /** + * @return the type + */ + CalculationResultType getType(); + + /** + * @return get a list of {@link ICraftingPreviewElement}s + */ + List> getPreviewElements(); + + /** + * @return the task if the calculation {@link #isOk()}, otherwise null + */ + @Nullable + ICraftingTask getTask(); + + /** + * @return whether the calculation succeeded + */ + boolean isOk(); + + /** + * If this result type is a {@link CalculationResultType#RECURSIVE}, the recursed pattern will be returned here. + * + * @return the recursed pattern, or null if this result is not {@link CalculationResultType#RECURSIVE} + */ + @Nullable + ICraftingPattern getRecursedPattern(); +} diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTask.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTask.java index 397d23d10..a53f6363f 100644 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTask.java +++ b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTask.java @@ -2,14 +2,11 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; import com.refinedmods.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorElement; -import com.refinedmods.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement; import com.refinedmods.refinedstorage.api.network.INetwork; -import com.refinedmods.refinedstorage.api.util.IStackList; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraftforge.fluids.FluidStack; -import javax.annotation.Nullable; import java.util.List; import java.util.UUID; @@ -17,19 +14,10 @@ import java.util.UUID; * Represents a crafting task. */ public interface ICraftingTask { - /** - * Calculates what this task will do, but doesn't run the task yet. - * - * @return the error, or null if there was no error - */ - @Nullable - ICraftingTaskError calculate(); - /** * Updates this task. - * {@link ICraftingTask#calculate()} must be run before this! * - * @return true if this crafting task is finished and can be deleted from the list, false otherwise + * @return true if this crafting task is finished, false otherwise */ boolean update(); @@ -57,6 +45,7 @@ public interface ICraftingTask { * Called when a stack is inserted into the system through {@link INetwork#insertItemTracked(ItemStack, int)}. * * @param stack the stack + * @return the remainder of this stack after processing of the task */ int onTrackedInsert(ItemStack stack, int size); @@ -64,6 +53,7 @@ public interface ICraftingTask { * Called when a stack is inserted into the system through {@link INetwork#insertFluidTracked(FluidStack, int)}. * * @param stack the stack + * @return the remainder of this stack after processing of the task */ int onTrackedInsert(FluidStack stack, int size); @@ -76,56 +66,25 @@ public interface ICraftingTask { CompoundNBT writeToNbt(CompoundNBT tag); /** - * {@link ICraftingTask#calculate()} must be run before this! - * * @return the elements of this task for display in the crafting monitor */ List getCraftingMonitorElements(); - /** - * {@link ICraftingTask#calculate()} must be run before this! - * - * @return get a list of {@link ICraftingPreviewElement}s - */ - List> getPreviewStacks(); - /** * @return the crafting pattern corresponding to this task */ ICraftingPattern getPattern(); /** - * @return the time in ms when this task has started + * @return the unix time in ms when this task has started */ - long getExecutionStarted(); - - /** - * @return the missing items - */ - IStackList getMissing(); - - /** - * @return the missing fluids - */ - IStackList getMissingFluids(); - - /** - * @return true if any items or fluids are missing, false otherwise - */ - default boolean hasMissing() { - return !getMissing().isEmpty() || !getMissingFluids().isEmpty(); - } + long getStartTime(); /** * @return the id of this task */ UUID getId(); - /** - * @return the state of this crafting task - */ - CraftingTaskState getState(); - /** * Start the CraftingTask */ diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTaskError.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTaskError.java deleted file mode 100644 index 440c94885..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTaskError.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.refinedmods.refinedstorage.api.autocrafting.task; - -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; - -import javax.annotation.Nullable; - -/** - * Returned from {@link ICraftingTask#calculate()} when an error occurs during the calculation. - */ -public interface ICraftingTaskError { - /** - * @return the type - */ - CraftingTaskErrorType getType(); - - /** - * If this error type is a {@link CraftingTaskErrorType#RECURSIVE}, the recursed pattern will be returned here. - * - * @return the recursed pattern, or null if this error is not {@link CraftingTaskErrorType#RECURSIVE} - */ - @Nullable - ICraftingPattern getRecursedPattern(); -} diff --git a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTaskFactory.java b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTaskFactory.java index bba1cc291..e6d7541fe 100644 --- a/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTaskFactory.java +++ b/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ICraftingTaskFactory.java @@ -4,8 +4,6 @@ import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; import com.refinedmods.refinedstorage.api.network.INetwork; import net.minecraft.nbt.CompoundNBT; -import javax.annotation.Nonnull; - /** * A factory that creates a crafting task. * Register this factory in the {@link ICraftingTaskRegistry}. @@ -18,10 +16,9 @@ public interface ICraftingTaskFactory { * @param requested the request info * @param pattern the pattern * @param quantity the quantity - * @return the crafting task + * @return the calculation result */ - @Nonnull - ICraftingTask create(INetwork network, ICraftingRequestInfo requested, int quantity, ICraftingPattern pattern); + ICalculationResult create(INetwork network, ICraftingRequestInfo requested, int quantity, ICraftingPattern pattern); /** * Returns a crafting task for a given NBT tag. diff --git a/src/main/java/com/refinedmods/refinedstorage/api/util/IStackList.java b/src/main/java/com/refinedmods/refinedstorage/api/util/IStackList.java index aca13e545..92969c2f3 100644 --- a/src/main/java/com/refinedmods/refinedstorage/api/util/IStackList.java +++ b/src/main/java/com/refinedmods/refinedstorage/api/util/IStackList.java @@ -56,6 +56,23 @@ public interface IStackList { return get(stack, IComparer.COMPARE_NBT); } + /** + * Returns the amount in this list, based on the stack and the flags. + * + * @param stack the stack + * @param flags the flags + * @return the count, 0 if not found + */ + int getCount(@Nonnull T stack, int flags); + + /** + * @param stack the stack + * @return the count, 0 if not found + */ + default int getCount(@Nonnull T stack) { + return getCount(stack, IComparer.COMPARE_NBT); + } + /** * Returns a stack. * diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingManager.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingManager.java index 7d559785c..e7aaecd0f 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingManager.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingManager.java @@ -2,18 +2,14 @@ package com.refinedmods.refinedstorage.apiimpl.autocrafting; import com.refinedmods.refinedstorage.api.autocrafting.ICraftingManager; import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternChainList; import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternContainer; import com.refinedmods.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorListener; -import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTask; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTaskError; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTaskFactory; +import com.refinedmods.refinedstorage.api.autocrafting.task.*; import com.refinedmods.refinedstorage.api.network.INetwork; import com.refinedmods.refinedstorage.api.network.node.INetworkNode; import com.refinedmods.refinedstorage.api.util.IComparer; import com.refinedmods.refinedstorage.apiimpl.API; -import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.CraftingTask; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.calculator.CalculationResult; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.ListNBT; @@ -94,40 +90,33 @@ public class CraftingManager implements ICraftingManager { } @Override - @Nullable - public ICraftingTask create(ItemStack stack, int quantity) { + public ICalculationResult create(ItemStack stack, int quantity) { ICraftingPattern pattern = getPattern(stack); if (pattern == null) { - return null; + return new CalculationResult(CalculationResultType.NO_PATTERN); } ICraftingTaskFactory factory = API.instance().getCraftingTaskRegistry().get(pattern.getCraftingTaskFactoryId()); if (factory == null) { - return null; - } - - return factory.create(network, API.instance().createCraftingRequestInfo(stack), quantity, pattern); - } - - @Nullable - @Override - public ICraftingTask create(FluidStack stack, int quantity) { - ICraftingPattern pattern = getPattern(stack); - if (pattern == null) { - return null; - } - - ICraftingTaskFactory factory = API.instance().getCraftingTaskRegistry().get(pattern.getCraftingTaskFactoryId()); - if (factory == null) { - return null; + return new CalculationResult(CalculationResultType.NO_PATTERN); } return factory.create(network, API.instance().createCraftingRequestInfo(stack), quantity, pattern); } @Override - public ICraftingPatternChainList createPatternChainList() { - return new CraftingPatternChainList(patterns); + public ICalculationResult create(FluidStack stack, int quantity) { + ICraftingPattern pattern = getPattern(stack); + if (pattern == null) { + return new CalculationResult(CalculationResultType.NO_PATTERN); + } + + ICraftingTaskFactory factory = API.instance().getCraftingTaskRegistry().get(pattern.getCraftingTaskFactoryId()); + if (factory == null) { + return new CalculationResult(CalculationResultType.NO_PATTERN); + } + + return factory.create(network, API.instance().createCraftingRequestInfo(stack), quantity, pattern); } @Override @@ -249,18 +238,10 @@ public class CraftingManager implements ICraftingManager { } if (amount > 0) { - ICraftingTask task = create(stack, amount); + ICalculationResult result = create(stack, amount); - if (task != null) { - ICraftingTaskError error = task.calculate(); - - if (error == null && !task.hasMissing()) { - this.start(task); - - return task; - } else { - throttle(source); - } + if (result.isOk()) { + start(result.getTask()); } else { throttle(source); } @@ -285,18 +266,10 @@ public class CraftingManager implements ICraftingManager { } if (amount > 0) { - ICraftingTask task = create(stack, amount); + ICalculationResult result = create(stack, amount); - if (task != null) { - ICraftingTaskError error = task.calculate(); - - if (error == null && !task.hasMissing()) { - this.start(task); - - return task; - } else { - throttle(source); - } + if (result.isOk()) { + start(result.getTask()); } else { throttle(source); } @@ -413,8 +386,8 @@ public class CraftingManager implements ICraftingManager { } @Override - public Set getAllContainer(ICraftingPattern pattern) { - return patternToContainer.getOrDefault(pattern, new LinkedHashSet<>()); + public Set getAllContainers(ICraftingPattern pattern) { + return patternToContainer.getOrDefault(pattern, Collections.emptySet()); } @Nullable diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPattern.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPattern.java index 8a16ef8a6..5cc26cf53 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPattern.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPattern.java @@ -169,7 +169,13 @@ public class CraftingPattern implements ICraftingPattern { } @Override - public boolean canBeInChainWith(ICraftingPattern other) { + public boolean equals(Object otherObj) { + if (!(otherObj instanceof ICraftingPattern)) { + return false; + } + + ICraftingPattern other = (ICraftingPattern) otherObj; + if (other.isProcessing() != processing) { return false; } @@ -239,7 +245,7 @@ public class CraftingPattern implements ICraftingPattern { } @Override - public int getChainHashCode() { + public int hashCode() { int result = 0; result = 31 * result + (processing ? 1 : 0); @@ -272,19 +278,6 @@ public class CraftingPattern implements ICraftingPattern { return result; } - @Override - public int hashCode() { - return getChainHashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof CraftingPattern) { - return canBeInChainWith((CraftingPattern) obj); - } - return false; - } - public static class DummyCraftingInventory extends CraftingInventory { public DummyCraftingInventory() { super(new Container(null, 0) { diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternChain.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternChain.java deleted file mode 100644 index 8a8026802..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternChain.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.refinedmods.refinedstorage.apiimpl.autocrafting; - -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternChain; - -import java.util.ArrayList; -import java.util.List; - -public class CraftingPatternChain implements ICraftingPatternChain { - private final List patterns = new ArrayList<>(); - private int pos; - - public void addPattern(ICraftingPattern pattern) { - patterns.add(pattern); - } - - @Override - public ICraftingPattern current() { - return patterns.get(pos); - } - - @Override - public ICraftingPattern cycle() { - if (pos + 1 >= patterns.size()) { - this.pos = 0; - } else { - this.pos++; - } - - return current(); - } -} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternChainList.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternChainList.java deleted file mode 100644 index 111fb82cd..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternChainList.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.refinedmods.refinedstorage.apiimpl.autocrafting; - -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternChain; -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternChainList; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public class CraftingPatternChainList implements ICraftingPatternChainList { - private final Map map = new HashMap<>(); - - public CraftingPatternChainList(Collection patterns) { - for (ICraftingPattern pattern : patterns) { - Key key = new Key(pattern); - - CraftingPatternChain chain = map.get(key); - - if (chain == null) { - map.put(key, chain = new CraftingPatternChain()); - } - - chain.addPattern(pattern); - } - } - - @Override - public ICraftingPatternChain getChain(ICraftingPattern pattern) { - ICraftingPatternChain chain = map.get(new Key(pattern)); - - if (chain == null) { - throw new IllegalStateException("Pattern was not found in pattern chain"); - } - - return chain; - } - - private static class Key { - private final ICraftingPattern pattern; - - public Key(ICraftingPattern pattern) { - this.pattern = pattern; - } - - @Override - public boolean equals(Object other) { - return other instanceof Key && pattern.canBeInChainWith(((Key) other).pattern); - } - - @Override - public int hashCode() { - return pattern.getChainHashCode(); - } - } -} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/craftingmonitor/FluidCraftingMonitorElement.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/craftingmonitor/FluidCraftingMonitorElement.java index cb3ca8a20..6362e07a8 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/craftingmonitor/FluidCraftingMonitorElement.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/craftingmonitor/FluidCraftingMonitorElement.java @@ -158,4 +158,50 @@ public class FluidCraftingMonitorElement implements ICraftingMonitorElement { public int elementHashCode() { return API.instance().getFluidStackHashCode(stack); } + + public static class Builder { + private final FluidStack stack; + private int stored; + private int missing; + private int processing; + private int scheduled; + private int crafting; + + public Builder(FluidStack stack) { + this.stack = stack; + } + + public FluidCraftingMonitorElement.Builder stored(int stored) { + this.stored = stored; + return this; + } + + public FluidCraftingMonitorElement.Builder missing(int missing) { + this.missing = missing; + return this; + } + + public FluidCraftingMonitorElement.Builder processing(int processing) { + this.processing = processing; + return this; + } + + public FluidCraftingMonitorElement.Builder scheduled(int scheduled) { + this.scheduled = scheduled; + return this; + } + + public FluidCraftingMonitorElement.Builder crafting(int crafting) { + this.crafting = crafting; + return this; + } + + public FluidCraftingMonitorElement build() { + return new FluidCraftingMonitorElement(stack, stored, missing, processing, scheduled, crafting); + } + + public static FluidCraftingMonitorElement.Builder forStack(FluidStack stack) { + return new FluidCraftingMonitorElement.Builder(stack); + } + } } diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/craftingmonitor/ItemCraftingMonitorElement.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/craftingmonitor/ItemCraftingMonitorElement.java index 906d03f67..40aefff78 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/craftingmonitor/ItemCraftingMonitorElement.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/craftingmonitor/ItemCraftingMonitorElement.java @@ -158,4 +158,50 @@ public class ItemCraftingMonitorElement implements ICraftingMonitorElement { public int elementHashCode() { return API.instance().getItemStackHashCode(stack); } + + public static class Builder { + private final ItemStack stack; + private int stored; + private int missing; + private int processing; + private int scheduled; + private int crafting; + + public Builder(ItemStack stack) { + this.stack = stack; + } + + public Builder stored(int stored) { + this.stored = stored; + return this; + } + + public Builder missing(int missing) { + this.missing = missing; + return this; + } + + public Builder processing(int processing) { + this.processing = processing; + return this; + } + + public Builder scheduled(int scheduled) { + this.scheduled = scheduled; + return this; + } + + public Builder crafting(int crafting) { + this.crafting = crafting; + return this; + } + + public ItemCraftingMonitorElement build() { + return new ItemCraftingMonitorElement(stack, stored, missing, processing, scheduled, crafting); + } + + public static Builder forStack(ItemStack stack) { + return new Builder(stack); + } + } } diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/preview/ErrorCraftingPreviewElement.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/preview/ErrorCraftingPreviewElement.java index a4c57c81a..51e8f8d1f 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/preview/ErrorCraftingPreviewElement.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/preview/ErrorCraftingPreviewElement.java @@ -3,7 +3,7 @@ package com.refinedmods.refinedstorage.apiimpl.autocrafting.preview; import com.mojang.blaze3d.matrix.MatrixStack; import com.refinedmods.refinedstorage.RS; import com.refinedmods.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement; -import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskErrorType; +import com.refinedmods.refinedstorage.api.autocrafting.task.CalculationResultType; import com.refinedmods.refinedstorage.api.render.IElementDrawers; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketBuffer; @@ -12,10 +12,10 @@ import net.minecraft.util.ResourceLocation; public class ErrorCraftingPreviewElement implements ICraftingPreviewElement { public static final ResourceLocation ID = new ResourceLocation(RS.ID, "error"); - private final CraftingTaskErrorType type; + private final CalculationResultType type; private final ItemStack stack; - public ErrorCraftingPreviewElement(CraftingTaskErrorType type, ItemStack stack) { + public ErrorCraftingPreviewElement(CalculationResultType type, ItemStack stack) { this.type = type; this.stack = stack; } @@ -51,13 +51,13 @@ public class ErrorCraftingPreviewElement implements ICraftingPreviewElement= 0 && errorIdx < CraftingTaskErrorType.values().length ? CraftingTaskErrorType.values()[errorIdx] : CraftingTaskErrorType.TOO_COMPLEX; + CalculationResultType error = errorIdx >= 0 && errorIdx < CalculationResultType.values().length ? CalculationResultType.values()[errorIdx] : CalculationResultType.TOO_COMPLEX; ItemStack stack = buf.readItemStack(); return new ErrorCraftingPreviewElement(error, stack); diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/Craft.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/Craft.java deleted file mode 100644 index 16332c729..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/Craft.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; - -import com.google.common.primitives.Ints; -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; -import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; -import com.refinedmods.refinedstorage.api.network.INetwork; -import com.refinedmods.refinedstorage.api.util.IStackList; -import com.refinedmods.refinedstorage.api.util.StackListEntry; -import com.refinedmods.refinedstorage.apiimpl.API; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.nbt.ListNBT; -import net.minecraftforge.common.util.Constants; -import org.apache.logging.log4j.LogManager; - -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - - -public abstract class Craft { - private static final String NBT_PATTERN = "Pattern"; - private static final String NBT_ROOT = "Root"; - private static final String NBT_IS_PROCESSING = "IsProcessing"; - private static final String NBT_ITEMS_TO_USE = "ItemsToUse"; - private static final String NBT_QUANTITY = "Quantity"; - private static final String NBT_NEEDED_PER_CRAFT = "NeededPerCraft"; - - private final boolean root; - protected int quantity; - private final ICraftingPattern pattern; - private final Map> itemsToUse = new LinkedHashMap<>(); - private final Map neededPerCraft = new LinkedHashMap<>(); - - Craft(ICraftingPattern pattern, boolean root) { - this.pattern = pattern; - this.root = root; - } - - Craft(INetwork network, CompoundNBT tag) throws CraftingTaskReadException { - this.quantity = tag.getInt(NBT_QUANTITY); - this.pattern = CraftingTask.readPatternFromNbt(tag.getCompound(NBT_PATTERN), network.getWorld()); - this.root = tag.getBoolean(NBT_ROOT); - ListNBT list = tag.getList(NBT_ITEMS_TO_USE, Constants.NBT.TAG_LIST); - for (int i = 0; i < list.size(); i++) { - this.itemsToUse.put(i, CraftingTask.readItemStackList(list.getList(i))); - } - List perCraftList = Ints.asList(tag.getIntArray(NBT_NEEDED_PER_CRAFT)); - for (int i = 0; i < perCraftList.size(); i++) { - neededPerCraft.put(i, perCraftList.get(i)); - } - } - - static Craft createCraftFromNBT(INetwork network, CompoundNBT tag) throws CraftingTaskReadException { - return tag.getBoolean(NBT_IS_PROCESSING) ? new Processing(network, tag) : new Crafting(network, tag); - } - - ICraftingPattern getPattern() { - return pattern; - } - - int getQuantity() { - return quantity; - } - - void addQuantity(int quantity) { - this.quantity += quantity; - } - - void next() { - quantity--; - } - - boolean isRoot() { - return root; - } - - boolean hasItems() { - return !itemsToUse.isEmpty(); - } - - IStackList getItemsToUse(boolean simulate) { - IStackList toReturn = API.instance().createItemStackList(); - for (int i = 0; i < itemsToUse.size(); i++) { - int needed = neededPerCraft.get(i); - if (!itemsToUse.get(i).isEmpty()) { - Iterator> it = itemsToUse.get(i).getStacks().iterator(); - while (needed > 0 && it.hasNext()) { - ItemStack toUse = it.next().getStack(); - if (needed < toUse.getCount()) { - if (!simulate) { - itemsToUse.get(i).remove(toUse, needed); - } - toReturn.add(toUse, needed); - needed = 0; - } else { - if (!simulate) { - it.remove(); - } - needed -= toUse.getCount(); - toReturn.add(toUse); - } - } - } else { - LogManager.getLogger(Craft.class).warn("Craft requested more Items than available"); - this.quantity = 0; // stop crafting - break; - } - } - return toReturn; - } - - void addItemsToUse(int ingredientNumber, ItemStack stack, int size, int perCraft) { - if (!neededPerCraft.containsKey(ingredientNumber)) { - neededPerCraft.put(ingredientNumber, perCraft); - } - if (!itemsToUse.containsKey(ingredientNumber)) { - itemsToUse.put(ingredientNumber, API.instance().createItemStackList()); - } - itemsToUse.get(ingredientNumber).add(stack, size); - } - - CompoundNBT writeToNbt() { - CompoundNBT tag = new CompoundNBT(); - tag.putInt(NBT_QUANTITY, quantity); - tag.putBoolean(NBT_IS_PROCESSING, this instanceof Processing); - tag.putBoolean(NBT_ROOT, root); - tag.put(NBT_PATTERN, CraftingTask.writePatternToNbt(pattern)); - ListNBT list = new ListNBT(); - for (IStackList stackList : itemsToUse.values()) { - list.add(CraftingTask.writeItemStackList(stackList)); - } - tag.put(NBT_ITEMS_TO_USE, list); - tag.putIntArray(NBT_NEEDED_PER_CRAFT, Ints.toArray(neededPerCraft.values())); - - - return tag; - } - - void finishCalculation() { - //NOOP - } - -} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/Crafting.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/Crafting.java deleted file mode 100644 index 12806bacc..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/Crafting.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; - -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; -import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; -import com.refinedmods.refinedstorage.api.network.INetwork; -import com.refinedmods.refinedstorage.util.StackUtils; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.nbt.ListNBT; -import net.minecraft.util.NonNullList; -import net.minecraftforge.common.util.Constants; - -class Crafting extends Craft { - private static final String NBT_RECIPE = "Recipe"; - private final NonNullList recipe; - - Crafting(ICraftingPattern pattern, boolean root, NonNullList recipe) { - super(pattern, root); - this.recipe = recipe; - } - - Crafting(INetwork network, CompoundNBT tag) throws CraftingTaskReadException { - super(network, tag); - this.recipe = NonNullList.create(); - ListNBT tookList = tag.getList(NBT_RECIPE, Constants.NBT.TAG_COMPOUND); - for (int i = 0; i < tookList.size(); ++i) { - ItemStack stack = StackUtils.deserializeStackFromNbt(tookList.getCompound(i)); - - // Can be empty. - recipe.add(stack); - } - } - - NonNullList getRecipe() { - return recipe; - } - - CompoundNBT writeToNbt() { - CompoundNBT tag = super.writeToNbt(); - - ListNBT tookList = new ListNBT(); - for (ItemStack took : this.recipe) { - tookList.add(StackUtils.serializeStackToNbt(took)); - } - - tag.put(NBT_RECIPE, tookList); - - return tag; - } -} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingPatternInputs.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingPatternInputs.java new file mode 100644 index 000000000..b8440a641 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingPatternInputs.java @@ -0,0 +1,144 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; + +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.util.IComparer; +import com.refinedmods.refinedstorage.apiimpl.API; +import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class CraftingPatternInputs { + private final NonNullList recipe = NonNullList.create(); + private final List> itemIngredients = new ArrayList<>(); + private final List> fluidIngredients = new ArrayList<>(); + + public CraftingPatternInputs(ICraftingPattern pattern) { + fillOutRecipe(pattern); + combineItemInputs(pattern); + combineFluidInputs(pattern); + } + + private void fillOutRecipe(ICraftingPattern pattern) { + for (NonNullList inputsForSlot : pattern.getInputs()) { + if (inputsForSlot.isEmpty()) { + recipe.add(ItemStack.EMPTY); + } else { + recipe.add(inputsForSlot.get(0)); + } + } + } + + private void combineItemInputs(ICraftingPattern pattern) { + for (NonNullList inputsForSlot : pattern.getInputs()) { + if (inputsForSlot.isEmpty()) { + continue; + } + + Ingredient matchingIngredient = findMatchingItemIngredient(inputsForSlot); + + if (matchingIngredient == null) { + itemIngredients.add(new Ingredient<>(inputsForSlot, inputsForSlot.get(0).getCount())); + } else { + matchingIngredient.increaseCount(inputsForSlot.get(0).getCount()); + } + } + } + + private void combineFluidInputs(ICraftingPattern pattern) { + for (NonNullList inputsForSlot : pattern.getFluidInputs()) { + if (inputsForSlot.isEmpty()) { + continue; + } + + Ingredient matchingIngredient = findMatchingFluidIngredient(inputsForSlot); + + if (matchingIngredient == null) { + fluidIngredients.add(new Ingredient<>(inputsForSlot, inputsForSlot.get(0).getAmount())); + } else { + matchingIngredient.increaseCount(inputsForSlot.get(0).getAmount()); + } + } + } + + @Nullable + private Ingredient findMatchingItemIngredient(NonNullList inputsForSlot) { + for (Ingredient existingIngredient : itemIngredients) { + if (existingIngredient.getInputs().size() == inputsForSlot.size()) { + boolean found = true; + + for (int i = 0; i < inputsForSlot.size(); i++) { + if (!API.instance().getComparer().isEqualNoQuantity(existingIngredient.getInputs().get(i), inputsForSlot.get(i))) { + found = false; + break; + } + } + + if (found) { + return existingIngredient; + } + } + } + + return null; + } + + @Nullable + private Ingredient findMatchingFluidIngredient(NonNullList inputsForSlot) { + for (Ingredient existingIngredient : fluidIngredients) { + if (existingIngredient.getInputs().size() == inputsForSlot.size()) { + boolean found = true; + + for (int i = 0; i < inputsForSlot.size(); i++) { + if (!API.instance().getComparer().isEqual(existingIngredient.getInputs().get(i), inputsForSlot.get(i), IComparer.COMPARE_NBT)) { + found = false; + break; + } + } + + if (found) { + return existingIngredient; + } + } + } + + return null; + } + + public NonNullList getRecipe() { + return recipe; + } + + public List> getItemIngredients() { + return itemIngredients; + } + + public List> getFluidIngredients() { + return fluidIngredients; + } + + public static class Ingredient { + private final NonNullList inputs; + private int count; + + public Ingredient(NonNullList inputs, int count) { + this.inputs = inputs; + this.count = count; + } + + public NonNullList getInputs() { + return inputs; + } + + public int getCount() { + return count; + } + + public void increaseCount(int count) { + this.count += count; + } + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTask.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTask.java index 80771d556..ae41ab486 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTask.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTask.java @@ -1,54 +1,36 @@ package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; -import com.google.common.collect.ImmutableList; -import com.refinedmods.refinedstorage.RS; import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternContainer; -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternProvider; import com.refinedmods.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorElement; -import com.refinedmods.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorElementList; -import com.refinedmods.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement; -import com.refinedmods.refinedstorage.api.autocrafting.task.*; +import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; +import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingRequestInfo; +import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTask; import com.refinedmods.refinedstorage.api.network.INetwork; -import com.refinedmods.refinedstorage.api.network.node.INetworkNode; import com.refinedmods.refinedstorage.api.storage.disk.IStorageDisk; import com.refinedmods.refinedstorage.api.util.Action; import com.refinedmods.refinedstorage.api.util.IComparer; import com.refinedmods.refinedstorage.api.util.IStackList; -import com.refinedmods.refinedstorage.api.util.StackListEntry; import com.refinedmods.refinedstorage.apiimpl.API; -import com.refinedmods.refinedstorage.apiimpl.autocrafting.craftingmonitor.ErrorCraftingMonitorElement; -import com.refinedmods.refinedstorage.apiimpl.autocrafting.craftingmonitor.FluidCraftingMonitorElement; -import com.refinedmods.refinedstorage.apiimpl.autocrafting.craftingmonitor.ItemCraftingMonitorElement; -import com.refinedmods.refinedstorage.apiimpl.autocrafting.preview.FluidCraftingPreviewElement; -import com.refinedmods.refinedstorage.apiimpl.autocrafting.preview.ItemCraftingPreviewElement; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.monitor.CraftingMonitorElementFactory; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.Node; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.NodeList; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.NodeListener; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.ProcessingNode; import com.refinedmods.refinedstorage.apiimpl.storage.disk.FluidStorageDisk; import com.refinedmods.refinedstorage.apiimpl.storage.disk.ItemStorageDisk; import com.refinedmods.refinedstorage.apiimpl.storage.disk.factory.FluidStorageDiskFactory; import com.refinedmods.refinedstorage.apiimpl.storage.disk.factory.ItemStorageDiskFactory; -import com.refinedmods.refinedstorage.util.StackUtils; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.ListNBT; -import net.minecraft.util.NonNullList; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.util.Constants; import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.IFluidHandler; -import net.minecraftforge.items.IItemHandler; -import org.apache.commons.lang3.tuple.MutablePair; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import javax.annotation.Nullable; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.IntStream; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; -public class CraftingTask implements ICraftingTask { +public class CraftingTask implements ICraftingTask, NodeListener { private static final String NBT_REQUESTED = "Requested"; private static final String NBT_QUANTITY = "Quantity"; private static final String NBT_PATTERN = "Pattern"; @@ -60,56 +42,48 @@ public class CraftingTask implements ICraftingTask { private static final String NBT_TO_EXTRACT_INITIAL = "ToExtractInitial"; private static final String NBT_TO_EXTRACT_INITIAL_FLUIDS = "ToExtractInitialFluids"; private static final String NBT_CRAFTS = "Crafts"; - private static final String NBT_MISSING = "Missing"; - private static final String NBT_MISSING_FLUIDS = "MissingFluids"; private static final String NBT_TOTAL_STEPS = "TotalSteps"; private static final String NBT_CURRENT_STEP = "CurrentStep"; - private static final String NBT_PATTERN_STACK = "Stack"; - private static final String NBT_PATTERN_CONTAINER_POS = "ContainerPos"; - private static final int DEFAULT_EXTRACT_FLAGS = IComparer.COMPARE_NBT; - - private static final Logger LOGGER = LogManager.getLogger(CraftingTask.class); + private final IStorageDisk internalStorage; + private final IStorageDisk internalFluidStorage; private final INetwork network; private final ICraftingRequestInfo requested; private final int quantity; private final ICraftingPattern pattern; - private UUID id = UUID.randomUUID(); + private final UUID id; + private final NodeList nodes; + private final IStackList toExtractInitial; + private final IStackList toExtractInitialFluids; + private int ticks; - private long calculationStarted = -1; - private long executionStarted = -1; + private long startTime = -1; private int totalSteps; - private int currentstep; - private final Set patternsUsed = new HashSet<>(); - private CraftingTaskState state = CraftingTaskState.UNKNOWN; + private int currentStep; - private final IStorageDisk internalStorage; - private final IStorageDisk internalFluidStorage; + private final CraftingMonitorElementFactory craftingMonitorElementFactory = new CraftingMonitorElementFactory(); - private IStackList toExtractInitial = API.instance().createItemStackList(); - private IStackList toExtractInitialFluids = API.instance().createFluidStackList(); - - private final Map crafts = new LinkedHashMap<>(); - private final List toRemove = new ArrayList<>(); - - private IStackList missing = API.instance().createItemStackList(); - private IStackList missingFluids = API.instance().createFluidStackList(); - - private final IStackList toTake = API.instance().createItemStackList(); - private final IStackList toTakeFluids = API.instance().createFluidStackList(); - - private final List toCraft = new ArrayList<>(); - private final List toCraftFluids = new ArrayList<>(); - - public CraftingTask(INetwork network, ICraftingRequestInfo requested, int quantity, ICraftingPattern pattern) { + public CraftingTask(INetwork network, + ICraftingRequestInfo requested, + int quantity, + ICraftingPattern pattern, + NodeList nodes, + IStackList toExtractInitial, + IStackList toExtractInitialFluids) { this.network = network; + this.requested = requested; this.quantity = quantity; this.pattern = pattern; + this.id = UUID.randomUUID(); + this.nodes = nodes; this.internalStorage = new ItemStorageDisk(null, -1); this.internalFluidStorage = new FluidStorageDisk(null, -1); + + this.toExtractInitial = toExtractInitial; + this.toExtractInitialFluids = toExtractInitialFluids; } public CraftingTask(INetwork network, CompoundNBT tag) throws CraftingTaskReadException { @@ -117,819 +91,64 @@ public class CraftingTask implements ICraftingTask { this.requested = API.instance().createCraftingRequestInfo(tag.getCompound(NBT_REQUESTED)); this.quantity = tag.getInt(NBT_QUANTITY); - this.pattern = readPatternFromNbt(tag.getCompound(NBT_PATTERN), network.getWorld()); - this.ticks = tag.getInt(NBT_TICKS); + this.pattern = SerializationUtil.readPatternFromNbt(tag.getCompound(NBT_PATTERN), network.getWorld()); this.id = tag.getUniqueId(NBT_ID); - this.executionStarted = tag.getLong(NBT_EXECUTION_STARTED); + this.nodes = new NodeList(); + + this.ticks = tag.getInt(NBT_TICKS); + this.startTime = tag.getLong(NBT_EXECUTION_STARTED); this.totalSteps = tag.getInt(NBT_TOTAL_STEPS); - this.currentstep = tag.getInt(NBT_CURRENT_STEP); + this.currentStep = tag.getInt(NBT_CURRENT_STEP); - ItemStorageDiskFactory factoryItem = new ItemStorageDiskFactory(); - FluidStorageDiskFactory factoryFluid = new FluidStorageDiskFactory(); + this.internalStorage = new ItemStorageDiskFactory().createFromNbt(null, tag.getCompound(NBT_INTERNAL_STORAGE)); + this.internalFluidStorage = new FluidStorageDiskFactory().createFromNbt(null, tag.getCompound(NBT_INTERNAL_FLUID_STORAGE)); - this.internalStorage = factoryItem.createFromNbt(null, tag.getCompound(NBT_INTERNAL_STORAGE)); - this.internalFluidStorage = factoryFluid.createFromNbt(null, tag.getCompound(NBT_INTERNAL_FLUID_STORAGE)); + this.toExtractInitial = SerializationUtil.readItemStackList(tag.getList(NBT_TO_EXTRACT_INITIAL, Constants.NBT.TAG_COMPOUND)); + this.toExtractInitialFluids = SerializationUtil.readFluidStackList(tag.getList(NBT_TO_EXTRACT_INITIAL_FLUIDS, Constants.NBT.TAG_COMPOUND)); - this.toExtractInitial = readItemStackList(tag.getList(NBT_TO_EXTRACT_INITIAL, Constants.NBT.TAG_COMPOUND)); - this.toExtractInitialFluids = readFluidStackList(tag.getList(NBT_TO_EXTRACT_INITIAL_FLUIDS, Constants.NBT.TAG_COMPOUND)); - - ListNBT craftList = tag.getList(NBT_CRAFTS, Constants.NBT.TAG_COMPOUND); - for (int i = 0; i < craftList.size(); ++i) { - Craft c = Craft.createCraftFromNBT(network, craftList.getCompound(i)); - crafts.put(c.getPattern(), c); + ListNBT nodeList = tag.getList(NBT_CRAFTS, Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < nodeList.size(); ++i) { + Node node = Node.fromNbt(network, nodeList.getCompound(i)); + nodes.put(node.getPattern(), node); } - - this.missing = readItemStackList(tag.getList(NBT_MISSING, Constants.NBT.TAG_COMPOUND)); - this.missingFluids = readFluidStackList(tag.getList(NBT_MISSING_FLUIDS, Constants.NBT.TAG_COMPOUND)); } @Override public CompoundNBT writeToNbt(CompoundNBT tag) { tag.put(NBT_REQUESTED, requested.writeToNbt()); tag.putInt(NBT_QUANTITY, quantity); - tag.put(NBT_PATTERN, writePatternToNbt(pattern)); + tag.put(NBT_PATTERN, SerializationUtil.writePatternToNbt(pattern)); tag.putInt(NBT_TICKS, ticks); tag.putUniqueId(NBT_ID, id); - tag.putLong(NBT_EXECUTION_STARTED, executionStarted); + tag.putLong(NBT_EXECUTION_STARTED, startTime); tag.put(NBT_INTERNAL_STORAGE, internalStorage.writeToNbt()); tag.put(NBT_INTERNAL_FLUID_STORAGE, internalFluidStorage.writeToNbt()); - tag.put(NBT_TO_EXTRACT_INITIAL, writeItemStackList(toExtractInitial)); - tag.put(NBT_TO_EXTRACT_INITIAL_FLUIDS, writeFluidStackList(toExtractInitialFluids)); + tag.put(NBT_TO_EXTRACT_INITIAL, SerializationUtil.writeItemStackList(toExtractInitial)); + tag.put(NBT_TO_EXTRACT_INITIAL_FLUIDS, SerializationUtil.writeFluidStackList(toExtractInitialFluids)); tag.putInt(NBT_TOTAL_STEPS, totalSteps); - tag.putInt(NBT_CURRENT_STEP, currentstep); + tag.putInt(NBT_CURRENT_STEP, currentStep); - ListNBT craftingList = new ListNBT(); - for (Craft craft : this.crafts.values()) { - craftingList.add(craft.writeToNbt()); + ListNBT nodeList = new ListNBT(); + for (Node node : this.nodes.all()) { + nodeList.add(node.writeToNbt()); } - tag.put(NBT_CRAFTS, craftingList); - - - tag.put(NBT_MISSING, writeItemStackList(missing)); - tag.put(NBT_MISSING_FLUIDS, writeFluidStackList(missingFluids)); + tag.put(NBT_CRAFTS, nodeList); return tag; } - static ListNBT writeItemStackList(IStackList stacks) { - ListNBT list = new ListNBT(); - - for (StackListEntry entry : stacks.getStacks()) { - list.add(StackUtils.serializeStackToNbt(entry.getStack())); - } - - return list; - } - - static IStackList readItemStackList(ListNBT list) throws CraftingTaskReadException { - IStackList stacks = API.instance().createItemStackList(); - - for (int i = 0; i < list.size(); ++i) { - ItemStack stack = StackUtils.deserializeStackFromNbt(list.getCompound(i)); - - if (stack.isEmpty()) { - throw new CraftingTaskReadException("Empty stack!"); - } - - stacks.add(stack); - } - - return stacks; - } - - static ListNBT writeFluidStackList(IStackList stacks) { - ListNBT list = new ListNBT(); - - for (StackListEntry entry : stacks.getStacks()) { - list.add(entry.getStack().writeToNBT(new CompoundNBT())); - } - - return list; - } - - static IStackList readFluidStackList(ListNBT list) throws CraftingTaskReadException { - IStackList stacks = API.instance().createFluidStackList(); - - for (int i = 0; i < list.size(); ++i) { - FluidStack stack = FluidStack.loadFluidStackFromNBT(list.getCompound(i)); - - if (stack.isEmpty()) { - throw new CraftingTaskReadException("Empty stack!"); - } - - stacks.add(stack); - } - - return stacks; - } - - @Override - @Nullable - public ICraftingTaskError calculate() { - if (calculationStarted != -1) { - throw new IllegalStateException("Task already calculated!"); - } - - if (executionStarted != -1) { - throw new IllegalStateException("Task already started!"); - } - - this.state = CraftingTaskState.CALCULATING; - this.calculationStarted = System.currentTimeMillis(); - - IStackList results = API.instance().createItemStackList(); - IStackList fluidResults = API.instance().createFluidStackList(); - - IStackList storage = network.getItemStorageCache().getList().copy(); - IStackList fluidStorage = network.getFluidStorageCache().getList().copy(); - - int qtyPerCraft = getQuantityPerCraft(requested.getItem(), requested.getFluid(), this.pattern); - int qty = ((this.quantity - 1) / qtyPerCraft) + 1; //CeilDiv - - ICraftingTaskError result = calculateInternal(qty, storage, fluidStorage, results, fluidResults, this.pattern, true); - - if (result != null) { - this.state = CraftingTaskState.CALCULATED; - - return result; - } - - if (requested.getItem() != null) { - ItemStack req = requested.getItem().copy(); - req.setCount(qty * qtyPerCraft); - this.toCraft.add(req); - } else { - FluidStack req = requested.getFluid().copy(); - req.setAmount(qty * qtyPerCraft); - this.toCraftFluids.add(req); - } - - this.state = CraftingTaskState.CALCULATED; - - return null; - } - - static class PossibleInputs { - private final List possibilities; - private int pos; - - PossibleInputs(List possibilities) { - this.possibilities = possibilities; - } - - ItemStack get() { - return possibilities.get(pos); - } - - // Return false if we're exhausted. - boolean cycle() { - if (pos + 1 >= possibilities.size()) { - pos = 0; - - return false; - } - - pos++; - - return true; - } - - void sort(IStackList mutatedStorage, IStackList results) { - possibilities.sort((a, b) -> { - ItemStack ar = mutatedStorage.get(a); - ItemStack br = mutatedStorage.get(b); - - return (br == null ? 0 : br.getCount()) - (ar == null ? 0 : ar.getCount()); - }); - - possibilities.sort((a, b) -> { - ItemStack ar = results.get(a); - ItemStack br = results.get(b); - - return (br == null ? 0 : br.getCount()) - (ar == null ? 0 : ar.getCount()); - }); - } - } - - static class PossibleFluidInputs { - private final List possibilities; - private int pos; - - PossibleFluidInputs(List possibilities) { - this.possibilities = possibilities; - } - - FluidStack get() { - return possibilities.get(pos); - } - - // Return false if we're exhausted. - boolean cycle() { - if (pos + 1 >= possibilities.size()) { - pos = 0; - - return false; - } - - pos++; - - return true; - } - - void sort(IStackList mutatedStorage, IStackList results) { - possibilities.sort((a, b) -> { - FluidStack ar = mutatedStorage.get(a); - FluidStack br = mutatedStorage.get(b); - - return (br == null ? 0 : br.getAmount()) - (ar == null ? 0 : ar.getAmount()); - }); - - possibilities.sort((a, b) -> { - FluidStack ar = results.get(a); - FluidStack br = results.get(b); - - return (br == null ? 0 : br.getAmount()) - (ar == null ? 0 : ar.getAmount()); - }); - } - } - - @Nullable - private ICraftingTaskError calculateInternal( - int qty, - IStackList mutatedStorage, - IStackList mutatedFluidStorage, - IStackList results, - IStackList fluidResults, - ICraftingPattern pattern, - boolean root) { - - if (System.currentTimeMillis() - calculationStarted > RS.SERVER_CONFIG.getAutocrafting().getCalculationTimeoutMs()) { - return new CraftingTaskError(CraftingTaskErrorType.TOO_COMPLEX); - } - - if (!patternsUsed.add(pattern)) { - return new CraftingTaskError(CraftingTaskErrorType.RECURSIVE, pattern); - } - - IStackList itemsToExtract = API.instance().createItemStackList(); - IStackList fluidsToExtract = API.instance().createFluidStackList(); - - NonNullList recipe = NonNullList.create(); - List, Integer>> ingredients = new ArrayList<>(); - - combineCommonStacks(recipe, ingredients, pattern); - Craft craft = crafts.get(pattern); - if (craft == null) { - craft = pattern.isProcessing() ? new Processing(pattern, root) : new Crafting(pattern, root, recipe); - crafts.put(pattern, craft); - } - craft.addQuantity(qty); - - int ingredientNumber = -1; - - for (Pair, Integer> pair : ingredients) { - ingredientNumber++; - - PossibleInputs possibleInputs = new PossibleInputs(new ArrayList<>(pair.getLeft())); - possibleInputs.sort(mutatedStorage, results); - - ItemStack possibleInput = possibleInputs.get(); - - ItemStack fromSelf = results.get(possibleInput); - ItemStack fromNetwork = mutatedStorage.get(possibleInput); - - int remaining = pair.getRight() * qty; - - if (remaining < 0) { //int overflow - return new CraftingTaskError(CraftingTaskErrorType.TOO_COMPLEX); - } - - while (remaining > 0) { - if (fromSelf != null) { - int toTake = Math.min(remaining, fromSelf.getCount()); - - craft.addItemsToUse(ingredientNumber, possibleInput, toTake, pair.getRight()); - - results.remove(fromSelf, toTake); - - remaining -= toTake; - - fromSelf = results.get(possibleInput); - } - if (fromNetwork != null && remaining > 0) { - int toTake = Math.min(remaining, fromNetwork.getCount()); - - this.toTake.add(possibleInput, toTake); - - craft.addItemsToUse(ingredientNumber, possibleInput, toTake, pair.getRight()); - - mutatedStorage.remove(fromNetwork, toTake); - - remaining -= toTake; - - fromNetwork = mutatedStorage.get(possibleInput); - - toExtractInitial.add(possibleInput, toTake); - } - if (remaining > 0) { - ICraftingPattern subPattern = network.getCraftingManager().getPattern(possibleInput); - - if (subPattern != null) { - int qtyPerCraft = getQuantityPerCraft(possibleInput, null, subPattern); - int subQty = ((remaining - 1) / qtyPerCraft) + 1; //CeilDiv - - ICraftingTaskError result = calculateInternal(subQty, mutatedStorage, mutatedFluidStorage, results, fluidResults, subPattern, false); - - if (result != null) { - return result; - } - - fromSelf = results.get(possibleInput); - if (fromSelf == null) { - throw new IllegalStateException("Recursive calculation didn't yield anything"); - } - - fromNetwork = mutatedStorage.get(possibleInput); - // fromSelf contains the amount crafted after the loop. - this.toCraft.add(fromSelf.copy()); - - - } else { - if (!possibleInputs.cycle()) { - // Give up. - possibleInput = possibleInputs.get(); // Revert back to 0. - - this.missing.add(possibleInput, remaining); - - itemsToExtract.add(possibleInput, remaining); - - remaining = 0; - } else { - // Retry with new input... - possibleInput = possibleInputs.get(); - - fromSelf = results.get(possibleInput); - fromNetwork = mutatedStorage.get(possibleInput); - } - } - } - } - } - - if (craft instanceof Crafting) { - - ItemStack output = pattern.getOutput(recipe); - results.add(output, output.getCount() * qty); - - for (ItemStack byproduct : pattern.getByproducts(recipe)) { - results.add(byproduct, byproduct.getCount() * qty); - } - - } else { - Processing processing = (Processing) craft; - - ingredientNumber = -1; - - for (NonNullList inputs : pattern.getFluidInputs()) { - if (inputs.isEmpty()) { - continue; - } - ingredientNumber++; - - PossibleFluidInputs possibleInputs = new PossibleFluidInputs(new ArrayList<>(inputs)); - possibleInputs.sort(mutatedFluidStorage, fluidResults); - - FluidStack possibleInput = possibleInputs.get(); - - FluidStack fromSelf = fluidResults.get(possibleInput, IComparer.COMPARE_NBT); - FluidStack fromNetwork = mutatedFluidStorage.get(possibleInput, IComparer.COMPARE_NBT); - - int remaining = possibleInput.getAmount() * qty; - - if (remaining < 0) { //int overflow - return new CraftingTaskError(CraftingTaskErrorType.TOO_COMPLEX); - } - - processing.addFluidsToUse(possibleInput); - - while (remaining > 0) { - if (fromSelf != null) { - int toTake = Math.min(remaining, fromSelf.getAmount()); - - fluidResults.remove(possibleInput, toTake); - - remaining -= toTake; - - fromSelf = fluidResults.get(possibleInput, IComparer.COMPARE_NBT); - } - if (fromNetwork != null && remaining > 0) { - - int toTake = Math.min(remaining, fromNetwork.getAmount()); - - this.toTakeFluids.add(possibleInput, toTake); - - mutatedFluidStorage.remove(fromNetwork, toTake); - - remaining -= toTake; - - fromNetwork = mutatedFluidStorage.get(possibleInput, IComparer.COMPARE_NBT); - - toExtractInitialFluids.add(possibleInput, toTake); - } - if (remaining > 0) { - ICraftingPattern subPattern = network.getCraftingManager().getPattern(possibleInput); - - if (subPattern != null) { - int qtyPerCraft = getQuantityPerCraft(null, possibleInput, subPattern); - int subQty = ((remaining - 1) / qtyPerCraft) + 1; //CeilDiv - - ICraftingTaskError result = calculateInternal(subQty, mutatedStorage, mutatedFluidStorage, results, fluidResults, subPattern, false); - - if (result != null) { - return result; - } - - fromSelf = fluidResults.get(possibleInput, IComparer.COMPARE_NBT); - if (fromSelf == null) { - throw new IllegalStateException("Recursive fluid calculation didn't yield anything"); - } - - fromNetwork = mutatedFluidStorage.get(possibleInput, IComparer.COMPARE_NBT); - - // fromSelf contains the amount crafted after the loop. - this.toCraftFluids.add(fromSelf.copy()); - } else { - if (!possibleInputs.cycle()) { - // Give up. - possibleInput = possibleInputs.get(); // Revert back to 0. - - this.missingFluids.add(possibleInput, remaining); - - fluidsToExtract.add(possibleInput, remaining); - - remaining = 0; - } else { - // Retry with new input... - possibleInput = possibleInputs.get(); - - fromSelf = fluidResults.get(possibleInput); - fromNetwork = mutatedFluidStorage.get(possibleInput); - } - } - } - } - } - - pattern.getOutputs().forEach(x -> results.add(x, x.getCount() * qty)); - pattern.getFluidOutputs().forEach(x -> fluidResults.add(x, x.getAmount() * qty)); - - //only add this once - if (processing.getItemsToReceive().isEmpty()) { - pattern.getOutputs().forEach(processing::addItemsToReceive); - } - if (processing.getFluidsToReceive().isEmpty()) { - pattern.getFluidOutputs().forEach(processing::addFluidsToReceive); - } - } - - patternsUsed.remove(pattern); - return null; - } - @Override public void start() { - if (hasMissing()) { - LOGGER.warn("Crafting task with missing items or fluids cannot execute, cancelling..."); - return; - } + nodes.all().forEach(node -> { + totalSteps += node.getQuantity(); - crafts.values().forEach(craft -> { - totalSteps += craft.getQuantity(); - craft.finishCalculation(); + node.onCalculationFinished(); }); - executionStarted = System.currentTimeMillis(); + startTime = System.currentTimeMillis(); - extractInitial(); - } - - - private void extractInitial() { - if (!toExtractInitial.isEmpty()) { - List toRemove = new ArrayList<>(); - - for (StackListEntry toExtract : toExtractInitial.getStacks()) { - ItemStack result = network.extractItem(toExtract.getStack(), toExtract.getStack().getCount(), Action.PERFORM); - - if (!result.isEmpty()) { - internalStorage.insert(toExtract.getStack(), result.getCount(), Action.PERFORM); - - toRemove.add(result); - } - } - - for (ItemStack stack : toRemove) { - toExtractInitial.remove(stack); - } - - if (!toRemove.isEmpty()) { - network.getCraftingManager().onTaskChanged(); - } - } - - if (!toExtractInitialFluids.isEmpty()) { - List toRemove = new ArrayList<>(); - - for (StackListEntry toExtract : toExtractInitialFluids.getStacks()) { - FluidStack result = network.extractFluid(toExtract.getStack(), toExtract.getStack().getAmount(), Action.PERFORM); - - if (!result.isEmpty()) { - internalFluidStorage.insert(toExtract.getStack(), result.getAmount(), Action.PERFORM); - - toRemove.add(result); - } - } - - for (FluidStack stack : toRemove) { - toExtractInitialFluids.remove(stack); - } - - if (!toRemove.isEmpty()) { - network.getCraftingManager().onTaskChanged(); - } - } - } - - private void combineCommonStacks(NonNullList recipe, List, Integer>> ingredients, ICraftingPattern pattern) { - for (NonNullList inputs : pattern.getInputs()) { - if (inputs.isEmpty()) { - recipe.add(ItemStack.EMPTY); - } else { - recipe.add(inputs.get(0)); - - boolean match = false; - for (Pair, Integer> pair : ingredients) { - if (pair.getLeft().size() == inputs.size()) { - match = true; - for (int i = 0; i < inputs.size(); i++) { - if (!API.instance().getComparer().isEqualNoQuantity(pair.getLeft().get(i), inputs.get(i))) { - match = false; - break; - } - } - if (match) { - pair.setValue(pair.getRight() + inputs.get(0).getCount()); - break; - } - } - } - if (!match) { - ingredients.add(new MutablePair<>(inputs, inputs.get(0).getCount())); - } - } - } - } - - private void updateCrafting(Crafting c) { - - for (ICraftingPatternContainer container : network.getCraftingManager().getAllContainer(c.getPattern())) { - - int interval = container.getUpdateInterval(); - - if (interval < 0) { - throw new IllegalStateException(container + " has an update interval of < 0"); - } - - if (interval == 0 || ticks % interval == 0) { - for (int i = 0; i < container.getMaximumSuccessfulCraftingUpdates(); i++) { - - if (c.getQuantity() <= 0) { - toRemove.add(c); - return; - } - - if (extractFromInternalItemStorage(c.getItemsToUse(true).getStacks(), this.internalStorage, Action.SIMULATE) != null) { - - extractFromInternalItemStorage(c.getItemsToUse(false).getStacks(), this.internalStorage, Action.PERFORM); - - ItemStack output = c.getPattern().getOutput(c.getRecipe()); - - if (!c.isRoot()) { - this.internalStorage.insert(output, output.getCount(), Action.PERFORM); - } else { - ItemStack remainder = this.network.insertItem(output, output.getCount(), Action.PERFORM); - - this.internalStorage.insert(remainder, remainder.getCount(), Action.PERFORM); - } - - // Byproducts need to always be inserted in the internal storage for later reuse further in the task. - // Regular outputs can be inserted into the network *IF* it's a root since it's *NOT* expected to be used later on. - for (ItemStack byp : c.getPattern().getByproducts(c.getRecipe())) { - this.internalStorage.insert(byp, byp.getCount(), Action.PERFORM); - } - - c.next(); - currentstep++; - network.getCraftingManager().onTaskChanged(); - } else { - break; - } - } - } - } - } - - private void updateProcessing(Processing p) { - - if (p.getState() == ProcessingState.PROCESSED) { - toRemove.add(p); - network.getCraftingManager().onTaskChanged(); - return; - } - //These are for handling multiple crafters with differing states - boolean allLocked = true; - boolean allNull = true; - boolean allRejected = true; - - ProcessingState originalState = p.getState(); - - for (ICraftingPatternContainer container : network.getCraftingManager().getAllContainer(p.getPattern())) { - int interval = container.getUpdateInterval(); - - if (interval < 0) { - throw new IllegalStateException(container + " has an update interval of < 0"); - } - - if (interval == 0 || ticks % interval == 0) { - - for (int i = 0; i < container.getMaximumSuccessfulCraftingUpdates(); i++) { - if (p.getQuantity() <= 0) { - return; - } - - if (container.isLocked()) { - if (allLocked) { - p.setState(ProcessingState.LOCKED); - } - break; - } else { - allLocked = false; - } - if (p.hasItems() && container.getConnectedInventory() == null - || p.hasFluids() && container.getConnectedFluidInventory() == null) { - if (allNull) { - p.setState(ProcessingState.MACHINE_NONE); - } - break; - } else { - allNull = false; - } - - boolean hasAll = false; - IStackList extractedItems; - IStackList extractedFluids = null; - - extractedItems = extractFromInternalItemStorage(p.getItemsToUse(true).getStacks(), this.internalStorage, Action.SIMULATE); - if (extractedItems != null) { - extractedFluids = extractFromInternalFluidStorage(p.getFluidsToUse().getStacks(), this.internalFluidStorage, Action.SIMULATE); - if (extractedFluids != null) { - hasAll = true; - } - } - - boolean canInsert = false; - if (hasAll) { - canInsert = insertIntoInventory(container.getConnectedInventory(), extractedItems.getStacks(), Action.SIMULATE); - if (canInsert) { - canInsert = insertIntoTank(container.getConnectedFluidInventory(), extractedFluids.getStacks(), Action.SIMULATE); - } - } - - if (hasAll && !canInsert) { - if (allRejected) { - p.setState(ProcessingState.MACHINE_DOES_NOT_ACCEPT); - } - break; - } else { - allRejected = false; - } - - if (hasAll && canInsert) { - p.setState(ProcessingState.READY); - - extractFromInternalItemStorage(p.getItemsToUse(false).getStacks(), this.internalStorage, Action.PERFORM); - extractFromInternalFluidStorage(p.getFluidsToUse().getStacks(), this.internalFluidStorage, Action.PERFORM); - - insertIntoInventory(container.getConnectedInventory(), extractedItems.getStacks(), Action.PERFORM); - insertIntoTank(container.getConnectedFluidInventory(), extractedFluids.getStacks(), Action.PERFORM); - - p.next(); - currentstep++; - network.getCraftingManager().onTaskChanged(); - container.onUsedForProcessing(); - - } - } - - } - } - if (originalState != p.getState()) { - network.getCraftingManager().onTaskChanged(); - } - } - - private static IStackList extractFromInternalItemStorage(Collection> stacks, IStorageDisk storage, Action action) { - IStackList toReturn = API.instance().createItemStackList(); - for (StackListEntry entry : stacks) { - ItemStack result = storage.extract(entry.getStack(), entry.getStack().getCount(), DEFAULT_EXTRACT_FLAGS, action); - - if (result == ItemStack.EMPTY || result.getCount() != entry.getStack().getCount()) { - if (action == Action.PERFORM) { - throw new IllegalStateException("The internal crafting inventory reported that " + entry.getStack() + " was available but we got " + result); - } - return null; - } - toReturn.add(result); - } - return toReturn; - } - - private static IStackList extractFromInternalFluidStorage(Collection> stacks, IStorageDisk storage, Action action) { - IStackList toReturn = API.instance().createFluidStackList(); - for (StackListEntry entry : stacks) { - FluidStack result = storage.extract(entry.getStack(), entry.getStack().getAmount(), IComparer.COMPARE_NBT, action); - if (result == FluidStack.EMPTY || result.getAmount() != entry.getStack().getAmount()) { - if (action == Action.PERFORM) { - throw new IllegalStateException("The internal crafting inventory reported that " + entry.getStack() + " was available but we got " + result); - } - return null; - } - toReturn.add(result); - } - return toReturn; - } - - - private static boolean insertIntoInventory(@Nullable IItemHandler dest, Collection> toInsert, Action action) { - if (dest == null) { - return false; - } - if (toInsert.isEmpty()) { - return true; - } - Deque> stacks = new ArrayDeque<>(toInsert); - - StackListEntry currentEntry = stacks.poll(); - - ItemStack current = currentEntry != null ? currentEntry.getStack() : null; - - List availableSlots = IntStream.range(0, dest.getSlots()).boxed().collect(Collectors.toList()); - - while (current != null && !availableSlots.isEmpty()) { - ItemStack remainder = ItemStack.EMPTY; - - for (int i = 0; i < availableSlots.size(); ++i) { - int slot = availableSlots.get(i); - - // .copy() is mandatory! - remainder = dest.insertItem(slot, current.copy(), action == Action.SIMULATE); - - // If we inserted *something* - if (remainder.isEmpty() || current.getCount() != remainder.getCount()) { - availableSlots.remove(i); - break; - } - } - - if (remainder.isEmpty()) { // If we inserted successfully, get a next stack. - currentEntry = stacks.poll(); - - current = currentEntry != null ? currentEntry.getStack() : null; - } else if (current.getCount() == remainder.getCount()) { // If we didn't insert anything over ALL these slots, stop here. - break; - } else { // If we didn't insert all, continue with other slots and use our remainder. - current = remainder; - } - } - - boolean success = current == null && stacks.isEmpty(); - if (!success && action == Action.PERFORM) { - LOGGER.warn("Item Handler unexpectedly didn't accept " + (current != null ? current.getTranslationKey() : null) + ", the remainder has been voided!"); - } - return success; - } - - private static boolean insertIntoTank(IFluidHandler dest, Collection> toInsert, Action action) { - for (StackListEntry entry : toInsert) { - int filled = dest.fill(entry.getStack(), action == Action.SIMULATE ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE); - if (filled != entry.getStack().getAmount()) { - if (action == Action.PERFORM) { - LOGGER.warn("Fluid Handler unexpectedly didn't accept all of " + entry.getStack().getTranslationKey() + ", the remainder has been voided!"); - } - return false; - } - } - return true; + IoUtil.extractItemsFromNetwork(toExtractInitial, network, internalStorage); + IoUtil.extractFluidsFromNetwork(toExtractInitialFluids, network, internalFluidStorage); } @Override @@ -937,22 +156,15 @@ public class CraftingTask implements ICraftingTask { if (totalSteps == 0) { return 0; } - return (int) ((float) currentstep * 100 / totalSteps); + + return (int) ((float) currentStep * 100 / totalSteps); } @Override public boolean update() { - if (hasMissing()) { - LOGGER.warn("Crafting task with missing items or fluids cannot execute, cancelling..."); - - return true; - } - ++ticks; - if (this.crafts.isEmpty()) { - this.state = CraftingTaskState.DONE; - + if (nodes.isEmpty()) { List toPerform = new ArrayList<>(); for (ItemStack stack : internalStorage.getStacks()) { @@ -972,22 +184,14 @@ public class CraftingTask implements ICraftingTask { return internalStorage.getStacks().isEmpty() && internalFluidStorage.getStacks().isEmpty(); } else { - this.state = CraftingTaskState.RUNNING; + IoUtil.extractItemsFromNetwork(toExtractInitial, network, internalStorage); + IoUtil.extractFluidsFromNetwork(toExtractInitialFluids, network, internalFluidStorage); - extractInitial(); - - for (Craft craft : crafts.values()) { - if (craft instanceof Crafting) { - updateCrafting((Crafting) craft); - } else { - updateProcessing((Processing) craft); - } + for (Node node : nodes.all()) { + node.update(network, ticks, nodes, internalStorage, internalFluidStorage, this); } - for (Craft craft : toRemove) { - crafts.remove(craft.getPattern()); - } - toRemove.clear(); + nodes.removeMarkedForRemoval(); return false; } @@ -995,11 +199,7 @@ public class CraftingTask implements ICraftingTask { @Override public void onCancelled() { - crafts.values().forEach(c -> { - if (c instanceof Processing) { - network.getCraftingManager().getAllContainer(c.getPattern()).forEach(ICraftingPatternContainer::unlock); - } - }); + nodes.unlockAll(network); for (ItemStack remainder : internalStorage.getStacks()) { network.insertItem(remainder, remainder.getCount(), Action.PERFORM); @@ -1015,30 +215,6 @@ public class CraftingTask implements ICraftingTask { return quantity; } - public int getQuantityPerCraft(ItemStack item, FluidStack fluid, ICraftingPattern pattern) { - int qty = 0; - - if (item != null) { - for (ItemStack output : pattern.getOutputs()) { - if (API.instance().getComparer().isEqualNoQuantity(output, item)) { - qty += output.getCount(); - - if (!pattern.isProcessing()) { - break; - } - } - } - } else { - for (FluidStack output : pattern.getFluidOutputs()) { - if (API.instance().getComparer().isEqual(output, fluid, IComparer.COMPARE_NBT)) { - qty += output.getAmount(); - } - } - } - - return qty; - } - @Override public ICraftingRequestInfo getRequested() { return requested; @@ -1046,22 +222,21 @@ public class CraftingTask implements ICraftingTask { @Override public int onTrackedInsert(ItemStack stack, int size) { - for (Craft craft : this.crafts.values()) { - if (craft instanceof Processing) { - Processing p = (Processing) craft; + for (Node node : this.nodes.all()) { + if (node instanceof ProcessingNode) { + ProcessingNode processing = (ProcessingNode) node; - int needed = p.getNeeded(stack); + int needed = processing.getNeeded(stack); if (needed > 0) { - if (needed > size) { needed = size; } - p.addFinished(stack, needed); + processing.markReceived(stack, needed); size -= needed; - if (!p.isRoot()) { + if (!processing.isRoot()) { internalStorage.insert(stack, needed, Action.PERFORM); } else { ItemStack remainder = network.insertItem(stack, needed, Action.PERFORM); @@ -1069,9 +244,7 @@ public class CraftingTask implements ICraftingTask { internalStorage.insert(remainder, remainder.getCount(), Action.PERFORM); } - if (p.updateFinished()) { //only update if finished changes - network.getCraftingManager().onTaskChanged(); - } + network.getCraftingManager().onTaskChanged(); if (size == 0) { return 0; @@ -1085,23 +258,22 @@ public class CraftingTask implements ICraftingTask { @Override public int onTrackedInsert(FluidStack stack, int size) { - for (Craft craft : this.crafts.values()) { - if (craft instanceof Processing) { - Processing p = (Processing) craft; + for (Node node : this.nodes.all()) { + if (node instanceof ProcessingNode) { + ProcessingNode processing = (ProcessingNode) node; - int needed = p.getNeeded(stack); + int needed = processing.getNeeded(stack); if (needed > 0) { - if (needed > size) { needed = size; } - p.addFinished(stack, needed); + processing.markReceived(stack, needed); size -= needed; - if (!p.isRoot()) { + if (!processing.isRoot()) { internalFluidStorage.insert(stack, needed, Action.PERFORM); } else { FluidStack remainder = network.insertFluid(stack, needed, Action.PERFORM); @@ -1109,9 +281,7 @@ public class CraftingTask implements ICraftingTask { internalFluidStorage.insert(remainder, remainder.getAmount(), Action.PERFORM); } - if (p.updateFinished()) { //only update if finished changees - network.getCraftingManager().onTaskChanged(); - } + network.getCraftingManager().onTaskChanged(); if (size == 0) { return 0; @@ -1123,204 +293,9 @@ public class CraftingTask implements ICraftingTask { return size; } - static CompoundNBT writePatternToNbt(ICraftingPattern pattern) { - CompoundNBT tag = new CompoundNBT(); - - tag.put(NBT_PATTERN_STACK, pattern.getStack().serializeNBT()); - tag.putLong(NBT_PATTERN_CONTAINER_POS, pattern.getContainer().getPosition().toLong()); - - return tag; - } - - static ICraftingPattern readPatternFromNbt(CompoundNBT tag, World world) throws CraftingTaskReadException { - BlockPos containerPos = BlockPos.fromLong(tag.getLong(NBT_PATTERN_CONTAINER_POS)); - - INetworkNode node = API.instance().getNetworkNodeManager((ServerWorld) world).getNode(containerPos); - - if (node instanceof ICraftingPatternContainer) { - ItemStack stack = ItemStack.read(tag.getCompound(NBT_PATTERN_STACK)); - - if (stack.getItem() instanceof ICraftingPatternProvider) { - return ((ICraftingPatternProvider) stack.getItem()).create(world, stack, (ICraftingPatternContainer) node); - } else { - throw new CraftingTaskReadException("Pattern stack is not a crafting pattern provider"); - } - } else { - throw new CraftingTaskReadException("Crafting pattern container doesn't exist anymore"); - } - } - @Override public List getCraftingMonitorElements() { - ICraftingMonitorElementList elements = API.instance().createCraftingMonitorElementList(); - - for (Craft craft : this.crafts.values()) { - if (craft instanceof Crafting) { - if (craft.getQuantity() > 0) { - Crafting c = (Crafting) craft; - for (ItemStack receive : c.getPattern().getOutputs()) { - elements.add(new ItemCraftingMonitorElement(receive, 0, 0, 0, 0, receive.getCount() * c.getQuantity()), false); - } - } - } else { - Processing p = (Processing) craft; - if (p.getState() == ProcessingState.PROCESSED) { - continue; - } - - for (StackListEntry put : p.getItemsToDisplay().getStacks()) { - if (p.getProcessing() > 0 || p.getState() != ProcessingState.READY) { - ICraftingMonitorElement element = new ItemCraftingMonitorElement(put.getStack(), 0, 0, put.getStack().getCount() * p.getProcessing(), 0, 0); - - if (p.getState() == ProcessingState.MACHINE_DOES_NOT_ACCEPT) { - element = new ErrorCraftingMonitorElement(element, "gui.refinedstorage.crafting_monitor.machine_does_not_accept_item"); - } else if (p.getState() == ProcessingState.MACHINE_NONE) { - element = new ErrorCraftingMonitorElement(element, "gui.refinedstorage.crafting_monitor.machine_none"); - } else if (p.getState() == ProcessingState.LOCKED) { - element = new ErrorCraftingMonitorElement(element, "gui.refinedstorage.crafting_monitor.crafter_is_locked"); - } - elements.add(element, true); - } - } - for (StackListEntry receive : p.getItemsToReceive().getStacks()) { - int count = p.getNeeded(receive.getStack()); - if (count > 0) { - elements.add(new ItemCraftingMonitorElement(receive.getStack(), 0, 0, 0, count, 0), true); - } - } - for (StackListEntry put : p.getFluidsToUse().getStacks()) { - if (p.getProcessing() > 0 || p.getState() != ProcessingState.READY) { - ICraftingMonitorElement element = new FluidCraftingMonitorElement(put.getStack(), 0, 0, put.getStack().getAmount() * p.getProcessing(), 0, 0); - if (p.getState() == ProcessingState.MACHINE_DOES_NOT_ACCEPT) { - element = new ErrorCraftingMonitorElement(element, "gui.refinedstorage.crafting_monitor.machine_does_not_accept_fluid"); - } else if (p.getState() == ProcessingState.MACHINE_NONE) { - element = new ErrorCraftingMonitorElement(element, "gui.refinedstorage.crafting_monitor.machine_none"); - } else if (p.getState() == ProcessingState.LOCKED) { - element = new ErrorCraftingMonitorElement(element, "gui.refinedstorage.crafting_monitor.crafter_is_locked"); - } - elements.add(element, true); - } - } - - for (StackListEntry receive : p.getFluidsToReceive().getStacks()) { - int count = p.getNeeded(receive.getStack()); - if (count > 0) { - elements.add(new FluidCraftingMonitorElement(receive.getStack(), 0, 0, 0, count, 0), true); - } - } - } - } - - for (ItemStack stack : this.internalStorage.getStacks()) { - elements.addStorage(new ItemCraftingMonitorElement(stack, stack.getCount(), 0, 0, 0, 0)); - } - - for (FluidStack stack : this.internalFluidStorage.getStacks()) { - elements.addStorage(new FluidCraftingMonitorElement(stack, stack.getAmount(), 0, 0, 0, 0)); - } - - elements.commit(); - - return elements.getElements(); - } - - @Override - public List> getPreviewStacks() { - Map map = new LinkedHashMap<>(); - Map mapFluids = new LinkedHashMap<>(); - - for (StackListEntry stack : missing.getStacks()) { - int hash = API.instance().getItemStackHashCode(stack.getStack()); - - ItemCraftingPreviewElement previewStack = map.get(hash); - - if (previewStack == null) { - previewStack = new ItemCraftingPreviewElement(stack.getStack()); - } - - previewStack.setMissing(true); - previewStack.addToCraft(stack.getStack().getCount()); - - map.put(hash, previewStack); - } - - for (StackListEntry stack : missingFluids.getStacks()) { - int hash = API.instance().getFluidStackHashCode(stack.getStack()); - - FluidCraftingPreviewElement previewStack = mapFluids.get(hash); - - if (previewStack == null) { - previewStack = new FluidCraftingPreviewElement(stack.getStack()); - } - - previewStack.setMissing(true); - previewStack.addToCraft(stack.getStack().getAmount()); - - mapFluids.put(hash, previewStack); - } - - for (ItemStack stack : ImmutableList.copyOf(toCraft).reverse()) { - int hash = API.instance().getItemStackHashCode(stack); - - ItemCraftingPreviewElement previewStack = map.get(hash); - - if (previewStack == null) { - previewStack = new ItemCraftingPreviewElement(stack.getStack()); - } - - previewStack.addToCraft(stack.getCount()); - - map.put(hash, previewStack); - } - - for (FluidStack stack : ImmutableList.copyOf(toCraftFluids).reverse()) { - int hash = API.instance().getFluidStackHashCode(stack); - - FluidCraftingPreviewElement previewStack = mapFluids.get(hash); - - if (previewStack == null) { - previewStack = new FluidCraftingPreviewElement(stack); - } - - previewStack.addToCraft(stack.getAmount()); - - mapFluids.put(hash, previewStack); - } - - for (StackListEntry stack : toTake.getStacks()) { - int hash = API.instance().getItemStackHashCode(stack.getStack()); - - ItemCraftingPreviewElement previewStack = map.get(hash); - - if (previewStack == null) { - previewStack = new ItemCraftingPreviewElement(stack.getStack()); - } - - previewStack.addAvailable(stack.getStack().getCount()); - - map.put(hash, previewStack); - } - - for (StackListEntry stack : toTakeFluids.getStacks()) { - int hash = API.instance().getFluidStackHashCode(stack.getStack()); - - FluidCraftingPreviewElement previewStack = mapFluids.get(hash); - - if (previewStack == null) { - previewStack = new FluidCraftingPreviewElement(stack.getStack()); - } - - previewStack.addAvailable(stack.getStack().getAmount()); - - mapFluids.put(hash, previewStack); - } - - List> elements = new ArrayList<>(); - - elements.addAll(map.values()); - elements.addAll(mapFluids.values()); - - return elements; + return craftingMonitorElementFactory.getElements(nodes.all(), internalStorage, internalFluidStorage); } @Override @@ -1329,18 +304,8 @@ public class CraftingTask implements ICraftingTask { } @Override - public long getExecutionStarted() { - return executionStarted; - } - - @Override - public IStackList getMissing() { - return missing; - } - - @Override - public IStackList getMissingFluids() { - return missingFluids; + public long getStartTime() { + return startTime; } @Override @@ -1349,7 +314,13 @@ public class CraftingTask implements ICraftingTask { } @Override - public CraftingTaskState getState() { - return state; + public void onAllDone(Node node) { + nodes.remove(node); + } + + @Override + public void onSingleDone(Node node) { + currentStep++; + network.getCraftingManager().onTaskChanged(); } } diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTaskError.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTaskError.java deleted file mode 100644 index 6f42e8875..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTaskError.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; - -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; -import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskErrorType; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTaskError; - -import javax.annotation.Nullable; - -public class CraftingTaskError implements ICraftingTaskError { - private final CraftingTaskErrorType type; - private ICraftingPattern recursedPattern; - - public CraftingTaskError(CraftingTaskErrorType type) { - this.type = type; - } - - public CraftingTaskError(CraftingTaskErrorType type, ICraftingPattern recursedPattern) { - this.type = type; - this.recursedPattern = recursedPattern; - } - - @Override - public CraftingTaskErrorType getType() { - return type; - } - - @Override - @Nullable - public ICraftingPattern getRecursedPattern() { - return recursedPattern; - } -} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTaskFactory.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTaskFactory.java index 91f382427..5287d6e5f 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTaskFactory.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/CraftingTaskFactory.java @@ -2,23 +2,19 @@ package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; import com.refinedmods.refinedstorage.RS; import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; -import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingRequestInfo; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTask; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTaskFactory; +import com.refinedmods.refinedstorage.api.autocrafting.task.*; import com.refinedmods.refinedstorage.api.network.INetwork; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.calculator.CraftingCalculator; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.ResourceLocation; -import javax.annotation.Nonnull; - public class CraftingTaskFactory implements ICraftingTaskFactory { public static final ResourceLocation ID = new ResourceLocation(RS.ID, "v6"); - @Nonnull @Override - public ICraftingTask create(INetwork network, ICraftingRequestInfo requested, int quantity, ICraftingPattern pattern) { - return new CraftingTask(network, requested, quantity, pattern); + public ICalculationResult create(INetwork network, ICraftingRequestInfo requested, int quantity, ICraftingPattern pattern) { + CraftingCalculator calculator = new CraftingCalculator(network, requested, quantity, pattern); + return calculator.calculate(); } @Override diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/IoUtil.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/IoUtil.java new file mode 100644 index 000000000..2cbfc4f30 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/IoUtil.java @@ -0,0 +1,187 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; + +import com.refinedmods.refinedstorage.api.network.INetwork; +import com.refinedmods.refinedstorage.api.storage.disk.IStorageDisk; +import com.refinedmods.refinedstorage.api.util.Action; +import com.refinedmods.refinedstorage.api.util.IComparer; +import com.refinedmods.refinedstorage.api.util.IStackList; +import com.refinedmods.refinedstorage.api.util.StackListEntry; +import com.refinedmods.refinedstorage.apiimpl.API; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.items.IItemHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class IoUtil { + private static final Logger LOGGER = LogManager.getLogger(IoUtil.class); + + private static final int DEFAULT_EXTRACT_FLAGS = IComparer.COMPARE_NBT; + + public static IStackList extractFromInternalItemStorage(IStackList list, IStorageDisk storage, Action action) { + IStackList extracted = API.instance().createItemStackList(); + + for (StackListEntry entry : list.getStacks()) { + ItemStack result = storage.extract(entry.getStack(), entry.getStack().getCount(), DEFAULT_EXTRACT_FLAGS, action); + + if (result.isEmpty() || result.getCount() != entry.getStack().getCount()) { + if (action == Action.PERFORM) { + throw new IllegalStateException("The internal crafting inventory reported that " + entry.getStack() + " was available but we got " + result); + } + + return null; + } + + extracted.add(result); + } + + return extracted; + } + + public static IStackList extractFromInternalFluidStorage(IStackList list, IStorageDisk storage, Action action) { + IStackList extracted = API.instance().createFluidStackList(); + + for (StackListEntry entry : list.getStacks()) { + FluidStack result = storage.extract(entry.getStack(), entry.getStack().getAmount(), DEFAULT_EXTRACT_FLAGS, action); + + if (result.isEmpty() || result.getAmount() != entry.getStack().getAmount()) { + if (action == Action.PERFORM) { + throw new IllegalStateException("The internal crafting inventory reported that " + entry.getStack() + " was available but we got " + result); + } + + return null; + } + + extracted.add(result); + } + + return extracted; + } + + public static boolean insertIntoInventory(@Nullable IItemHandler dest, Collection> toInsert, Action action) { + if (dest == null) { + return false; + } + + if (toInsert.isEmpty()) { + return true; + } + + Deque> stacks = new ArrayDeque<>(toInsert); + + StackListEntry currentEntry = stacks.poll(); + + ItemStack current = currentEntry != null ? currentEntry.getStack() : null; + + List availableSlots = IntStream.range(0, dest.getSlots()).boxed().collect(Collectors.toList()); + + while (current != null && !availableSlots.isEmpty()) { + ItemStack remainder = ItemStack.EMPTY; + + for (int i = 0; i < availableSlots.size(); ++i) { + int slot = availableSlots.get(i); + + // .copy() is mandatory! + remainder = dest.insertItem(slot, current.copy(), action == Action.SIMULATE); + + // If we inserted *something* + if (remainder.isEmpty() || current.getCount() != remainder.getCount()) { + availableSlots.remove(i); + break; + } + } + + if (remainder.isEmpty()) { // If we inserted successfully, get a next stack. + currentEntry = stacks.poll(); + + current = currentEntry != null ? currentEntry.getStack() : null; + } else if (current.getCount() == remainder.getCount()) { // If we didn't insert anything over ALL these slots, stop here. + break; + } else { // If we didn't insert all, continue with other slots and use our remainder. + current = remainder; + } + } + + boolean success = current == null && stacks.isEmpty(); + + if (!success && action == Action.PERFORM) { + LOGGER.warn("Inventory unexpectedly didn't accept " + (current != null ? current.getTranslationKey() : null) + ", the remainder has been voided!"); + } + + return success; + } + + public static boolean insertIntoInventory(IFluidHandler dest, Collection> toInsert, Action action) { + for (StackListEntry entry : toInsert) { + int filled = dest.fill(entry.getStack(), action == Action.SIMULATE ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE); + + if (filled != entry.getStack().getAmount()) { + if (action == Action.PERFORM) { + LOGGER.warn("Inventory unexpectedly didn't accept all of " + entry.getStack().getTranslationKey() + ", the remainder has been voided!"); + } + + return false; + } + } + + return true; + } + + public static void extractItemsFromNetwork(IStackList toExtractInitial, INetwork network, IStorageDisk internalStorage) { + if (toExtractInitial.isEmpty()) { + return; + } + + List toRemove = new ArrayList<>(); + + for (StackListEntry toExtract : toExtractInitial.getStacks()) { + ItemStack result = network.extractItem(toExtract.getStack(), toExtract.getStack().getCount(), Action.PERFORM); + + if (!result.isEmpty()) { + internalStorage.insert(toExtract.getStack(), result.getCount(), Action.PERFORM); + + toRemove.add(result); + } + } + + for (ItemStack stack : toRemove) { + toExtractInitial.remove(stack); + } + + if (!toRemove.isEmpty()) { + network.getCraftingManager().onTaskChanged(); + } + } + + public static void extractFluidsFromNetwork(IStackList toExtractInitial, INetwork network, IStorageDisk internalStorage) { + if (toExtractInitial.isEmpty()) { + return; + } + + List toRemove = new ArrayList<>(); + + for (StackListEntry toExtract : toExtractInitial.getStacks()) { + FluidStack result = network.extractFluid(toExtract.getStack(), toExtract.getStack().getAmount(), Action.PERFORM); + + if (!result.isEmpty()) { + internalStorage.insert(toExtract.getStack(), result.getAmount(), Action.PERFORM); + + toRemove.add(result); + } + } + + for (FluidStack stack : toRemove) { + toExtractInitial.remove(stack); + } + + if (!toRemove.isEmpty()) { + network.getCraftingManager().onTaskChanged(); + } + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/Processing.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/Processing.java deleted file mode 100644 index 6f8c78e55..000000000 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/Processing.java +++ /dev/null @@ -1,189 +0,0 @@ -package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; - -import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; -import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; -import com.refinedmods.refinedstorage.api.network.INetwork; -import com.refinedmods.refinedstorage.api.util.IStackList; -import com.refinedmods.refinedstorage.api.util.StackListEntry; -import com.refinedmods.refinedstorage.apiimpl.API; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompoundNBT; -import net.minecraftforge.common.util.Constants; -import net.minecraftforge.fluids.FluidStack; - -class Processing extends Craft { - private static final String NBT_ITEMS_TO_RECEIVE = "ItemsToReceive"; - private static final String NBT_FLUIDS_TO_RECEIVE = "FluidsToReceive"; - private static final String NBT_FLUIDS_TO_USE = "FluidsToUse"; - private static final String NBT_STATE = "State"; - private static final String NBT_QUANTITY_TOTAL = "TotalQuantity"; - private static final String NBT_ITEMS_RECEIVED = "ItemsReceived"; - private static final String NBT_FLUIDS_RECEIVED = "FluidsReceived"; - private static final String NBT_ITEMS_TO_DISPLAY = "ItemsToDisplay"; - - private IStackList itemsToReceive = API.instance().createItemStackList(); - private IStackList itemsReceived = API.instance().createItemStackList(); - private IStackList fluidsToReceive = API.instance().createFluidStackList(); - private IStackList fluidsReceived = API.instance().createFluidStackList(); - private IStackList fluidsToUse = API.instance().createFluidStackList(); - private IStackList itemsToDisplay; - private ProcessingState state = ProcessingState.READY; - - private int finished; - private int totalQuantity; - - Processing(ICraftingPattern pattern, boolean root) { - super(pattern, root); - } - - Processing(INetwork network, CompoundNBT tag) throws CraftingTaskReadException { - super(network, tag); - this.itemsToReceive = CraftingTask.readItemStackList(tag.getList(NBT_ITEMS_TO_RECEIVE, Constants.NBT.TAG_COMPOUND)); - this.fluidsToReceive = CraftingTask.readFluidStackList(tag.getList(NBT_FLUIDS_TO_RECEIVE, Constants.NBT.TAG_COMPOUND)); - this.state = ProcessingState.values()[tag.getInt(NBT_STATE)]; - this.totalQuantity = tag.getInt(NBT_QUANTITY_TOTAL); - this.itemsReceived = CraftingTask.readItemStackList(tag.getList(NBT_ITEMS_RECEIVED, Constants.NBT.TAG_COMPOUND)); - this.fluidsReceived = CraftingTask.readFluidStackList(tag.getList(NBT_FLUIDS_RECEIVED, Constants.NBT.TAG_COMPOUND)); - this.fluidsToUse = CraftingTask.readFluidStackList(tag.getList(NBT_FLUIDS_TO_USE, Constants.NBT.TAG_COMPOUND)); - this.itemsToDisplay = CraftingTask.readItemStackList(tag.getList(NBT_ITEMS_TO_DISPLAY, Constants.NBT.TAG_COMPOUND)); - } - - @Override - void finishCalculation() { - this.totalQuantity = quantity; - updateItemsToDisplay(); - } - - int getNeeded(ItemStack stack) { - if (itemsToReceive.get(stack) != null) { - int needed = itemsToReceive.get(stack).getCount() * totalQuantity; - if (itemsReceived.get(stack) != null) { - needed -= itemsReceived.get(stack).getCount(); - } - return needed; - } - return 0; - } - - int getNeeded(FluidStack stack) { - if (fluidsToReceive.get(stack) != null) { - int needed = fluidsToReceive.get(stack).getAmount() * totalQuantity; - if (fluidsReceived.get(stack) != null) { - needed -= fluidsReceived.get(stack).getAmount(); - } - return needed; - } - return 0; - } - - boolean updateFinished() { - int fin = finished; - updateFinishedPatterns(); - if (finished == totalQuantity) { - this.setState(ProcessingState.PROCESSED); - } - return fin != finished; - } - - /* - Calculates how many patterns were already finished - by calculating the number finished patterns for every output - and then taking the minimum of those - */ - private void updateFinishedPatterns() { - int temp = totalQuantity; - if (!itemsToReceive.isEmpty()) { - for (StackListEntry stack : itemsToReceive.getStacks()) { - if (itemsReceived.get(stack.getStack()) != null) { - if (temp > itemsReceived.get(stack.getStack()).getCount() / (itemsToReceive.get(stack.getStack()).getCount())) { - temp = itemsReceived.get(stack.getStack()).getCount() / (itemsToReceive.get(stack.getStack()).getCount()); - } - } else { - temp = 0; - } - } - } - if (!fluidsToReceive.isEmpty()) { - for (StackListEntry stack : fluidsToReceive.getStacks()) { - if (fluidsReceived.get(stack.getStack()) != null) { - if (temp > fluidsReceived.get(stack.getStack()).getAmount() / (fluidsToReceive.get(stack.getStack()).getAmount())) { - temp = fluidsReceived.get(stack.getStack()).getAmount() / (fluidsToReceive.get(stack.getStack()).getAmount()); - } - } else { - temp = 0; - } - } - } - finished = temp; - } - - IStackList getItemsToReceive() { - return itemsToReceive; - } - - IStackList getFluidsToReceive() { - return fluidsToReceive; - } - - IStackList getItemsToDisplay() { - return itemsToDisplay; - } - - private void updateItemsToDisplay() { - itemsToDisplay = getItemsToUse(true); - } - - IStackList getFluidsToUse() { - return fluidsToUse; - } - - void addFluidsToUse(FluidStack stack) { - fluidsToUse.add(stack); - } - - void addItemsToReceive(ItemStack stack) { - itemsToReceive.add(stack); - } - - void addFluidsToReceive(FluidStack stack) { - fluidsToReceive.add(stack); - } - - int getProcessing() { - return totalQuantity - quantity - finished; - } - - void addFinished(ItemStack received, int size) { - itemsReceived.add(received, size); - } - - void addFinished(FluidStack received, int size) { - fluidsReceived.add(received, size); - } - - void setState(ProcessingState state) { - this.state = state; - } - - ProcessingState getState() { - return state; - } - - boolean hasFluids() { - return !fluidsToUse.isEmpty(); - } - - CompoundNBT writeToNbt() { - CompoundNBT tag = super.writeToNbt(); - tag.put(NBT_ITEMS_TO_RECEIVE, CraftingTask.writeItemStackList(itemsToReceive)); - tag.put(NBT_FLUIDS_TO_RECEIVE, CraftingTask.writeFluidStackList(fluidsToReceive)); - tag.putInt(NBT_STATE, state.ordinal()); - tag.putInt(NBT_QUANTITY_TOTAL, totalQuantity); - tag.put(NBT_ITEMS_RECEIVED, CraftingTask.writeItemStackList(itemsReceived)); - tag.put(NBT_FLUIDS_RECEIVED, CraftingTask.writeFluidStackList(fluidsReceived)); - tag.put(NBT_FLUIDS_TO_USE, CraftingTask.writeFluidStackList(fluidsToUse)); - tag.put(NBT_ITEMS_TO_DISPLAY, CraftingTask.writeItemStackList(itemsToDisplay)); - - return tag; - } -} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/SerializationUtil.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/SerializationUtil.java new file mode 100644 index 000000000..5321e614d --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/SerializationUtil.java @@ -0,0 +1,102 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; + +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternContainer; +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternProvider; +import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; +import com.refinedmods.refinedstorage.api.network.node.INetworkNode; +import com.refinedmods.refinedstorage.api.util.IStackList; +import com.refinedmods.refinedstorage.api.util.StackListEntry; +import com.refinedmods.refinedstorage.apiimpl.API; +import com.refinedmods.refinedstorage.util.StackUtils; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.fluids.FluidStack; + +public class SerializationUtil { + private static final String NBT_PATTERN_STACK = "Stack"; + private static final String NBT_PATTERN_CONTAINER_POS = "ContainerPos"; + + public static ListNBT writeItemStackList(IStackList stacks) { + ListNBT list = new ListNBT(); + + for (StackListEntry entry : stacks.getStacks()) { + list.add(StackUtils.serializeStackToNbt(entry.getStack())); + } + + return list; + } + + public static IStackList readItemStackList(ListNBT list) throws CraftingTaskReadException { + IStackList stacks = API.instance().createItemStackList(); + + for (int i = 0; i < list.size(); ++i) { + ItemStack stack = StackUtils.deserializeStackFromNbt(list.getCompound(i)); + + if (stack.isEmpty()) { + throw new CraftingTaskReadException("Empty stack!"); + } + + stacks.add(stack); + } + + return stacks; + } + + public static ListNBT writeFluidStackList(IStackList stacks) { + ListNBT list = new ListNBT(); + + for (StackListEntry entry : stacks.getStacks()) { + list.add(entry.getStack().writeToNBT(new CompoundNBT())); + } + + return list; + } + + public static IStackList readFluidStackList(ListNBT list) throws CraftingTaskReadException { + IStackList stacks = API.instance().createFluidStackList(); + + for (int i = 0; i < list.size(); ++i) { + FluidStack stack = FluidStack.loadFluidStackFromNBT(list.getCompound(i)); + + if (stack.isEmpty()) { + throw new CraftingTaskReadException("Empty stack!"); + } + + stacks.add(stack); + } + + return stacks; + } + + public static CompoundNBT writePatternToNbt(ICraftingPattern pattern) { + CompoundNBT tag = new CompoundNBT(); + + tag.put(NBT_PATTERN_STACK, pattern.getStack().serializeNBT()); + tag.putLong(NBT_PATTERN_CONTAINER_POS, pattern.getContainer().getPosition().toLong()); + + return tag; + } + + public static ICraftingPattern readPatternFromNbt(CompoundNBT tag, World world) throws CraftingTaskReadException { + BlockPos containerPos = BlockPos.fromLong(tag.getLong(NBT_PATTERN_CONTAINER_POS)); + + INetworkNode node = API.instance().getNetworkNodeManager((ServerWorld) world).getNode(containerPos); + + if (node instanceof ICraftingPatternContainer) { + ItemStack stack = ItemStack.read(tag.getCompound(NBT_PATTERN_STACK)); + + if (stack.getItem() instanceof ICraftingPatternProvider) { + return ((ICraftingPatternProvider) stack.getItem()).create(world, stack, (ICraftingPatternContainer) node); + } else { + throw new CraftingTaskReadException("Pattern stack is not a crafting pattern provider"); + } + } else { + throw new CraftingTaskReadException("Crafting pattern container doesn't exist anymore"); + } + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/CalculationResult.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/CalculationResult.java new file mode 100644 index 000000000..57a7d3728 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/CalculationResult.java @@ -0,0 +1,66 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.calculator; + +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement; +import com.refinedmods.refinedstorage.api.autocrafting.task.CalculationResultType; +import com.refinedmods.refinedstorage.api.autocrafting.task.ICalculationResult; +import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTask; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +public class CalculationResult implements ICalculationResult { + private final CalculationResultType type; + private final ICraftingPattern recursedPattern; + private final List> previewElements; + private final ICraftingTask craftingTask; + + public CalculationResult(CalculationResultType type) { + this.type = type; + this.recursedPattern = null; + this.previewElements = Collections.emptyList(); + this.craftingTask = null; + } + + public CalculationResult(CalculationResultType type, ICraftingPattern recursedPattern) { + this.type = type; + this.recursedPattern = recursedPattern; + this.previewElements = Collections.emptyList(); + this.craftingTask = null; + } + + public CalculationResult(CalculationResultType type, List> previewElements, @Nullable ICraftingTask craftingTask) { + this.type = type; + this.recursedPattern = null; + this.previewElements = previewElements; + this.craftingTask = craftingTask; + } + + @Override + public CalculationResultType getType() { + return type; + } + + @Override + public List> getPreviewElements() { + return previewElements; + } + + @Nullable + @Override + public ICraftingTask getTask() { + return craftingTask; + } + + @Override + public boolean isOk() { + return type == CalculationResultType.OK; + } + + @Override + @Nullable + public ICraftingPattern getRecursedPattern() { + return recursedPattern; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/CraftingCalculator.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/CraftingCalculator.java new file mode 100644 index 000000000..c933490c3 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/CraftingCalculator.java @@ -0,0 +1,360 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.calculator; + +import com.refinedmods.refinedstorage.RS; +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement; +import com.refinedmods.refinedstorage.api.autocrafting.task.CalculationResultType; +import com.refinedmods.refinedstorage.api.autocrafting.task.ICalculationResult; +import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingRequestInfo; +import com.refinedmods.refinedstorage.api.network.INetwork; +import com.refinedmods.refinedstorage.api.util.IComparer; +import com.refinedmods.refinedstorage.api.util.IStackList; +import com.refinedmods.refinedstorage.apiimpl.API; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.CraftingPatternInputs; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.CraftingTask; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.CraftingNode; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.Node; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.NodeList; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.ProcessingNode; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.preview.CraftingPreviewElementFactory; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.preview.CraftingPreviewInfo; +import com.refinedmods.refinedstorage.util.StackUtils; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.items.ItemHandlerHelper; + +import javax.annotation.Nullable; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class CraftingCalculator { + private final INetwork network; + private final ICraftingRequestInfo requested; + private final int quantity; + private final ICraftingPattern pattern; + + private final Set patternsUsed = new HashSet<>(); + + private final CraftingPreviewInfo craftingPreviewInfo = new CraftingPreviewInfo(); + + private final NodeList nodes = new NodeList(); + + private final IStackList toExtractInitial = API.instance().createItemStackList(); + private final IStackList toExtractInitialFluids = API.instance().createFluidStackList(); + + private long calculationStarted = -1; + + public CraftingCalculator(INetwork network, ICraftingRequestInfo requested, int quantity, ICraftingPattern pattern) { + this.network = network; + this.requested = requested; + this.quantity = quantity; + this.pattern = pattern; + } + + public ICalculationResult calculate() { + this.calculationStarted = System.currentTimeMillis(); + + IStackList results = API.instance().createItemStackList(); + IStackList fluidResults = API.instance().createFluidStackList(); + + IStackList storageSource = network.getItemStorageCache().getList().copy(); + IStackList fluidStorageSource = network.getFluidStorageCache().getList().copy(); + + int qtyPerCraft = getQuantityPerCraft(requested.getItem(), requested.getFluid(), pattern); + int qty = ((quantity - 1) / qtyPerCraft) + 1; + + try { + calculateInternal(qty, storageSource, fluidStorageSource, results, fluidResults, pattern, true); + } catch (CraftingCalculatorException e) { + return new CalculationResult(e.getType(), e.getRecursedPattern()); + } + + if (requested.getItem() != null) { + craftingPreviewInfo.getToCraft().add(ItemHandlerHelper.copyStackWithSize(requested.getItem(), qty * qtyPerCraft)); + } else if (requested.getFluid() != null) { + craftingPreviewInfo.getToCraftFluids().add(StackUtils.copy(requested.getFluid(), qty * qtyPerCraft)); + } + + List> previewElements = new CraftingPreviewElementFactory().getElements(craftingPreviewInfo); + + if (craftingPreviewInfo.hasMissing()) { + return new CalculationResult(CalculationResultType.MISSING, previewElements, null); + } + + return new CalculationResult( + CalculationResultType.OK, + previewElements, + new CraftingTask(network, requested, quantity, pattern, nodes, toExtractInitial, toExtractInitialFluids) + ); + } + + private void calculateInternal( + int qty, + IStackList storageSource, + IStackList fluidStorageSource, + IStackList results, + IStackList fluidResults, + ICraftingPattern pattern, + boolean root) throws CraftingCalculatorException { + + if (System.currentTimeMillis() - calculationStarted > RS.SERVER_CONFIG.getAutocrafting().getCalculationTimeoutMs()) { + throw new CraftingCalculatorException(CalculationResultType.TOO_COMPLEX, null); + } + + if (!patternsUsed.add(pattern)) { + throw new CraftingCalculatorException(CalculationResultType.RECURSIVE, pattern); + } + + IStackList itemsToExtract = API.instance().createItemStackList(); + IStackList fluidsToExtract = API.instance().createFluidStackList(); + + CraftingPatternInputs inputs = new CraftingPatternInputs(pattern); + + Node node = nodes.createOrAddToExistingNode(pattern, root, inputs.getRecipe(), qty); + + calculateForItems(qty, storageSource, fluidStorageSource, results, fluidResults, itemsToExtract, inputs, node); + + if (node instanceof CraftingNode) { + ItemStack output = pattern.getOutput(inputs.getRecipe()); + results.add(output, output.getCount() * qty); + + for (ItemStack byproduct : pattern.getByproducts(inputs.getRecipe())) { + results.add(byproduct, byproduct.getCount() * qty); + } + } else if (node instanceof ProcessingNode) { + ProcessingNode processing = (ProcessingNode) node; + + calculateForFluids(qty, storageSource, fluidStorageSource, results, fluidResults, pattern, inputs, fluidsToExtract, processing); + + for (ItemStack output : pattern.getOutputs()) { + results.add(output, output.getCount() * qty); + } + + for (FluidStack output : pattern.getFluidOutputs()) { + fluidResults.add(output, output.getAmount() * qty); + } + } + + patternsUsed.remove(pattern); + } + + private void calculateForItems(int qty, + IStackList storageSource, + IStackList fluidStorageSource, + IStackList results, + IStackList fluidResults, + IStackList itemsToExtract, + CraftingPatternInputs inputs, + Node node) throws CraftingCalculatorException { + int ingredientNumber = -1; + + for (CraftingPatternInputs.Ingredient ingredient : inputs.getItemIngredients()) { + ingredientNumber++; + + PossibleInputs possibleInputs = new PossibleInputs<>(ingredient.getInputs()); + possibleInputs.sort(storageSource, results); + + ItemStack possibleInput = possibleInputs.get(); + + ItemStack fromSelf = results.get(possibleInput); + ItemStack fromNetwork = storageSource.get(possibleInput); + + int remaining = ingredient.getCount() * qty; + + if (remaining < 0) { // int overflow + throw new CraftingCalculatorException(CalculationResultType.TOO_COMPLEX); + } + + while (remaining > 0) { + if (fromSelf != null) { + int toTake = Math.min(remaining, fromSelf.getCount()); + + node.getRequirements().addItemRequirement(ingredientNumber, possibleInput, toTake, ingredient.getCount()); + + results.remove(fromSelf, toTake); + + remaining -= toTake; + + fromSelf = results.get(possibleInput); + } + + if (fromNetwork != null && remaining > 0) { + int toTake = Math.min(remaining, fromNetwork.getCount()); + + craftingPreviewInfo.getToTake().add(possibleInput, toTake); + + node.getRequirements().addItemRequirement(ingredientNumber, possibleInput, toTake, ingredient.getCount()); + + storageSource.remove(fromNetwork, toTake); + + remaining -= toTake; + + fromNetwork = storageSource.get(possibleInput); + + toExtractInitial.add(possibleInput, toTake); + } + + if (remaining > 0) { + ICraftingPattern subPattern = network.getCraftingManager().getPattern(possibleInput); + + if (subPattern != null) { + int qtyPerCraft = getQuantityPerCraft(possibleInput, null, subPattern); + int subQty = ((remaining - 1) / qtyPerCraft) + 1; + + calculateInternal(subQty, storageSource, fluidStorageSource, results, fluidResults, subPattern, false); + + fromSelf = results.get(possibleInput); + if (fromSelf == null) { + throw new IllegalStateException("Recursive calculation didn't yield anything"); + } + + fromNetwork = storageSource.get(possibleInput); + + // fromSelf contains the amount crafted after the loop. + craftingPreviewInfo.getToCraft().add(fromSelf.copy()); + } else { + if (!possibleInputs.cycle()) { + // Give up. + possibleInput = possibleInputs.get(); // Revert back to 0. + + craftingPreviewInfo.getMissing().add(possibleInput, remaining); + + itemsToExtract.add(possibleInput, remaining); + + remaining = 0; + } else { + // Retry with new input... + possibleInput = possibleInputs.get(); + + fromSelf = results.get(possibleInput); + fromNetwork = storageSource.get(possibleInput); + } + } + } + } + } + } + + private void calculateForFluids(int qty, + IStackList storageSource, + IStackList fluidStorageSource, + IStackList results, + IStackList fluidResults, + ICraftingPattern pattern, + CraftingPatternInputs inputs, + IStackList fluidsToExtract, + ProcessingNode node) throws CraftingCalculatorException { + int ingredientNumber = -1; + + for (CraftingPatternInputs.Ingredient ingredient : inputs.getFluidIngredients()) { + ingredientNumber++; + + PossibleInputs possibleInputs = new PossibleInputs<>(ingredient.getInputs()); + possibleInputs.sort(fluidStorageSource, fluidResults); + + FluidStack possibleInput = possibleInputs.get(); + + FluidStack fromSelf = fluidResults.get(possibleInput, IComparer.COMPARE_NBT); + FluidStack fromNetwork = fluidStorageSource.get(possibleInput, IComparer.COMPARE_NBT); + + int remaining = possibleInput.getAmount() * qty; + + if (remaining < 0) { // int overflow + throw new CraftingCalculatorException(CalculationResultType.TOO_COMPLEX); + } + + while (remaining > 0) { + if (fromSelf != null) { + int toTake = Math.min(remaining, fromSelf.getAmount()); + + node.getRequirements().addFluidRequirement(ingredientNumber, possibleInput, toTake, ingredient.getCount()); + + fluidResults.remove(possibleInput, toTake); + + remaining -= toTake; + + fromSelf = fluidResults.get(possibleInput, IComparer.COMPARE_NBT); + } + + if (fromNetwork != null && remaining > 0) { + int toTake = Math.min(remaining, fromNetwork.getAmount()); + + node.getRequirements().addFluidRequirement(ingredientNumber, possibleInput, toTake, ingredient.getCount()); + + craftingPreviewInfo.getToTakeFluids().add(possibleInput, toTake); + + fluidStorageSource.remove(fromNetwork, toTake); + + remaining -= toTake; + + fromNetwork = fluidStorageSource.get(possibleInput, IComparer.COMPARE_NBT); + + toExtractInitialFluids.add(possibleInput, toTake); + } + + if (remaining > 0) { + ICraftingPattern subPattern = network.getCraftingManager().getPattern(possibleInput); + + if (subPattern != null) { + int qtyPerCraft = getQuantityPerCraft(null, possibleInput, subPattern); + int subQty = ((remaining - 1) / qtyPerCraft) + 1; + + calculateInternal(subQty, storageSource, fluidStorageSource, results, fluidResults, subPattern, false); + + fromSelf = fluidResults.get(possibleInput, IComparer.COMPARE_NBT); + if (fromSelf == null) { + throw new IllegalStateException("Recursive fluid calculation didn't yield anything"); + } + + fromNetwork = fluidStorageSource.get(possibleInput, IComparer.COMPARE_NBT); + + // fromSelf contains the amount crafted after the loop. + craftingPreviewInfo.getToCraftFluids().add(fromSelf.copy()); + } else { + if (!possibleInputs.cycle()) { + // Give up. + possibleInput = possibleInputs.get(); // Revert back to 0. + + craftingPreviewInfo.getMissingFluids().add(possibleInput, remaining); + + fluidsToExtract.add(possibleInput, remaining); + + remaining = 0; + } else { + // Retry with new input... + possibleInput = possibleInputs.get(); + + fromSelf = fluidResults.get(possibleInput); + fromNetwork = fluidStorageSource.get(possibleInput); + } + } + } + } + } + } + + private int getQuantityPerCraft(@Nullable ItemStack item, @Nullable FluidStack fluid, ICraftingPattern pattern) { + int qty = 0; + + if (item != null) { + for (ItemStack output : pattern.getOutputs()) { + if (API.instance().getComparer().isEqualNoQuantity(output, item)) { + qty += output.getCount(); + + if (!pattern.isProcessing()) { + break; + } + } + } + } else if (fluid != null) { + for (FluidStack output : pattern.getFluidOutputs()) { + if (API.instance().getComparer().isEqual(output, fluid, IComparer.COMPARE_NBT)) { + qty += output.getAmount(); + } + } + } + + return qty; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/CraftingCalculatorException.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/CraftingCalculatorException.java new file mode 100644 index 000000000..1a6f07b2a --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/CraftingCalculatorException.java @@ -0,0 +1,31 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.calculator; + +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.autocrafting.task.CalculationResultType; + +import javax.annotation.Nullable; + +public class CraftingCalculatorException extends Exception { + private final CalculationResultType type; + @Nullable + private final ICraftingPattern recursedPattern; + + public CraftingCalculatorException(CalculationResultType type) { + this.type = type; + this.recursedPattern = null; + } + + public CraftingCalculatorException(CalculationResultType type, @Nullable ICraftingPattern recursedPattern) { + this.type = type; + this.recursedPattern = recursedPattern; + } + + public CalculationResultType getType() { + return type; + } + + @Nullable + public ICraftingPattern getRecursedPattern() { + return recursedPattern; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/PossibleInputs.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/PossibleInputs.java new file mode 100644 index 000000000..e1dc83bb1 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/calculator/PossibleInputs.java @@ -0,0 +1,48 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.calculator; + +import com.refinedmods.refinedstorage.api.util.IStackList; + +import java.util.ArrayList; +import java.util.List; + +public class PossibleInputs { + private final List possibilities; + private int pos; + + public PossibleInputs(List possibilities) { + this.possibilities = new ArrayList<>(possibilities); + } + + public T get() { + return possibilities.get(pos); + } + + // Return false if we're exhausted. + public boolean cycle() { + if (pos + 1 >= possibilities.size()) { + pos = 0; + + return false; + } + + pos++; + + return true; + } + + public void sort(IStackList mutatedStorage, IStackList results) { + possibilities.sort((a, b) -> { + int ar = mutatedStorage.getCount(a); + int br = mutatedStorage.getCount(b); + + return br - ar; + }); + + possibilities.sort((a, b) -> { + int ar = results.getCount(a); + int br = results.getCount(b); + + return br - ar; + }); + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/monitor/CraftingMonitorElementFactory.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/monitor/CraftingMonitorElementFactory.java new file mode 100644 index 000000000..54826aa40 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/monitor/CraftingMonitorElementFactory.java @@ -0,0 +1,123 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.monitor; + +import com.refinedmods.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorElement; +import com.refinedmods.refinedstorage.api.autocrafting.craftingmonitor.ICraftingMonitorElementList; +import com.refinedmods.refinedstorage.api.storage.disk.IStorageDisk; +import com.refinedmods.refinedstorage.api.util.StackListEntry; +import com.refinedmods.refinedstorage.apiimpl.API; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.craftingmonitor.ErrorCraftingMonitorElement; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.craftingmonitor.FluidCraftingMonitorElement; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.craftingmonitor.ItemCraftingMonitorElement; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.CraftingNode; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.Node; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.ProcessingNode; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node.ProcessingState; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import java.util.Collection; +import java.util.List; + +public class CraftingMonitorElementFactory { + public List getElements(Collection nodes, IStorageDisk internalStorage, IStorageDisk internalFluidStorage) { + ICraftingMonitorElementList list = API.instance().createCraftingMonitorElementList(); + + for (Node node : nodes) { + if (node instanceof CraftingNode) { + addForRecipe(list, (CraftingNode) node); + } else { + addForProcessing(list, (ProcessingNode) node); + } + } + + for (ItemStack stack : internalStorage.getStacks()) { + list.addStorage(new ItemCraftingMonitorElement(stack, stack.getCount(), 0, 0, 0, 0)); + } + + for (FluidStack stack : internalFluidStorage.getStacks()) { + list.addStorage(new FluidCraftingMonitorElement(stack, stack.getAmount(), 0, 0, 0, 0)); + } + + list.commit(); + + return list.getElements(); + } + + private void addForProcessing(ICraftingMonitorElementList list, ProcessingNode node) { + if (node.getState() == ProcessingState.PROCESSED) { + return; + } + + for (StackListEntry requirement : node.getSingleItemSetToRequire().getStacks()) { + if (node.getCurrentlyProcessing() > 0) { + ICraftingMonitorElement element = ItemCraftingMonitorElement.Builder + .forStack(requirement.getStack()) + .processing(requirement.getStack().getCount() * node.getCurrentlyProcessing()) + .build(); + + list.add(element, true); + } + } + + for (StackListEntry toReceive : node.getSingleItemSetToReceive().getStacks()) { + int needed = node.getNeeded(toReceive.getStack()); + + if (needed > 0) { + ICraftingMonitorElement element = ItemCraftingMonitorElement.Builder + .forStack(toReceive.getStack()) + .scheduled(needed) + .build(); + + element = wrapWithProcessingState(element, node.getState(), "item"); + + list.add(element, true); + } + } + + for (StackListEntry requirement : node.getSingleFluidSetToRequire().getStacks()) { + if (node.getCurrentlyProcessing() > 0) { + ICraftingMonitorElement element = FluidCraftingMonitorElement.Builder + .forStack(requirement.getStack()) + .processing(requirement.getStack().getAmount() * node.getCurrentlyProcessing()) + .build(); + + list.add(element, true); + } + } + + for (StackListEntry toReceive : node.getSingleFluidSetToReceive().getStacks()) { + int needed = node.getNeeded(toReceive.getStack()); + + if (needed > 0) { + ICraftingMonitorElement element = FluidCraftingMonitorElement.Builder + .forStack(toReceive.getStack()) + .scheduled(needed) + .build(); + + element = wrapWithProcessingState(element, node.getState(), "fluid"); + + list.add(element, true); + } + } + } + + private ICraftingMonitorElement wrapWithProcessingState(ICraftingMonitorElement element, ProcessingState state, String type) { + if (state == ProcessingState.MACHINE_DOES_NOT_ACCEPT) { + return new ErrorCraftingMonitorElement(element, "gui.refinedstorage.crafting_monitor.machine_does_not_accept_" + type); + } else if (state == ProcessingState.MACHINE_NONE) { + return new ErrorCraftingMonitorElement(element, "gui.refinedstorage.crafting_monitor.machine_none"); + } else if (state == ProcessingState.LOCKED) { + return new ErrorCraftingMonitorElement(element, "gui.refinedstorage.crafting_monitor.crafter_is_locked"); + } + + return element; + } + + private void addForRecipe(ICraftingMonitorElementList list, CraftingNode node) { + if (node.getQuantity() > 0) { + for (ItemStack receive : node.getPattern().getOutputs()) { + list.add(new ItemCraftingMonitorElement(receive, 0, 0, 0, 0, receive.getCount() * node.getQuantity()), false); + } + } + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/CraftingNode.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/CraftingNode.java new file mode 100644 index 000000000..7adafa245 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/CraftingNode.java @@ -0,0 +1,98 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node; + +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternContainer; +import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; +import com.refinedmods.refinedstorage.api.network.INetwork; +import com.refinedmods.refinedstorage.api.storage.disk.IStorageDisk; +import com.refinedmods.refinedstorage.api.util.Action; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.IoUtil; +import com.refinedmods.refinedstorage.util.StackUtils; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.util.NonNullList; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fluids.FluidStack; + +public class CraftingNode extends Node { + private static final String NBT_RECIPE = "Recipe"; + + private final NonNullList recipe; + + public CraftingNode(ICraftingPattern pattern, boolean root, NonNullList recipe) { + super(pattern, root); + + this.recipe = recipe; + } + + public CraftingNode(INetwork network, CompoundNBT tag) throws CraftingTaskReadException { + super(network, tag); + + this.recipe = NonNullList.create(); + + ListNBT tookList = tag.getList(NBT_RECIPE, Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < tookList.size(); ++i) { + recipe.add(StackUtils.deserializeStackFromNbt(tookList.getCompound(i))); + } + } + + @Override + public void update(INetwork network, int ticks, NodeList nodes, IStorageDisk internalStorage, IStorageDisk internalFluidStorage, NodeListener listener) { + for (ICraftingPatternContainer container : network.getCraftingManager().getAllContainers(getPattern())) { + int interval = container.getUpdateInterval(); + if (interval < 0) { + throw new IllegalStateException(container + " has an update interval of < 0"); + } + + if (interval == 0 || ticks % interval == 0) { + for (int i = 0; i < container.getMaximumSuccessfulCraftingUpdates(); i++) { + if (IoUtil.extractFromInternalItemStorage(requirements.getSingleItemRequirementSet(true), internalStorage, Action.SIMULATE) != null) { + IoUtil.extractFromInternalItemStorage(requirements.getSingleItemRequirementSet(false), internalStorage, Action.PERFORM); + + ItemStack output = getPattern().getOutput(recipe); + + if (!isRoot()) { + internalStorage.insert(output, output.getCount(), Action.PERFORM); + } else { + ItemStack remainder = network.insertItem(output, output.getCount(), Action.PERFORM); + + internalStorage.insert(remainder, remainder.getCount(), Action.PERFORM); + } + + // Byproducts need to always be inserted in the internal storage for later reuse further in the task. + // Regular outputs can be inserted into the network *IF* it's a root since it's *NOT* expected to be used later on. + for (ItemStack byp : getPattern().getByproducts(recipe)) { + internalStorage.insert(byp, byp.getCount(), Action.PERFORM); + } + + next(); + + listener.onSingleDone(this); + + if (getQuantity() <= 0) { + listener.onAllDone(this); + return; + } + } else { + break; + } + } + } + } + } + + @Override + public CompoundNBT writeToNbt() { + CompoundNBT tag = super.writeToNbt(); + + ListNBT tookList = new ListNBT(); + for (ItemStack took : this.recipe) { + tookList.add(StackUtils.serializeStackToNbt(took)); + } + + tag.put(NBT_RECIPE, tookList); + + return tag; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/Node.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/Node.java new file mode 100644 index 000000000..1bd14b6d3 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/Node.java @@ -0,0 +1,86 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node; + +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; +import com.refinedmods.refinedstorage.api.network.INetwork; +import com.refinedmods.refinedstorage.api.storage.disk.IStorageDisk; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.SerializationUtil; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraftforge.fluids.FluidStack; + +public abstract class Node { + private static final String NBT_PATTERN = "Pattern"; + private static final String NBT_ROOT = "Root"; + private static final String NBT_IS_PROCESSING = "IsProcessing"; + private static final String NBT_QUANTITY = "Quantity"; + private static final String NBT_QUANTITY_TOTAL = "TotalQuantity"; + + private final boolean root; + private final ICraftingPattern pattern; + + protected int quantity; + protected int totalQuantity; + + protected final NodeRequirements requirements = new NodeRequirements(); + + public Node(ICraftingPattern pattern, boolean root) { + this.pattern = pattern; + this.root = root; + } + + public Node(INetwork network, CompoundNBT tag) throws CraftingTaskReadException { + this.quantity = tag.getInt(NBT_QUANTITY); + this.totalQuantity = tag.getInt(NBT_QUANTITY_TOTAL); + this.pattern = SerializationUtil.readPatternFromNbt(tag.getCompound(NBT_PATTERN), network.getWorld()); + this.root = tag.getBoolean(NBT_ROOT); + this.requirements.readFromNbt(tag); + } + + public static Node fromNbt(INetwork network, CompoundNBT tag) throws CraftingTaskReadException { + return tag.getBoolean(NBT_IS_PROCESSING) ? new ProcessingNode(network, tag) : new CraftingNode(network, tag); + } + + public abstract void update(INetwork network, int ticks, NodeList nodes, IStorageDisk internalStorage, IStorageDisk internalFluidStorage, NodeListener listener); + + public void onCalculationFinished() { + this.totalQuantity = quantity; + } + + public ICraftingPattern getPattern() { + return pattern; + } + + public int getQuantity() { + return quantity; + } + + public void addQuantity(int quantity) { + this.quantity += quantity; + } + + protected void next() { + quantity--; + } + + public boolean isRoot() { + return root; + } + + public NodeRequirements getRequirements() { + return requirements; + } + + public CompoundNBT writeToNbt() { + CompoundNBT tag = new CompoundNBT(); + + tag.putInt(NBT_QUANTITY, quantity); + tag.putInt(NBT_QUANTITY_TOTAL, totalQuantity); + tag.putBoolean(NBT_IS_PROCESSING, this instanceof ProcessingNode); + tag.putBoolean(NBT_ROOT, root); + tag.put(NBT_PATTERN, SerializationUtil.writePatternToNbt(pattern)); + tag = requirements.writeToNbt(tag); + + return tag; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/NodeList.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/NodeList.java new file mode 100644 index 000000000..6d59277cc --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/NodeList.java @@ -0,0 +1,60 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node; + +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternContainer; +import com.refinedmods.refinedstorage.api.network.INetwork; +import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; + +import java.util.*; + +public class NodeList { + private final Map nodes = new LinkedHashMap<>(); + private final List nodesToRemove = new ArrayList<>(); + + public void removeMarkedForRemoval() { + for (Node node : nodesToRemove) { + nodes.remove(node.getPattern()); + } + nodesToRemove.clear(); + } + + public Collection all() { + return nodes.values(); + } + + public void unlockAll(INetwork network) { + for (Node node : nodes.values()) { + if (node instanceof ProcessingNode) { + network.getCraftingManager().getAllContainers(node.getPattern()).forEach(ICraftingPatternContainer::unlock); + } + } + } + + public boolean isEmpty() { + return nodes.isEmpty(); + } + + public void remove(Node node) { + nodesToRemove.add(node); + } + + public Node createOrAddToExistingNode(ICraftingPattern pattern, boolean root, NonNullList recipe, int qty) { + Node node = nodes.get(pattern); + if (node == null) { + nodes.put(pattern, node = createNode(pattern, root, recipe)); + } + + node.addQuantity(qty); + + return node; + } + + private Node createNode(ICraftingPattern pattern, boolean root, NonNullList recipe) { + return pattern.isProcessing() ? new ProcessingNode(pattern, root) : new CraftingNode(pattern, root, recipe); + } + + public void put(ICraftingPattern pattern, Node node) { + nodes.put(pattern, node); + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/NodeListener.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/NodeListener.java new file mode 100644 index 000000000..13f45393d --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/NodeListener.java @@ -0,0 +1,7 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node; + +public interface NodeListener { + void onAllDone(Node node); + + void onSingleDone(Node node); +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/NodeRequirements.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/NodeRequirements.java new file mode 100644 index 000000000..e9e291168 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/NodeRequirements.java @@ -0,0 +1,176 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node; + +import com.google.common.primitives.Ints; +import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; +import com.refinedmods.refinedstorage.api.util.IStackList; +import com.refinedmods.refinedstorage.api.util.StackListEntry; +import com.refinedmods.refinedstorage.apiimpl.API; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.SerializationUtil; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fluids.FluidStack; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class NodeRequirements { + private static final String NBT_ITEMS_TO_USE = "ItemsToUse"; + private static final String NBT_FLUIDS_TO_USE = "FluidsToUse"; + + private static final String NBT_ITEMS_NEEDED_PER_CRAFT = "ItemsNeededPerCraft"; + private static final String NBT_FLUIDS_NEEDED_PER_CRAFT = "FluidsNeededPerCraft"; + + private final Map> itemRequirements = new LinkedHashMap<>(); + private final Map itemsNeededPerCraft = new LinkedHashMap<>(); + + private final Map> fluidRequirements = new LinkedHashMap<>(); + private final Map fluidsNeededPerCraft = new LinkedHashMap<>(); + + public void addItemRequirement(int ingredientNumber, ItemStack stack, int size, int perCraft) { + if (!itemsNeededPerCraft.containsKey(ingredientNumber)) { + itemsNeededPerCraft.put(ingredientNumber, perCraft); + } + + IStackList list = itemRequirements.get(ingredientNumber); + if (list == null) { + itemRequirements.put(ingredientNumber, list = API.instance().createItemStackList()); + } + + list.add(stack, size); + } + + public void addFluidRequirement(int ingredientNumber, FluidStack stack, int size, int perCraft) { + if (!fluidsNeededPerCraft.containsKey(ingredientNumber)) { + fluidsNeededPerCraft.put(ingredientNumber, perCraft); + } + + IStackList list = fluidRequirements.get(ingredientNumber); + if (list == null) { + fluidRequirements.put(ingredientNumber, list = API.instance().createFluidStackList()); + } + + list.add(stack, size); + } + + public IStackList getSingleItemRequirementSet(boolean simulate) { + IStackList toReturn = API.instance().createItemStackList(); + + for (int i = 0; i < itemRequirements.size(); i++) { + int needed = itemsNeededPerCraft.get(i); + + if (!itemRequirements.get(i).isEmpty()) { + Iterator> it = itemRequirements.get(i).getStacks().iterator(); + + while (needed > 0 && it.hasNext()) { + ItemStack toUse = it.next().getStack(); + + if (needed < toUse.getCount()) { + if (!simulate) { + itemRequirements.get(i).remove(toUse, needed); + } + + toReturn.add(toUse, needed); + + needed = 0; + } else { + if (!simulate) { + it.remove(); + } + + toReturn.add(toUse); + + needed -= toUse.getCount(); + } + } + } else { + throw new IllegalStateException("Bad!"); + } + } + + return toReturn; + } + + public IStackList getSingleFluidRequirementSet(boolean simulate) { + IStackList toReturn = API.instance().createFluidStackList(); + + for (int i = 0; i < fluidRequirements.size(); i++) { + int needed = fluidsNeededPerCraft.get(i); + + if (!fluidRequirements.get(i).isEmpty()) { + Iterator> it = fluidRequirements.get(i).getStacks().iterator(); + + while (needed > 0 && it.hasNext()) { + FluidStack toUse = it.next().getStack(); + + if (needed < toUse.getAmount()) { + if (!simulate) { + fluidRequirements.get(i).remove(toUse, needed); + } + + toReturn.add(toUse, needed); + + needed = 0; + } else { + if (!simulate) { + it.remove(); + } + + toReturn.add(toUse); + + needed -= toUse.getAmount(); + } + } + } else { + throw new IllegalStateException("Bad!"); + } + } + + return toReturn; + } + + public void readFromNbt(CompoundNBT tag) throws CraftingTaskReadException { + ListNBT itemRequirements = tag.getList(NBT_ITEMS_TO_USE, Constants.NBT.TAG_LIST); + for (int i = 0; i < itemRequirements.size(); i++) { + this.itemRequirements.put(i, SerializationUtil.readItemStackList(itemRequirements.getList(i))); + } + + List itemsNeededPerCraft = Ints.asList(tag.getIntArray(NBT_ITEMS_NEEDED_PER_CRAFT)); + for (int i = 0; i < itemsNeededPerCraft.size(); i++) { + this.itemsNeededPerCraft.put(i, itemsNeededPerCraft.get(i)); + } + + ListNBT fluidRequirements = tag.getList(NBT_FLUIDS_TO_USE, Constants.NBT.TAG_LIST); + for (int i = 0; i < fluidRequirements.size(); i++) { + this.fluidRequirements.put(i, SerializationUtil.readFluidStackList(fluidRequirements.getList(i))); + } + + List fluidsNeededPerCraft = Ints.asList(tag.getIntArray(NBT_FLUIDS_NEEDED_PER_CRAFT)); + for (int i = 0; i < fluidsNeededPerCraft.size(); i++) { + this.fluidsNeededPerCraft.put(i, fluidsNeededPerCraft.get(i)); + } + } + + public CompoundNBT writeToNbt(CompoundNBT tag) { + ListNBT itemRequirements = new ListNBT(); + for (IStackList list : this.itemRequirements.values()) { + itemRequirements.add(SerializationUtil.writeItemStackList(list)); + } + tag.put(NBT_ITEMS_TO_USE, itemRequirements); + + tag.putIntArray(NBT_ITEMS_NEEDED_PER_CRAFT, Ints.toArray(itemsNeededPerCraft.values())); + + ListNBT fluidRequirements = new ListNBT(); + for (IStackList list : this.fluidRequirements.values()) { + fluidRequirements.add(SerializationUtil.writeFluidStackList(list)); + } + tag.put(NBT_FLUIDS_TO_USE, fluidRequirements); + + tag.putIntArray(NBT_FLUIDS_NEEDED_PER_CRAFT, Ints.toArray(fluidsNeededPerCraft.values())); + + return tag; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/ProcessingNode.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/ProcessingNode.java new file mode 100644 index 000000000..0c4ff7b1b --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/ProcessingNode.java @@ -0,0 +1,272 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node; + +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; +import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternContainer; +import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskReadException; +import com.refinedmods.refinedstorage.api.network.INetwork; +import com.refinedmods.refinedstorage.api.storage.disk.IStorageDisk; +import com.refinedmods.refinedstorage.api.util.Action; +import com.refinedmods.refinedstorage.api.util.IStackList; +import com.refinedmods.refinedstorage.api.util.StackListEntry; +import com.refinedmods.refinedstorage.apiimpl.API; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.IoUtil; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.SerializationUtil; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fluids.FluidStack; + +public class ProcessingNode extends Node { + private static final String NBT_ITEMS_RECEIVED = "ItemsReceived"; + private static final String NBT_FLUIDS_RECEIVED = "FluidsReceived"; + private static final String NBT_SINGLE_ITEM_SET_TO_REQUIRE = "SingleItemSetToRequire"; + private static final String NBT_SINGLE_FLUID_SET_TO_REQUIRE = "SingleFluidSetToRequire"; + private static final String NBT_STATE = "State"; + + private final IStackList singleItemSetToReceive = API.instance().createItemStackList(); + private final IStackList singleFluidSetToReceive = API.instance().createFluidStackList(); + + private IStackList singleItemSetToRequire; + private IStackList singleFluidSetToRequire; + + private IStackList itemsReceived = API.instance().createItemStackList(); + private IStackList fluidsReceived = API.instance().createFluidStackList(); + + private ProcessingState state; + + private int quantityFinished; + + public ProcessingNode(ICraftingPattern pattern, boolean root) { + super(pattern, root); + + initSetsToReceive(); + } + + public ProcessingNode(INetwork network, CompoundNBT tag) throws CraftingTaskReadException { + super(network, tag); + + this.itemsReceived = SerializationUtil.readItemStackList(tag.getList(NBT_ITEMS_RECEIVED, Constants.NBT.TAG_COMPOUND)); + this.fluidsReceived = SerializationUtil.readFluidStackList(tag.getList(NBT_FLUIDS_RECEIVED, Constants.NBT.TAG_COMPOUND)); + + this.singleItemSetToRequire = SerializationUtil.readItemStackList(tag.getList(NBT_SINGLE_ITEM_SET_TO_REQUIRE, Constants.NBT.TAG_COMPOUND)); + this.singleFluidSetToRequire = SerializationUtil.readFluidStackList(tag.getList(NBT_SINGLE_FLUID_SET_TO_REQUIRE, Constants.NBT.TAG_COMPOUND)); + + this.state = ProcessingState.values()[tag.getInt(NBT_STATE)]; + + initSetsToReceive(); + } + + private void initSetsToReceive() { + for (ItemStack output : getPattern().getOutputs()) { + singleItemSetToReceive.add(output, output.getCount()); + } + + for (FluidStack output : getPattern().getFluidOutputs()) { + singleFluidSetToReceive.add(output, output.getAmount()); + } + } + + @Override + public void update(INetwork network, int ticks, NodeList nodes, IStorageDisk internalStorage, IStorageDisk internalFluidStorage, NodeListener listener) { + if (state == ProcessingState.PROCESSED) { + listener.onAllDone(this); + return; + } + + if (getQuantity() <= 0) { + return; + } + + boolean allLocked = true; + boolean allMissingMachine = true; + boolean allRejected = true; + + ProcessingState originalState = state; + + for (ICraftingPatternContainer container : network.getCraftingManager().getAllContainers(getPattern())) { + int interval = container.getUpdateInterval(); + + if (interval < 0) { + throw new IllegalStateException(container + " has an update interval of < 0"); + } + + if (interval == 0 || ticks % interval == 0) { + for (int i = 0; i < container.getMaximumSuccessfulCraftingUpdates(); i++) { + if (getQuantity() <= 0) { + return; + } + + if (container.isLocked()) { + if (allLocked) { + this.state = ProcessingState.LOCKED; + } + + break; + } else { + allLocked = false; + } + + if ((!singleItemSetToReceive.isEmpty() && container.getConnectedInventory() == null) || + (!singleFluidSetToReceive.isEmpty() && container.getConnectedFluidInventory() == null)) { + if (allMissingMachine) { + this.state = ProcessingState.MACHINE_NONE; + } + + break; + } else { + allMissingMachine = false; + } + + boolean hasAllRequirements = false; + + IStackList extractedItems = IoUtil.extractFromInternalItemStorage(requirements.getSingleItemRequirementSet(true), internalStorage, Action.SIMULATE); + IStackList extractedFluids = null; + if (extractedItems != null) { + extractedFluids = IoUtil.extractFromInternalFluidStorage(requirements.getSingleFluidRequirementSet(true), internalFluidStorage, Action.SIMULATE); + if (extractedFluids != null) { + hasAllRequirements = true; + } + } + + boolean canInsertFullAmount = false; + if (hasAllRequirements) { + canInsertFullAmount = IoUtil.insertIntoInventory(container.getConnectedInventory(), extractedItems.getStacks(), Action.SIMULATE); + if (canInsertFullAmount) { + canInsertFullAmount = IoUtil.insertIntoInventory(container.getConnectedFluidInventory(), extractedFluids.getStacks(), Action.SIMULATE); + } + } + + if (hasAllRequirements && !canInsertFullAmount) { + if (allRejected) { + this.state = ProcessingState.MACHINE_DOES_NOT_ACCEPT; + } + + break; + } else { + allRejected = false; + } + + if (hasAllRequirements && canInsertFullAmount) { + this.state = ProcessingState.READY; + + extractedItems = IoUtil.extractFromInternalItemStorage(requirements.getSingleItemRequirementSet(false), internalStorage, Action.PERFORM); + extractedFluids = IoUtil.extractFromInternalFluidStorage(requirements.getSingleFluidRequirementSet(false), internalFluidStorage, Action.PERFORM); + + IoUtil.insertIntoInventory(container.getConnectedInventory(), extractedItems.getStacks(), Action.PERFORM); + IoUtil.insertIntoInventory(container.getConnectedFluidInventory(), extractedFluids.getStacks(), Action.PERFORM); + + next(); + + listener.onSingleDone(this); + + container.onUsedForProcessing(); + } + } + } + } + + if (originalState != state) { + network.getCraftingManager().onTaskChanged(); + } + } + + public ProcessingState getState() { + return state; + } + + public IStackList getSingleItemSetToReceive() { + return singleItemSetToReceive; + } + + public IStackList getSingleFluidSetToReceive() { + return singleFluidSetToReceive; + } + + public IStackList getSingleItemSetToRequire() { + return singleItemSetToRequire; + } + + public IStackList getSingleFluidSetToRequire() { + return singleFluidSetToRequire; + } + + public int getNeeded(ItemStack stack) { + return (singleItemSetToReceive.getCount(stack) * totalQuantity) - itemsReceived.getCount(stack); + } + + public int getNeeded(FluidStack stack) { + return (singleFluidSetToReceive.getCount(stack) * totalQuantity) - fluidsReceived.getCount(stack); + } + + public int getCurrentlyProcessing() { + int unprocessed = totalQuantity - quantity; + return unprocessed - quantityFinished; + } + + public void markReceived(ItemStack stack, int count) { + itemsReceived.add(stack, count); + updateFinishedQuantity(); + } + + public void markReceived(FluidStack stack, int count) { + fluidsReceived.add(stack, count); + updateFinishedQuantity(); + } + + public void updateFinishedQuantity() { + int quantityFinished = totalQuantity; + + for (StackListEntry toReceive : singleItemSetToReceive.getStacks()) { + if (itemsReceived.get(toReceive.getStack()) != null) { + int ratioReceived = itemsReceived.get(toReceive.getStack()).getCount() / toReceive.getStack().getCount(); + + if (quantityFinished > ratioReceived) { + quantityFinished = ratioReceived; + } + } else { + quantityFinished = 0; + } + } + + for (StackListEntry toReceive : singleFluidSetToReceive.getStacks()) { + if (fluidsReceived.get(toReceive.getStack()) != null) { + int ratioReceived = fluidsReceived.get(toReceive.getStack()).getAmount() / toReceive.getStack().getAmount(); + + if (quantityFinished > ratioReceived) { + quantityFinished = ratioReceived; + } + } else { + quantityFinished = 0; + } + } + + this.quantityFinished = quantityFinished; + + if (this.quantityFinished == this.totalQuantity) { + this.state = ProcessingState.PROCESSED; + } + } + + @Override + public void onCalculationFinished() { + super.onCalculationFinished(); + + this.singleItemSetToRequire = requirements.getSingleItemRequirementSet(true); + this.singleFluidSetToRequire = requirements.getSingleFluidRequirementSet(true); + } + + @Override + public CompoundNBT writeToNbt() { + CompoundNBT tag = super.writeToNbt(); + + tag.put(NBT_ITEMS_RECEIVED, SerializationUtil.writeItemStackList(itemsReceived)); + tag.put(NBT_FLUIDS_RECEIVED, SerializationUtil.writeFluidStackList(fluidsReceived)); + + tag.put(NBT_SINGLE_ITEM_SET_TO_REQUIRE, SerializationUtil.writeItemStackList(singleItemSetToRequire)); + tag.put(NBT_SINGLE_FLUID_SET_TO_REQUIRE, SerializationUtil.writeFluidStackList(singleFluidSetToRequire)); + + tag.putInt(NBT_STATE, state.ordinal()); + + return tag; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/ProcessingState.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/ProcessingState.java similarity index 79% rename from src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/ProcessingState.java rename to src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/ProcessingState.java index 9a9e2ec1c..2d905ac7a 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/ProcessingState.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/node/ProcessingState.java @@ -1,6 +1,6 @@ -package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6; +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.node; -enum ProcessingState { +public enum ProcessingState { READY, MACHINE_NONE, MACHINE_DOES_NOT_ACCEPT, diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/preview/CraftingPreviewElementFactory.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/preview/CraftingPreviewElementFactory.java new file mode 100644 index 000000000..594624e00 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/preview/CraftingPreviewElementFactory.java @@ -0,0 +1,115 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.preview; + +import com.google.common.collect.ImmutableList; +import com.refinedmods.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement; +import com.refinedmods.refinedstorage.api.util.StackListEntry; +import com.refinedmods.refinedstorage.apiimpl.API; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.preview.FluidCraftingPreviewElement; +import com.refinedmods.refinedstorage.apiimpl.autocrafting.preview.ItemCraftingPreviewElement; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class CraftingPreviewElementFactory { + public List> getElements(CraftingPreviewInfo info) { + Map map = new LinkedHashMap<>(); + Map mapFluids = new LinkedHashMap<>(); + + for (StackListEntry stack : info.getMissing().getStacks()) { + int hash = API.instance().getItemStackHashCode(stack.getStack()); + + ItemCraftingPreviewElement previewStack = map.get(hash); + + if (previewStack == null) { + previewStack = new ItemCraftingPreviewElement(stack.getStack()); + } + + previewStack.setMissing(true); + previewStack.addToCraft(stack.getStack().getCount()); + + map.put(hash, previewStack); + } + + for (StackListEntry stack : info.getMissingFluids().getStacks()) { + int hash = API.instance().getFluidStackHashCode(stack.getStack()); + + FluidCraftingPreviewElement previewStack = mapFluids.get(hash); + + if (previewStack == null) { + previewStack = new FluidCraftingPreviewElement(stack.getStack()); + } + + previewStack.setMissing(true); + previewStack.addToCraft(stack.getStack().getAmount()); + + mapFluids.put(hash, previewStack); + } + + for (ItemStack stack : ImmutableList.copyOf(info.getToCraft()).reverse()) { + int hash = API.instance().getItemStackHashCode(stack); + + ItemCraftingPreviewElement previewStack = map.get(hash); + + if (previewStack == null) { + previewStack = new ItemCraftingPreviewElement(stack.getStack()); + } + + previewStack.addToCraft(stack.getCount()); + + map.put(hash, previewStack); + } + + for (FluidStack stack : ImmutableList.copyOf(info.getToCraftFluids()).reverse()) { + int hash = API.instance().getFluidStackHashCode(stack); + + FluidCraftingPreviewElement previewStack = mapFluids.get(hash); + + if (previewStack == null) { + previewStack = new FluidCraftingPreviewElement(stack); + } + + previewStack.addToCraft(stack.getAmount()); + + mapFluids.put(hash, previewStack); + } + + for (StackListEntry stack : info.getToTake().getStacks()) { + int hash = API.instance().getItemStackHashCode(stack.getStack()); + + ItemCraftingPreviewElement previewStack = map.get(hash); + + if (previewStack == null) { + previewStack = new ItemCraftingPreviewElement(stack.getStack()); + } + + previewStack.addAvailable(stack.getStack().getCount()); + + map.put(hash, previewStack); + } + + for (StackListEntry stack : info.getToTakeFluids().getStacks()) { + int hash = API.instance().getFluidStackHashCode(stack.getStack()); + + FluidCraftingPreviewElement previewStack = mapFluids.get(hash); + + if (previewStack == null) { + previewStack = new FluidCraftingPreviewElement(stack.getStack()); + } + + previewStack.addAvailable(stack.getStack().getAmount()); + + mapFluids.put(hash, previewStack); + } + + List> elements = new ArrayList<>(); + + elements.addAll(map.values()); + elements.addAll(mapFluids.values()); + + return elements; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/preview/CraftingPreviewInfo.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/preview/CraftingPreviewInfo.java new file mode 100644 index 000000000..f10f32ce1 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/task/v6/preview/CraftingPreviewInfo.java @@ -0,0 +1,48 @@ +package com.refinedmods.refinedstorage.apiimpl.autocrafting.task.v6.preview; + +import com.refinedmods.refinedstorage.api.util.IStackList; +import com.refinedmods.refinedstorage.apiimpl.API; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import java.util.ArrayList; +import java.util.List; + +public class CraftingPreviewInfo { + private final IStackList missing = API.instance().createItemStackList(); + private final IStackList missingFluids = API.instance().createFluidStackList(); + + private final IStackList toTake = API.instance().createItemStackList(); + private final IStackList toTakeFluids = API.instance().createFluidStackList(); + + private final List toCraft = new ArrayList<>(); + private final List toCraftFluids = new ArrayList<>(); + + public IStackList getMissing() { + return missing; + } + + public IStackList getMissingFluids() { + return missingFluids; + } + + public boolean hasMissing() { + return !missing.isEmpty() || !missingFluids.isEmpty(); + } + + public IStackList getToTake() { + return toTake; + } + + public IStackList getToTakeFluids() { + return toTakeFluids; + } + + public List getToCraft() { + return toCraft; + } + + public List getToCraftFluids() { + return toCraftFluids; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/grid/handler/FluidGridHandler.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/grid/handler/FluidGridHandler.java index c0799f894..28b1c4306 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/grid/handler/FluidGridHandler.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/grid/handler/FluidGridHandler.java @@ -1,8 +1,8 @@ package com.refinedmods.refinedstorage.apiimpl.network.grid.handler; import com.refinedmods.refinedstorage.RS; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTask; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTaskError; +import com.refinedmods.refinedstorage.api.autocrafting.task.CalculationResultType; +import com.refinedmods.refinedstorage.api.autocrafting.task.ICalculationResult; import com.refinedmods.refinedstorage.api.network.INetwork; import com.refinedmods.refinedstorage.api.network.grid.handler.IFluidGridHandler; import com.refinedmods.refinedstorage.api.network.security.Permission; @@ -15,7 +15,6 @@ import com.refinedmods.refinedstorage.util.StackUtils; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.InventoryHelper; import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; import net.minecraftforge.fluids.FluidAttributes; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; @@ -101,36 +100,30 @@ public class FluidGridHandler implements IFluidGridHandler { if (stack != null) { Thread calculationThread = new Thread(() -> { - ICraftingTask task = network.getCraftingManager().create(stack, quantity); - if (task == null) { + ICalculationResult result = network.getCraftingManager().create(stack, quantity); + if (result == null) { return; } - ICraftingTaskError error = task.calculate(); - - ResourceLocation factoryId = task.getPattern().getCraftingTaskFactoryId(); - - if (error != null) { + if (!result.isOk() && result.getType() != CalculationResultType.MISSING) { RS.NETWORK_HANDLER.sendTo( player, new GridCraftingPreviewResponseMessage( - factoryId, - Collections.singletonList(new ErrorCraftingPreviewElement(error.getType(), error.getRecursedPattern() == null ? ItemStack.EMPTY : error.getRecursedPattern().getStack())), + Collections.singletonList(new ErrorCraftingPreviewElement(result.getType(), result.getRecursedPattern() == null ? ItemStack.EMPTY : result.getRecursedPattern().getStack())), id, quantity, true ) ); - } else if (noPreview && !task.hasMissing()) { - network.getCraftingManager().start(task); + } else if (result.isOk() && noPreview) { + network.getCraftingManager().start(result.getTask()); RS.NETWORK_HANDLER.sendTo(player, new GridCraftingStartResponseMessage()); } else { RS.NETWORK_HANDLER.sendTo( player, new GridCraftingPreviewResponseMessage( - factoryId, - task.getPreviewStacks(), + result.getPreviewElements(), id, quantity, true @@ -152,14 +145,9 @@ public class FluidGridHandler implements IFluidGridHandler { FluidStack stack = network.getFluidStorageCache().getCraftablesList().get(id); if (stack != null) { - ICraftingTask task = network.getCraftingManager().create(stack, quantity); - if (task == null) { - return; - } - - ICraftingTaskError error = task.calculate(); - if (error == null && !task.hasMissing()) { - network.getCraftingManager().start(task); + ICalculationResult result = network.getCraftingManager().create(stack, quantity); + if (result.isOk()) { + network.getCraftingManager().start(result.getTask()); } } } diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/grid/handler/ItemGridHandler.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/grid/handler/ItemGridHandler.java index 67ddb5d5f..c9fbdc545 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/grid/handler/ItemGridHandler.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/grid/handler/ItemGridHandler.java @@ -1,8 +1,8 @@ package com.refinedmods.refinedstorage.apiimpl.network.grid.handler; import com.refinedmods.refinedstorage.RS; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTask; -import com.refinedmods.refinedstorage.api.autocrafting.task.ICraftingTaskError; +import com.refinedmods.refinedstorage.api.autocrafting.task.CalculationResultType; +import com.refinedmods.refinedstorage.api.autocrafting.task.ICalculationResult; import com.refinedmods.refinedstorage.api.network.INetwork; import com.refinedmods.refinedstorage.api.network.grid.handler.IItemGridHandler; import com.refinedmods.refinedstorage.api.network.security.Permission; @@ -14,7 +14,6 @@ import com.refinedmods.refinedstorage.network.grid.GridCraftingStartResponseMess import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.Direction; -import net.minecraft.util.ResourceLocation; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; @@ -159,36 +158,27 @@ public class ItemGridHandler implements IItemGridHandler { if (stack != null) { Thread calculationThread = new Thread(() -> { - ICraftingTask task = network.getCraftingManager().create(stack, quantity); - if (task == null) { - return; - } + ICalculationResult result = network.getCraftingManager().create(stack, quantity); - ICraftingTaskError error = task.calculate(); - - ResourceLocation factoryId = task.getPattern().getCraftingTaskFactoryId(); - - if (error != null) { + if (!result.isOk() && result.getType() != CalculationResultType.MISSING) { RS.NETWORK_HANDLER.sendTo( player, new GridCraftingPreviewResponseMessage( - factoryId, - Collections.singletonList(new ErrorCraftingPreviewElement(error.getType(), error.getRecursedPattern() == null ? ItemStack.EMPTY : error.getRecursedPattern().getStack())), + Collections.singletonList(new ErrorCraftingPreviewElement(result.getType(), result.getRecursedPattern() == null ? ItemStack.EMPTY : result.getRecursedPattern().getStack())), id, quantity, false ) ); - } else if (noPreview && !task.hasMissing()) { - network.getCraftingManager().start(task); + } else if (result.isOk() && noPreview) { + network.getCraftingManager().start(result.getTask()); RS.NETWORK_HANDLER.sendTo(player, new GridCraftingStartResponseMessage()); } else { RS.NETWORK_HANDLER.sendTo( player, new GridCraftingPreviewResponseMessage( - factoryId, - task.getPreviewStacks(), + result.getPreviewElements(), id, quantity, false @@ -210,14 +200,9 @@ public class ItemGridHandler implements IItemGridHandler { ItemStack stack = network.getItemStorageCache().getCraftablesList().get(id); if (stack != null) { - ICraftingTask task = network.getCraftingManager().create(stack, quantity); - if (task == null) { - return; - } - - ICraftingTaskError error = task.calculate(); - if (error == null && !task.hasMissing()) { - network.getCraftingManager().start(task); + ICalculationResult result = network.getCraftingManager().create(stack, quantity); + if (result.isOk()) { + network.getCraftingManager().start(result.getTask()); } } } diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/util/FluidStackList.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/util/FluidStackList.java index f5e82f1fa..b08d518ee 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/util/FluidStackList.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/util/FluidStackList.java @@ -82,6 +82,16 @@ public class FluidStackList implements IStackList { return remove(stack, stack.getAmount()); } + @Override + public int getCount(@Nonnull FluidStack stack, int flags) { + FluidStack found = get(stack, flags); + if (found == null) { + return 0; + } + + return found.getAmount(); + } + @Override @Nullable public FluidStack get(@Nonnull FluidStack stack, int flags) { diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/util/ItemStackList.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/util/ItemStackList.java index 9d966c2fa..48b221c75 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/util/ItemStackList.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/util/ItemStackList.java @@ -80,6 +80,16 @@ public class ItemStackList implements IStackList { return remove(stack, stack.getCount()); } + @Override + public int getCount(@Nonnull ItemStack stack, int flags) { + ItemStack found = get(stack, flags); + if (found == null) { + return 0; + } + + return found.getCount(); + } + @Override @Nullable public ItemStack get(@Nonnull ItemStack stack, int flags) { diff --git a/src/main/java/com/refinedmods/refinedstorage/network/ClientProxy.java b/src/main/java/com/refinedmods/refinedstorage/network/ClientProxy.java index d1e94ef47..ff3c97c12 100644 --- a/src/main/java/com/refinedmods/refinedstorage/network/ClientProxy.java +++ b/src/main/java/com/refinedmods/refinedstorage/network/ClientProxy.java @@ -20,8 +20,7 @@ public class ClientProxy { Minecraft.getInstance().displayGuiScreen(new CraftingPreviewScreen( screen, - message.getFactoryId(), - message.getStacks(), + message.getElements(), message.getId(), message.getQuantity(), message.isFluids(), diff --git a/src/main/java/com/refinedmods/refinedstorage/network/craftingmonitor/CraftingMonitorUpdateMessage.java b/src/main/java/com/refinedmods/refinedstorage/network/craftingmonitor/CraftingMonitorUpdateMessage.java index 6cce3929f..060229a8f 100644 --- a/src/main/java/com/refinedmods/refinedstorage/network/craftingmonitor/CraftingMonitorUpdateMessage.java +++ b/src/main/java/com/refinedmods/refinedstorage/network/craftingmonitor/CraftingMonitorUpdateMessage.java @@ -83,7 +83,7 @@ public class CraftingMonitorUpdateMessage { buf.writeUniqueId(task.getId()); buf.writeCompoundTag(task.getRequested().writeToNbt()); buf.writeInt(task.getQuantity()); - buf.writeLong(task.getExecutionStarted()); + buf.writeLong(task.getStartTime()); buf.writeInt(task.getCompletionPercentage()); List elements = task.getCraftingMonitorElements(); diff --git a/src/main/java/com/refinedmods/refinedstorage/network/grid/GridCraftingPreviewResponseMessage.java b/src/main/java/com/refinedmods/refinedstorage/network/grid/GridCraftingPreviewResponseMessage.java index f6095b259..10ad79be8 100644 --- a/src/main/java/com/refinedmods/refinedstorage/network/grid/GridCraftingPreviewResponseMessage.java +++ b/src/main/java/com/refinedmods/refinedstorage/network/grid/GridCraftingPreviewResponseMessage.java @@ -13,26 +13,20 @@ import java.util.UUID; import java.util.function.Supplier; public class GridCraftingPreviewResponseMessage { - private final ResourceLocation factoryId; - private final List> stacks; + private final List> elements; private final UUID id; private final int quantity; private final boolean fluids; - public GridCraftingPreviewResponseMessage(ResourceLocation factoryId, List> stacks, UUID id, int quantity, boolean fluids) { - this.factoryId = factoryId; - this.stacks = stacks; + public GridCraftingPreviewResponseMessage(List> elements, UUID id, int quantity, boolean fluids) { + this.elements = elements; this.id = id; this.quantity = quantity; this.fluids = fluids; } - public ResourceLocation getFactoryId() { - return factoryId; - } - - public List> getStacks() { - return stacks; + public List> getElements() { + return elements; } public UUID getId() { @@ -48,7 +42,6 @@ public class GridCraftingPreviewResponseMessage { } public static GridCraftingPreviewResponseMessage decode(PacketBuffer buf) { - ResourceLocation factoryId = buf.readResourceLocation(); UUID id = buf.readUniqueId(); int quantity = buf.readInt(); boolean fluids = buf.readBoolean(); @@ -62,17 +55,16 @@ public class GridCraftingPreviewResponseMessage { stacks.add(API.instance().getCraftingPreviewElementRegistry().get(type).apply(buf)); } - return new GridCraftingPreviewResponseMessage(factoryId, stacks, id, quantity, fluids); + return new GridCraftingPreviewResponseMessage(stacks, id, quantity, fluids); } public static void encode(GridCraftingPreviewResponseMessage message, PacketBuffer buf) { - buf.writeResourceLocation(message.factoryId); buf.writeUniqueId(message.id); buf.writeInt(message.quantity); buf.writeBoolean(message.fluids); - buf.writeInt(message.stacks.size()); + buf.writeInt(message.elements.size()); - for (ICraftingPreviewElement stack : message.stacks) { + for (ICraftingPreviewElement stack : message.elements) { buf.writeResourceLocation(stack.getId()); stack.write(buf); } diff --git a/src/main/java/com/refinedmods/refinedstorage/screen/grid/CraftingPreviewScreen.java b/src/main/java/com/refinedmods/refinedstorage/screen/grid/CraftingPreviewScreen.java index 6766520b5..3f96c4c94 100644 --- a/src/main/java/com/refinedmods/refinedstorage/screen/grid/CraftingPreviewScreen.java +++ b/src/main/java/com/refinedmods/refinedstorage/screen/grid/CraftingPreviewScreen.java @@ -5,7 +5,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.refinedmods.refinedstorage.RS; import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; import com.refinedmods.refinedstorage.api.autocrafting.preview.ICraftingPreviewElement; -import com.refinedmods.refinedstorage.api.autocrafting.task.CraftingTaskErrorType; +import com.refinedmods.refinedstorage.api.autocrafting.task.CalculationResultType; import com.refinedmods.refinedstorage.api.render.IElementDrawers; import com.refinedmods.refinedstorage.apiimpl.autocrafting.preview.ErrorCraftingPreviewElement; import com.refinedmods.refinedstorage.apiimpl.autocrafting.preview.FluidCraftingPreviewElement; @@ -25,7 +25,6 @@ import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.container.Container; import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TranslationTextComponent; import net.minecraftforge.fluids.FluidStack; @@ -36,14 +35,12 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; public class CraftingPreviewScreen extends BaseScreen { private static final int VISIBLE_ROWS = 5; private final List> stacks; private final Screen parent; - private final ResourceLocation factoryId; private final ScrollbarWidget scrollbar; @@ -56,7 +53,7 @@ public class CraftingPreviewScreen extends BaseScreen { private final IElementDrawers drawers = new CraftingPreviewElementDrawers(this, font); - public CraftingPreviewScreen(Screen parent, ResourceLocation factoryId, List> stacks, UUID id, int quantity, boolean fluids, ITextComponent title) { + public CraftingPreviewScreen(Screen parent, List> stacks, UUID id, int quantity, boolean fluids, ITextComponent title) { super(new Container(null, 0) { @Override public boolean canInteractWith(@Nonnull PlayerEntity player) { @@ -66,7 +63,6 @@ public class CraftingPreviewScreen extends BaseScreen { this.stacks = new ArrayList<>(stacks); this.parent = parent; - this.factoryId = factoryId; this.id = id; this.quantity = quantity; @@ -90,7 +86,7 @@ public class CraftingPreviewScreen extends BaseScreen { } @Nullable - private CraftingTaskErrorType getErrorType() { + private CalculationResultType getErrorType() { if (stacks.size() == 1 && stacks.get(0) instanceof ErrorCraftingPreviewElement) { return ((ErrorCraftingPreviewElement) stacks.get(0)).getType(); } @@ -135,8 +131,6 @@ public class CraftingPreviewScreen extends BaseScreen { renderString(matrixStack, RenderUtils.getOffsetOnScale(x + 5, scale), RenderUtils.getOffsetOnScale(y + 61, scale), I18n.format("gui.refinedstorage.crafting_preview.error.recursive.4")); - RenderSystem.popMatrix(); - ICraftingPattern pattern = PatternItem.fromCache(parent.getMinecraft().world, (ItemStack) stacks.get(0).getElement()); int yy = 83; @@ -162,11 +156,11 @@ public class CraftingPreviewScreen extends BaseScreen { renderString(matrixStack, RenderUtils.getOffsetOnScale(x + 5, scale), RenderUtils.getOffsetOnScale(y + 21, scale), I18n.format("gui.refinedstorage.crafting_preview.error.too_complex.0")); renderString(matrixStack, RenderUtils.getOffsetOnScale(x + 5, scale), RenderUtils.getOffsetOnScale(y + 31, scale), I18n.format("gui.refinedstorage.crafting_preview.error.too_complex.1")); - RenderSystem.popMatrix(); - break; } } + + RenderSystem.popMatrix(); } else { int slot = scrollbar != null ? (scrollbar.getOffset() * 3) : 0;