From 499b2737c1dd5811852e1240d46788108fd1dab6 Mon Sep 17 00:00:00 2001 From: raoulvdberge Date: Sun, 5 Jan 2025 18:56:13 +0100 Subject: [PATCH] feat: starting and running crafting tasks in a pattern provider --- .../api/autocrafting/Pattern.java | 10 +- .../api/autocrafting/PatternBuilder.java | 18 +- .../api/autocrafting/calculation/Amount.java | 6 + .../CraftingCalculatorListener.java | 4 +- ...ngResourcesCraftingCalculatorListener.java | 4 +- .../PreviewCraftingCalculatorListener.java | 4 +- .../autocrafting/preview/PreviewProvider.java | 2 +- .../task/ExternalPatternInputSink.java | 2 - .../task/ExternalTaskPattern.java | 17 +- .../autocrafting/task/MutableTaskPlan.java | 30 +- .../api/autocrafting/task/Task.java | 10 +- .../api/autocrafting/task/TaskId.java | 5 + .../api/autocrafting/task/TaskImpl.java | 29 +- .../api/autocrafting/task/TaskPlan.java | 8 +- ...> TaskPlanCraftingCalculatorListener.java} | 22 +- .../api/autocrafting/PatternTest.java | 29 +- .../api/autocrafting/ResourceFixtures.java | 6 +- .../autocrafting/calculation/AmountTest.java | 53 +++ .../task/ExternalPatternInputSinkBuilder.java | 31 +- .../api/autocrafting/task/TaskImplTest.java | 370 +++++++++++++-- .../api/autocrafting/task/TaskPlanTest.java | 13 +- .../api/autocrafting/task/TaskUtil.java | 48 +- ...oviderExternalPatternInputSinkFactory.java | 14 + .../common/AbstractModInitializer.java | 2 - .../refinedstorage/common/Platform.java | 3 + .../refinedstorage/common/PlatformProxy.java | 6 + .../common/autocrafting/PatternItem.java | 16 +- .../common/autocrafting/PatternResolver.java | 50 +- .../autocrafter/AutocrafterBlockEntity.java | 11 + .../monitor/TaskStatusProviderImpl.java | 51 --- .../AutocraftingPreviewContainerMenu.java | 4 +- .../common/grid/AbstractGridBlockEntity.java | 7 +- .../grid/AbstractGridContainerMenu.java | 5 +- .../common/grid/ClientCraftingGrid.java | 5 +- .../common/grid/WirelessGrid.java | 7 +- .../storage/portablegrid/PortableGrid.java | 7 +- ...tocraftingStorageMonitorContainerMenu.java | 5 +- .../StorageMonitorBlockEntity.java | 7 +- .../packet/c2s/AutocraftingRequestPacket.java | 6 +- .../s2c/AutocraftingResponsePacket.java | 6 +- .../common/support/packet/s2c/S2CPackets.java | 4 +- .../common/util/ClientPlatformUtil.java | 4 +- .../textures/gui/sprites/cancel.png | Bin 283 -> 324 bytes .../textures/gui/sprites/reset.png | Bin 231 -> 274 bytes .../textures/gui/sprites/start.png | Bin 249 -> 279 bytes ...orageExternalPatternInputSinkStrategy.java | 13 + ...ternalPatternInputSinkStrategyFactory.java | 12 + .../fabric/api/RefinedStorageFabricApi.java | 6 + .../api/RefinedStorageFabricApiProxy.java | 12 + .../fabric/ModInitializerImpl.java | 19 + .../refinedstorage/fabric/PlatformImpl.java | 6 + .../fabric/RefinedStorageFabricApiImpl.java | 22 + ...alPatternInputSinkStrategyFactoryImpl.java | 41 ++ ...eExternalPatternInputSinkStrategyImpl.java | 53 +++ ...tternProviderExternalPatternInputSink.java | 36 ++ ...oviderExternalPatternInputSinkFactory.java | 33 ++ .../AutocraftingNetworkComponent.java | 4 +- .../network/autocrafting/ParentContainer.java | 4 +- .../network/autocrafting/PatternProvider.java | 6 +- ...tternProviderExternalPatternInputSink.java | 14 + .../test/fixtures/FakeTaskStatusProvider.java | 35 -- .../test/fixtures/NetworkTestFixtures.java | 5 +- .../AutocraftingNetworkComponentImpl.java | 100 ++-- .../PatternProviderNetworkNode.java | 88 +++- .../node/relay/RelayOutputNetworkNode.java | 14 + .../relay/RelayOutputPatternProvider.java | 25 +- .../AutocraftingNetworkComponentImplTest.java | 91 ++-- .../PatternProviderNetworkNodeTest.java | 427 ++++++++++++++++++ 68 files changed, 1641 insertions(+), 366 deletions(-) rename refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/{TaskCraftingCalculatorListener.java => TaskPlanCraftingCalculatorListener.java} (71%) create mode 100644 refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/AmountTest.java create mode 100644 refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/autocrafting/PatternProviderExternalPatternInputSinkFactory.java delete mode 100644 refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/TaskStatusProviderImpl.java create mode 100644 refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/FabricStorageExternalPatternInputSinkStrategy.java create mode 100644 refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/FabricStorageExternalPatternInputSinkStrategyFactory.java create mode 100644 refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStorageExternalPatternInputSinkStrategyFactoryImpl.java create mode 100644 refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStorageExternalPatternInputSinkStrategyImpl.java create mode 100644 refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStoragePatternProviderExternalPatternInputSink.java create mode 100644 refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStoragePatternProviderExternalPatternInputSinkFactory.java create mode 100644 refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java delete mode 100644 refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/FakeTaskStatusProvider.java diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/Pattern.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/Pattern.java index 742d5a36f..f8e046d99 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/Pattern.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/Pattern.java @@ -4,15 +4,21 @@ import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import java.util.List; +import java.util.UUID; import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.6") -public record Pattern(List ingredients, List outputs, PatternType type) { - public Pattern(final List ingredients, final List outputs, final PatternType type) { +public record Pattern(UUID id, List ingredients, List outputs, PatternType type) { + public Pattern(final UUID id, + final List ingredients, + final List outputs, + final PatternType type) { + CoreValidations.validateNotNull(id, "ID cannot be null"); CoreValidations.validateNotEmpty(ingredients, "Ingredients cannot be empty"); CoreValidations.validateNotEmpty(outputs, "Outputs cannot be empty"); CoreValidations.validateNotNull(type, "Type cannot be null"); + this.id = id; this.ingredients = List.copyOf(ingredients); this.outputs = List.copyOf(outputs); this.type = type; diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/PatternBuilder.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/PatternBuilder.java index 0ad8938fd..5790112b3 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/PatternBuilder.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/PatternBuilder.java @@ -5,16 +5,19 @@ import java.util.ArrayList; import java.util.List; +import java.util.UUID; import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") public class PatternBuilder { private final PatternType type; + private final UUID id; private final List ingredients = new ArrayList<>(); private final List outputs = new ArrayList<>(); - private PatternBuilder(final PatternType type) { + private PatternBuilder(final UUID id, final PatternType type) { + this.id = id; this.type = type; } @@ -23,16 +26,15 @@ public static PatternBuilder pattern() { } public static PatternBuilder pattern(final PatternType type) { - return new PatternBuilder(type); + return pattern(UUID.randomUUID(), type); } - public IngredientBuilder ingredient(final long amount) { - return new IngredientBuilder(amount); + public static PatternBuilder pattern(final UUID id, final PatternType type) { + return new PatternBuilder(id, type); } - public PatternBuilder ingredient(final Ingredient ingredient) { - ingredients.add(ingredient); - return this; + public IngredientBuilder ingredient(final long amount) { + return new IngredientBuilder(amount); } public PatternBuilder ingredient(final ResourceKey input, final long amount) { @@ -46,7 +48,7 @@ public PatternBuilder output(final ResourceKey output, final long amount) { } public Pattern build() { - return new Pattern(ingredients, outputs, type); + return new Pattern(id, ingredients, outputs, type); } public class IngredientBuilder { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/Amount.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/Amount.java index 60386dd6a..abbfa3da1 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/Amount.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/Amount.java @@ -1,10 +1,16 @@ package com.refinedmods.refinedstorage.api.autocrafting.calculation; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.core.CoreValidations; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.resource.ResourceKey; public record Amount(long iterations, long amountPerIteration) { + public Amount { + CoreValidations.validateLargerThanZero(iterations, "Iterations"); + CoreValidations.validateLargerThanZero(amountPerIteration, "Amount per iteration"); + } + public long getTotal() { return iterations * amountPerIteration; } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorListener.java index 02d6891c1..3384e2ecc 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorListener.java @@ -7,13 +7,13 @@ @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") public interface CraftingCalculatorListener { - CraftingCalculatorListener childCalculationStarted(Pattern pattern, ResourceKey resource, Amount amount); + CraftingCalculatorListener childCalculationStarted(Pattern childPattern, ResourceKey resource, Amount amount); void childCalculationCompleted(CraftingCalculatorListener childListener); void ingredientsExhausted(ResourceKey resource, long amount); - void ingredientUsed(Pattern pattern, int ingredientIndex, ResourceKey resource, long amount); + void ingredientUsed(Pattern ingredientPattern, int ingredientIndex, ResourceKey resource, long amount); void ingredientExtractedFromStorage(ResourceKey resource, long amount); diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java index 578fb8ed8..5c22f0f7a 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java @@ -20,7 +20,7 @@ boolean isMissingResources() { } @Override - public CraftingCalculatorListener childCalculationStarted(final Pattern pattern, + public CraftingCalculatorListener childCalculationStarted(final Pattern childPattern, final ResourceKey resource, final Amount amount) { return new MissingResourcesCraftingCalculatorListener(missingResources); @@ -37,7 +37,7 @@ public void ingredientsExhausted(final ResourceKey resource, final long amount) } @Override - public void ingredientUsed(final Pattern pattern, + public void ingredientUsed(final Pattern ingredientPattern, final int ingredientIndex, final ResourceKey resource, final long amount) { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java index 26e29f10c..669f6f760 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java @@ -42,7 +42,7 @@ public static Preview calculatePreview(final CraftingCalculator calculator, } @Override - public CraftingCalculatorListener childCalculationStarted(final Pattern pattern, + public CraftingCalculatorListener childCalculationStarted(final Pattern childPattern, final ResourceKey resource, final Amount amount) { LOGGER.debug("{} - Child calculation starting for {}x {}", listenerId, amount, resource); @@ -64,7 +64,7 @@ public void ingredientsExhausted(final ResourceKey resource, final long amount) } @Override - public void ingredientUsed(final Pattern pattern, + public void ingredientUsed(final Pattern ingredientPattern, final int ingredientIndex, final ResourceKey resource, final long amount) { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java index b22c567d4..718d984d4 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java @@ -14,5 +14,5 @@ public interface PreviewProvider { CompletableFuture getMaxAmount(ResourceKey resource); - boolean startTask(ResourceKey resource, long amount, Actor actor, boolean notify); + CompletableFuture startTask(ResourceKey resource, long amount, Actor actor, boolean notify); } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java index b7048d2ac..acf5a59a6 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java @@ -11,7 +11,5 @@ @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") @FunctionalInterface public interface ExternalPatternInputSink { - ExternalPatternInputSink EMPTY = (pattern, resources, action) -> false; - boolean accept(Pattern pattern, Collection resources, Action action); } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java index c940f03d2..a8a3d94c3 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java @@ -30,11 +30,9 @@ boolean step(final MutableResourceList internalStorage, final ExternalPatternInp return true; } if (iterationsToSendToSink == 0) { - // TODO: coverage return false; } if (!acceptsIterationInputs(internalStorage, externalPatternInputSink)) { - // TODO: coverage return false; } LOGGER.info("Stepped {} with {} iterations remaining", pattern, iterationsToSendToSink); @@ -50,7 +48,6 @@ long interceptInsertion(final ResourceKey resource, final long amount) { expectedOutputs.remove(resource, available); return available; } - // TODO: coverage return 0; } @@ -58,18 +55,24 @@ private boolean acceptsIterationInputs(final MutableResourceList internalStorage final ExternalPatternInputSink externalPatternInputSink) { final ResourceList iterationInputsSimulated = calculateIterationInputs(Action.SIMULATE); if (!extractAll(iterationInputsSimulated, internalStorage, Action.SIMULATE)) { - // TODO: coverage return false; } if (!externalPatternInputSink.accept(pattern, iterationInputsSimulated.copyState(), Action.SIMULATE)) { - // TODO: coverage return false; } final ResourceList iterationInputs = calculateIterationInputs(Action.EXECUTE); extractAll(iterationInputs, internalStorage, Action.EXECUTE); + // If the sink does not accept the inputs + // we cannot return the extracted resources to the internal storage + // because we have already deducted from the iteration inputs + // and because the sink might have still accepted some resources halfway. + // If we returned the extracted resources to the internal storage and correct the + // iteration inputs, it would potentially duplicate the resources + // across the sink and the internal storage. + // The end result is that we lie, do as if the insertion was successful, + // and potentially void the extracted resources from the internal storage. if (!externalPatternInputSink.accept(pattern, iterationInputs.copyState(), Action.EXECUTE)) { - // TODO: coverage - return false; + LOGGER.warn("External sink {} did not accept all inputs for pattern {}", externalPatternInputSink, pattern); } return true; } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/MutableTaskPlan.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/MutableTaskPlan.java index 123c7925d..65e5b7506 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/MutableTaskPlan.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/MutableTaskPlan.java @@ -10,52 +10,62 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import javax.annotation.Nullable; import static java.util.Objects.requireNonNull; public class MutableTaskPlan { + @Nullable + private final Pattern pattern; private final Map patterns; private final MutableResourceList initialRequirements; private boolean missing; MutableTaskPlan() { - this(new LinkedHashMap<>(), MutableResourceListImpl.create(), false); + this(null, new LinkedHashMap<>(), MutableResourceListImpl.create(), false); } - private MutableTaskPlan(final Map patterns, + private MutableTaskPlan(@Nullable final Pattern pattern, + final Map patterns, final MutableResourceList initialRequirements, final boolean missing) { + this.pattern = pattern; this.patterns = patterns; this.initialRequirements = initialRequirements; this.missing = missing; } - void addOrUpdatePattern(final Pattern pattern, final long iterations) { - patterns.computeIfAbsent(pattern, MutablePatternPlan::new).addIterations(iterations); + void addOrUpdatePattern(final Pattern usedPattern, final long iterations) { + patterns.computeIfAbsent(usedPattern, MutablePatternPlan::new).addIterations(iterations); } void addToExtract(final ResourceKey resource, final long amount) { initialRequirements.add(resource, amount); } - void addUsedIngredient(final Pattern pattern, + void addUsedIngredient(final Pattern ingredientPattern, final int ingredientIndex, final ResourceKey resource, final long amount) { - final MutablePatternPlan patternPlan = requireNonNull(patterns.get(pattern)); + final MutablePatternPlan patternPlan = requireNonNull(patterns.get(ingredientPattern)); patternPlan.addUsedIngredient(ingredientIndex, resource, amount); } - MutableTaskPlan copy() { + MutableTaskPlan copy(final Pattern childPattern) { final Map patternsCopy = new LinkedHashMap<>(); for (final Map.Entry entry : patterns.entrySet()) { patternsCopy.put(entry.getKey(), entry.getValue().copy()); } - return new MutableTaskPlan(patternsCopy, initialRequirements.copy(), missing); + return new MutableTaskPlan( + pattern == null ? childPattern : pattern, + patternsCopy, + initialRequirements.copy(), + missing + ); } Optional getPlan() { - if (missing) { + if (missing || pattern == null) { return Optional.empty(); } final Map finalPatterns = Collections.unmodifiableMap(patterns.entrySet() @@ -66,7 +76,7 @@ Optional getPlan() { (a, b) -> a, LinkedHashMap::new ))); - return Optional.of(new TaskPlan(finalPatterns, initialRequirements.copyState())); + return Optional.of(new TaskPlan(pattern, finalPatterns, initialRequirements.copyState())); } void setMissing() { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/Task.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/Task.java index 0fcf94ae2..81addb9a6 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/Task.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/Task.java @@ -1,12 +1,20 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; +import com.refinedmods.refinedstorage.api.storage.root.RootStorageListener; + +import java.util.Collection; import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") -public interface Task { +public interface Task extends RootStorageListener { + TaskId getId(); + TaskState getState(); + + Collection copyInternalStorageState(); void step(RootStorage rootStorage, ExternalPatternInputSink externalPatternInputSink); } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskId.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskId.java index 338ec75ff..f843b6300 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskId.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskId.java @@ -9,4 +9,9 @@ public record TaskId(UUID id) { public static TaskId create() { return new TaskId(UUID.randomUUID()); } + + @Override + public String toString() { + return id.toString(); + } } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImpl.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImpl.java index c469d1748..560274130 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImpl.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImpl.java @@ -8,7 +8,6 @@ import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl; import com.refinedmods.refinedstorage.api.storage.Actor; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; -import com.refinedmods.refinedstorage.api.storage.root.RootStorageListener; import java.util.Collection; import java.util.HashSet; @@ -20,7 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class TaskImpl implements Task, RootStorageListener { +public class TaskImpl implements Task { private static final Logger LOGGER = LoggerFactory.getLogger(TaskImpl.class); private final TaskId id = TaskId.create(); @@ -29,7 +28,7 @@ public class TaskImpl implements Task, RootStorageListener { private final MutableResourceList internalStorage = MutableResourceListImpl.create(); private TaskState state = TaskState.READY; - public TaskImpl(final TaskPlan plan) { + private TaskImpl(final TaskPlan plan) { this.patterns = plan.patterns().entrySet().stream().collect(Collectors.toMap( Map.Entry::getKey, e -> createTaskPattern(e.getKey(), e.getValue()), @@ -39,6 +38,10 @@ public TaskImpl(final TaskPlan plan) { plan.initialRequirements().forEach(initialRequirements::add); } + public static Task fromPlan(final TaskPlan plan) { + return new TaskImpl(plan); + } + private static AbstractTaskPattern createTaskPattern(final Pattern pattern, final TaskPlan.PatternPlan patternPlan) { return switch (pattern.type()) { @@ -47,6 +50,11 @@ private static AbstractTaskPattern createTaskPattern(final Pattern pattern, }; } + @Override + public TaskId getId() { + return id; + } + @Override public TaskState getState() { return state; @@ -97,7 +105,8 @@ private void returnInternalStorageAndTryCompleteTask(final RootStorage rootStora } } - Collection copyInternalStorageState() { + @Override + public Collection copyInternalStorageState() { return internalStorage.copyState(); } @@ -138,20 +147,18 @@ private boolean returnInternalStorage(final RootStorage rootStorage) { @Override public long beforeInsert(final ResourceKey resource, final long amount, final Actor actor) { - // TODO: coverage - long totalIntercepted = 0; + long remaining = amount; for (final AbstractTaskPattern pattern : patterns.values()) { - final long remaining = amount - totalIntercepted; final long intercepted = pattern.interceptInsertion(resource, remaining); if (intercepted > 0) { internalStorage.add(resource, intercepted); } - totalIntercepted += intercepted; - if (totalIntercepted == amount) { - break; + remaining -= intercepted; + if (remaining == 0) { + return amount; } } - return totalIntercepted; + return amount - remaining; } @Override diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlan.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlan.java index 8d344a752..04d8cd46e 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlan.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlan.java @@ -10,9 +10,11 @@ import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") -public record TaskPlan(Map patterns, Collection initialRequirements) { - public PatternPlan pattern(final Pattern pattern) { - return patterns.get(pattern); +public record TaskPlan(Pattern pattern, + Map patterns, + Collection initialRequirements) { + public PatternPlan getPattern(final Pattern p) { + return patterns.get(p); } public record PatternPlan(long iterations, Map> ingredients) { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskCraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanCraftingCalculatorListener.java similarity index 71% rename from refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskCraftingCalculatorListener.java rename to refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanCraftingCalculatorListener.java index 6a8c4743b..37b6f745d 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskCraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanCraftingCalculatorListener.java @@ -8,28 +8,32 @@ import java.util.Optional; -public class TaskCraftingCalculatorListener implements CraftingCalculatorListener { +public class TaskPlanCraftingCalculatorListener implements CraftingCalculatorListener { private MutableTaskPlan task; - private TaskCraftingCalculatorListener(final MutableTaskPlan task) { + private TaskPlanCraftingCalculatorListener(final MutableTaskPlan task) { this.task = task; } + private TaskPlanCraftingCalculatorListener() { + this(new MutableTaskPlan()); + } + public static Optional calculatePlan(final CraftingCalculator calculator, final ResourceKey resource, final long amount) { - final TaskCraftingCalculatorListener listener = new TaskCraftingCalculatorListener(new MutableTaskPlan()); + final TaskPlanCraftingCalculatorListener listener = new TaskPlanCraftingCalculatorListener(); calculator.calculate(resource, amount, listener); return listener.task.getPlan(); } @Override - public CraftingCalculatorListener childCalculationStarted(final Pattern pattern, + public CraftingCalculatorListener childCalculationStarted(final Pattern childPattern, final ResourceKey resource, final Amount amount) { - final MutableTaskPlan copy = task.copy(); - copy.addOrUpdatePattern(pattern, amount.iterations()); - return new TaskCraftingCalculatorListener(copy); + final MutableTaskPlan copy = task.copy(childPattern); + copy.addOrUpdatePattern(childPattern, amount.iterations()); + return new TaskPlanCraftingCalculatorListener(copy); } @Override @@ -43,11 +47,11 @@ public void ingredientsExhausted(final ResourceKey resource, final long amount) } @Override - public void ingredientUsed(final Pattern pattern, + public void ingredientUsed(final Pattern ingredientPattern, final int ingredientIndex, final ResourceKey resource, final long amount) { - task.addUsedIngredient(pattern, ingredientIndex, resource, amount); + task.addUsedIngredient(ingredientPattern, ingredientIndex, resource, amount); } @Override diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternTest.java index 921b20181..676c3b742 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternTest.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.UUID; import org.assertj.core.api.ThrowableAssert; import org.junit.jupiter.api.Test; @@ -21,6 +22,7 @@ class PatternTest { void testPattern() { // Act final Pattern sut = new Pattern( + UUID.randomUUID(), List.of( new Ingredient(1, List.of(A, B)), new Ingredient(2, List.of(C)) @@ -51,6 +53,7 @@ void testPattern() { void shouldNotCreatePatternWithoutIngredients() { // Act final ThrowableAssert.ThrowingCallable action = () -> new Pattern( + UUID.randomUUID(), List.of(), List.of( new ResourceAmount(OAK_LOG, 3), @@ -67,6 +70,7 @@ void shouldNotCreatePatternWithoutIngredients() { void shouldNotCreatePatternWithoutOutputs() { // Act final ThrowableAssert.ThrowingCallable action = () -> new Pattern( + UUID.randomUUID(), List.of( new Ingredient(1, List.of(A, B)), new Ingredient(2, List.of(C)) @@ -86,7 +90,7 @@ void shouldCopyIngredientsAndOutputs() { ingredients.add(new Ingredient(1, List.of(A, B))); final List outputs = new ArrayList<>(); outputs.add(new ResourceAmount(OAK_LOG, 3)); - final Pattern sut = new Pattern(ingredients, outputs, PatternType.INTERNAL); + final Pattern sut = new Pattern(UUID.randomUUID(), ingredients, outputs, PatternType.INTERNAL); // Act ingredients.add(new Ingredient(2, List.of(C))); @@ -101,6 +105,7 @@ void shouldCopyIngredientsAndOutputs() { void shouldNotBeAbleToModifyIngredientsAndOutputs() { // Arrange final Pattern sut = new Pattern( + UUID.randomUUID(), List.of(new Ingredient(1, List.of(A))), List.of(new ResourceAmount(OAK_LOG, 3)), PatternType.INTERNAL @@ -125,6 +130,7 @@ void shouldNotBeAbleToModifyIngredientsAndOutputs() { void shouldNotCreatePatternWithoutPatternType() { // Act final ThrowableAssert.ThrowingCallable action = () -> new Pattern( + UUID.randomUUID(), List.of( new Ingredient(1, List.of(A, B)), new Ingredient(2, List.of(C)) @@ -139,4 +145,25 @@ void shouldNotCreatePatternWithoutPatternType() { // Assert assertThatThrownBy(action).isInstanceOf(NullPointerException.class); } + + @Test + @SuppressWarnings("ConstantConditions") + void shouldNotCreateWithoutId() { + // Act + final ThrowableAssert.ThrowingCallable action = () -> new Pattern( + null, + List.of( + new Ingredient(1, List.of(A, B)), + new Ingredient(2, List.of(C)) + ), + List.of( + new ResourceAmount(OAK_LOG, 3), + new ResourceAmount(OAK_PLANKS, 4) + ), + PatternType.INTERNAL + ); + + // Assert + assertThatThrownBy(action).isInstanceOf(NullPointerException.class); + } } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/ResourceFixtures.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/ResourceFixtures.java index 083e5ac50..76cf883c9 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/ResourceFixtures.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/ResourceFixtures.java @@ -15,5 +15,9 @@ public enum ResourceFixtures implements ResourceKey { SIGN, IRON_ORE, IRON_INGOT, - IRON_PICKAXE + IRON_PICKAXE, + COBBLESTONE, + STONE, + STONE_BRICKS, + SMOOTH_STONE } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/AmountTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/AmountTest.java new file mode 100644 index 000000000..169d336dc --- /dev/null +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/AmountTest.java @@ -0,0 +1,53 @@ +package com.refinedmods.refinedstorage.api.autocrafting.calculation; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class AmountTest { + @ParameterizedTest + @ValueSource(longs = {-1L, 0L}) + void testInvalidIterations(final long iterations) { + // Act + final Executable action = () -> new Amount(iterations, 1); + + // Assert + assertThrows(IllegalArgumentException.class, action); + } + + @ParameterizedTest + @ValueSource(longs = {-1L, 0L}) + void testInvalidAmountPerIteration(final long amountPerIteration) { + // Act + final Executable action = () -> new Amount(1, amountPerIteration); + + // Assert + assertThrows(IllegalArgumentException.class, action); + } + + @Test + void testMinimumValid() { + // Act + final Amount amount = new Amount(1, 1); + + // Assert + assertThat(amount.iterations()).isEqualTo(1); + assertThat(amount.amountPerIteration()).isEqualTo(1); + } + + @Test + void testTotal() { + // Arrange + final Amount amount = new Amount(2, 3); + + // Act + final long total = amount.getTotal(); + + // Assert + assertThat(total).isEqualTo(6); + } +} diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java index 00cb3f535..f34547ea9 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java @@ -21,10 +21,10 @@ static ExternalPatternInputSinkBuilder externalPatternInputSink() { return new ExternalPatternInputSinkBuilder(); } - Storage storageSink(final Pattern pattern) { - final Storage storage = new StorageImpl(); - sinks.put(pattern, new StorageSink(storage)); - return storage; + Sink storageSink(final Pattern pattern) { + final Sink sink = new Sink(new StorageImpl()); + sinks.put(pattern, sink); + return sink; } ExternalPatternInputSink build() { @@ -34,19 +34,26 @@ ExternalPatternInputSink build() { }; } - private interface Sink { - boolean accept(Collection resources, Action action); - } - - private static class StorageSink implements Sink { + static class Sink { private final Storage storage; + private boolean enabled = true; - private StorageSink(final Storage storage) { + private Sink(final Storage storage) { this.storage = storage; } - @Override - public boolean accept(final Collection resources, final Action action) { + Collection getAll() { + return storage.getAll(); + } + + void setEnabled(final boolean enabled) { + this.enabled = enabled; + } + + private boolean accept(final Collection resources, final Action action) { + if (!enabled) { + return false; + } if (action == Action.EXECUTE) { return accept(resources); } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java index 5aa85efbc..b483a62a9 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java @@ -1,10 +1,11 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.PatternRepository; +import com.refinedmods.refinedstorage.api.autocrafting.PatternType; import com.refinedmods.refinedstorage.api.core.Action; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.storage.Actor; -import com.refinedmods.refinedstorage.api.storage.Storage; import com.refinedmods.refinedstorage.api.storage.StorageImpl; import com.refinedmods.refinedstorage.api.storage.limited.LimitedStorageImpl; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; @@ -14,6 +15,9 @@ import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.patterns; import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.storage; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternBuilder.pattern; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.A; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.COBBLESTONE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_INGOT; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_ORE; @@ -21,15 +25,21 @@ import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_PLANKS; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SIGN; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SMOOTH_STONE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_LOG; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_PLANKS; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STICKS; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STONE; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STONE_BRICKS; import static com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkBuilder.externalPatternInputSink; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.CRAFTING_TABLE_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.EMPTY_SINK; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.IRON_INGOT_PATTERN; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.IRON_PICKAXE_PATTERN; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.OAK_PLANKS_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.SMOOTH_STONE_PATTERN; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.SPRUCE_PLANKS_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.STONE_PATTERN; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.getRunningTask; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.getTask; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.getTaskReadyToReturnInternalStorage; @@ -47,10 +57,11 @@ void testInitialState() { final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); // Act - final TaskImpl task = getTask(storage, patterns, CRAFTING_TABLE, 3); + final Task task = getTask(storage, patterns, CRAFTING_TABLE, 3); // Assert assertThat(task.getState()).isEqualTo(TaskState.READY); + assertThat(task.getId()).isNotNull(); } @Test @@ -63,10 +74,10 @@ void shouldExtractAllResources() { new ResourceAmount(SIGN, 10) ); final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); - final TaskImpl task = getTask(storage, patterns, CRAFTING_TABLE, 3); + final Task task = getTask(storage, patterns, CRAFTING_TABLE, 3); // Act - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); // Assert assertThat(task.getState()).isEqualTo(TaskState.RUNNING); @@ -92,25 +103,25 @@ void shouldPartiallyExtractAllResources() { new ResourceAmount(SIGN, 10) ); final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); - final TaskImpl task = getTask(storage, patterns, CRAFTING_TABLE, 3); + final Task task = getTask(storage, patterns, CRAFTING_TABLE, 3); storage.extract(OAK_PLANKS, 4, Action.EXECUTE, Actor.EMPTY); // Act & assert - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.EXTRACTING_INITIAL_RESOURCES); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(SIGN, 10) ); storage.insert(OAK_PLANKS, 2, Action.EXECUTE, Actor.EMPTY); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.EXTRACTING_INITIAL_RESOURCES); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(SIGN, 10) ); storage.insert(OAK_PLANKS, 3, Action.EXECUTE, Actor.EMPTY); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(SIGN, 10), @@ -128,10 +139,10 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(SIGN, 10) ); final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); - final TaskImpl task = getRunningTask(storage, patterns, ExternalPatternInputSink.EMPTY, CRAFTING_TABLE, 3); + final Task task = getRunningTask(storage, patterns, EMPTY_SINK, CRAFTING_TABLE, 3); // Act & assert - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -140,7 +151,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(OAK_PLANKS, 8) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -150,7 +161,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(OAK_PLANKS, 5) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -160,7 +171,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(OAK_PLANKS, 2) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -171,7 +182,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(SIGN, 10) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); assertThat(task.copyInternalStorageState()).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -179,7 +190,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(CRAFTING_TABLE, 3) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); assertThat(task.copyInternalStorageState()).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -197,9 +208,9 @@ void shouldCompleteTaskWithExternalPattern() { ); final PatternRepository patterns = patterns(IRON_INGOT_PATTERN, IRON_PICKAXE_PATTERN); final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); - final Storage ironOreSink = sinkBuilder.storageSink(IRON_INGOT_PATTERN); + final ExternalPatternInputSinkBuilder.Sink ironOreSink = sinkBuilder.storageSink(IRON_INGOT_PATTERN); final ExternalPatternInputSink sink = sinkBuilder.build(); - final TaskImpl task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); + final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); assertThat(storage.getAll()).isEmpty(); assertThat(task.copyInternalStorageState()) @@ -277,9 +288,11 @@ void shouldCompleteTaskWithExternalPattern() { ); storage.insert(IRON_INGOT, 5, Action.EXECUTE, Actor.EMPTY); + storage.insert(STONE, 2, Action.EXECUTE, Actor.EMPTY); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( - new ResourceAmount(IRON_INGOT, 3) + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(IRON_INGOT, 3), + new ResourceAmount(STONE, 2) ); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -290,8 +303,9 @@ void shouldCompleteTaskWithExternalPattern() { task.step(storage, sink); assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); - assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( - new ResourceAmount(IRON_INGOT, 3) + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(IRON_INGOT, 3), + new ResourceAmount(STONE, 2) ); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -303,9 +317,311 @@ void shouldCompleteTaskWithExternalPattern() { assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(IRON_INGOT, 3), - new ResourceAmount(IRON_PICKAXE, 1) + new ResourceAmount(IRON_PICKAXE, 1), + new ResourceAmount(STONE, 2) + ); + assertThat(task.copyInternalStorageState()).isEmpty(); + } + + @Test + void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(COBBLESTONE, 64) + ); + final PatternRepository patterns = patterns(STONE_PATTERN, SMOOTH_STONE_PATTERN); + final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); + final ExternalPatternInputSinkBuilder.Sink cobblestoneSink = sinkBuilder.storageSink(STONE_PATTERN); + final ExternalPatternInputSinkBuilder.Sink stoneSink = sinkBuilder.storageSink(SMOOTH_STONE_PATTERN); + final ExternalPatternInputSink sink = sinkBuilder.build(); + final Task task = getRunningTask(storage, patterns, sink, SMOOTH_STONE, 4); + + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 60) + ); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + + // Act & assert + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly( + new ResourceAmount(COBBLESTONE, 3) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 1) + ); + assertThat(stoneSink.getAll()).isEmpty(); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly( + new ResourceAmount(COBBLESTONE, 2) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 2) + ); + assertThat(stoneSink.getAll()).isEmpty(); + + storage.insert(STONE, 2, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(COBBLESTONE, 2), + new ResourceAmount(STONE, 2) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 2) + ); + assertThat(stoneSink.getAll()).isEmpty(); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(COBBLESTONE, 1), + new ResourceAmount(STONE, 1) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 3) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 1) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 2) + ); + + storage.insert(STONE, 2, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly( + new ResourceAmount(STONE, 2) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 2) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 1) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 3) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 4) + ); + + storage.insert(SMOOTH_STONE, 4, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(SMOOTH_STONE, 4) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 4) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(COBBLESTONE, 60), + new ResourceAmount(SMOOTH_STONE, 4) + ); + } + + @Test + void shouldCompleteTaskWithExternalPatternsThatShareTheSameOutputResources() { + // Arrange + final Pattern aToStonePattern = pattern(PatternType.EXTERNAL) + .ingredient(A, 1) + .output(STONE, 1) + .build(); + final Pattern stoneBricksPattern = pattern() + .ingredient(STONE, 1) + .ingredient(STONE, 1) + .output(STONE_BRICKS, 1) + .build(); + final RootStorage storage = storage( + new ResourceAmount(COBBLESTONE, 1), + new ResourceAmount(A, 1) ); + final PatternRepository patterns = patterns(STONE_PATTERN, aToStonePattern, stoneBricksPattern); + final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); + final ExternalPatternInputSinkBuilder.Sink cobblestoneSink = sinkBuilder.storageSink(STONE_PATTERN); + final ExternalPatternInputSinkBuilder.Sink aSink = sinkBuilder.storageSink(aToStonePattern); + final ExternalPatternInputSink sink = sinkBuilder.build(); + final Task task = getRunningTask(storage, patterns, sink, STONE_BRICKS, 1); + + assertThat(storage.getAll()).isEmpty(); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(COBBLESTONE, 1), + new ResourceAmount(A, 1) + ); + + // Act & assert + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(storage.getAll()).isEmpty(); assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 1) + ); + assertThat(aSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(A, 1) + ); + + storage.insert(STONE, 1, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(storage.getAll()).isEmpty(); + assertThat(task.copyInternalStorageState()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 1) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 1) + ); + assertThat(aSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(A, 1) + ); + } + + @Test + void shouldNotCompleteTaskWithExternalPatternIfSinkDoesNotAcceptResources() { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(STICKS, 2 * 2), + new ResourceAmount(IRON_ORE, 3 * 2) + ); + final PatternRepository patterns = patterns(IRON_INGOT_PATTERN, IRON_PICKAXE_PATTERN); + final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); + final ExternalPatternInputSinkBuilder.Sink ironOreSink = sinkBuilder.storageSink(IRON_INGOT_PATTERN); + ironOreSink.setEnabled(false); + final ExternalPatternInputSink sink = sinkBuilder.build(); + final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 2); + + assertThat(storage.getAll()).isEmpty(); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 3 * 2), + new ResourceAmount(STICKS, 2 * 2) + ); + + // Act & assert + for (int i = 0; i < 6; ++i) { + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 3 * 2), + new ResourceAmount(STICKS, 2 * 2) + ); + } + + ironOreSink.setEnabled(true); + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, (3 * 2) - 1), + new ResourceAmount(STICKS, 2 * 2) + ); + assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 1) + ); + } + + @Test + void shouldNotCompleteTaskWithExternalPatternIfSinkDoesNotAcceptResourcesOnlyWhenExecuting() { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(STICKS, 2), + new ResourceAmount(IRON_ORE, 3) + ); + final PatternRepository patterns = patterns(IRON_INGOT_PATTERN, IRON_PICKAXE_PATTERN); + final ExternalPatternInputSink sink = (pattern, resources, action) -> + action == Action.SIMULATE; + final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); + + assertThat(storage.getAll()).isEmpty(); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 3), + new ResourceAmount(STICKS, 2) + ); + + // Act & assert + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 2), // we have voided 1 iron ore + new ResourceAmount(STICKS, 2) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 1), // we have voided 1 iron ore + new ResourceAmount(STICKS, 2) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly(new ResourceAmount(STICKS, 2)); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly(new ResourceAmount(STICKS, 2)); } @Test @@ -318,8 +634,8 @@ void shouldPartiallyReturnInternalStorage() { new ResourceAmount(SIGN, 10) ); final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); - final TaskImpl task = getTaskReadyToReturnInternalStorage( - storage, patterns, ExternalPatternInputSink.EMPTY, CRAFTING_TABLE, 3 + final Task task = getTaskReadyToReturnInternalStorage( + storage, patterns, EMPTY_SINK, CRAFTING_TABLE, 3 ); // Act & assert @@ -330,20 +646,20 @@ void shouldPartiallyReturnInternalStorage() { final RootStorage returnStorage = new RootStorageImpl(); returnStorage.addSource(new LimitedStorageImpl(2)); - task.step(returnStorage, ExternalPatternInputSink.EMPTY); + task.step(returnStorage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); assertThat(returnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(CRAFTING_TABLE, 2) ); - task.step(returnStorage, ExternalPatternInputSink.EMPTY); + task.step(returnStorage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); assertThat(returnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(CRAFTING_TABLE, 2) ); returnStorage.addSource(new StorageImpl()); - task.step(returnStorage, ExternalPatternInputSink.EMPTY); + task.step(returnStorage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); assertThat(returnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(CRAFTING_TABLE, 3) diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanTest.java index e84ad72c9..2195bfc4d 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanTest.java @@ -18,7 +18,7 @@ import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_PLANKS; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_LOG; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_PLANKS; -import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskCraftingCalculatorListener.calculatePlan; +import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskPlanCraftingCalculatorListener.calculatePlan; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.CRAFTING_TABLE_PATTERN; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.OAK_PLANKS_PATTERN; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.SPRUCE_PLANKS_PATTERN; @@ -58,13 +58,14 @@ void testPlanTaskWithIngredientsUsedFromRootStorageAndInternalStorageWithChildPa assertThat(optionalPlan).isPresent(); final TaskPlan plan = optionalPlan.get(); + assertThat(plan.pattern()).isEqualTo(CRAFTING_TABLE_PATTERN); assertThat(plan.initialRequirements()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(OAK_PLANKS, 4), new ResourceAmount(OAK_LOG, 1), new ResourceAmount(SPRUCE_LOG, 1) ); assertThat(plan.patterns()).containsOnlyKeys(CRAFTING_TABLE_PATTERN, OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN); - assertThat(plan.pattern(CRAFTING_TABLE_PATTERN)) + assertThat(plan.getPattern(CRAFTING_TABLE_PATTERN)) .usingRecursiveComparison() .isEqualTo(new TaskPlan.PatternPlan(3, Map.of( 0, Map.of(OAK_PLANKS, 3L), @@ -72,12 +73,12 @@ void testPlanTaskWithIngredientsUsedFromRootStorageAndInternalStorageWithChildPa 2, Map.of(OAK_PLANKS, 2L, SPRUCE_PLANKS, 1L), 3, Map.of(SPRUCE_PLANKS, 3L) ))); - assertThat(plan.pattern(OAK_PLANKS_PATTERN)) + assertThat(plan.getPattern(OAK_PLANKS_PATTERN)) .usingRecursiveComparison() .isEqualTo(new TaskPlan.PatternPlan(1, Map.of( 0, Map.of(OAK_LOG, 1L) ))); - assertThat(plan.pattern(SPRUCE_PLANKS_PATTERN)) + assertThat(plan.getPattern(SPRUCE_PLANKS_PATTERN)) .usingRecursiveComparison() .isEqualTo(new TaskPlan.PatternPlan(1, Map.of( 0, Map.of(SPRUCE_LOG, 1L) @@ -106,9 +107,9 @@ void shouldNotModifyPlan() { .isInstanceOf(UnsupportedOperationException.class); assertThatThrownBy(() -> plan.patterns().clear()) .isInstanceOf(UnsupportedOperationException.class); - assertThatThrownBy(() -> plan.pattern(CRAFTING_TABLE_PATTERN).ingredients().clear()) + assertThatThrownBy(() -> plan.getPattern(CRAFTING_TABLE_PATTERN).ingredients().clear()) .isInstanceOf(UnsupportedOperationException.class); - assertThatThrownBy(() -> plan.pattern(CRAFTING_TABLE_PATTERN).ingredients().get(0).put(OAK_LOG, 1L)) + assertThatThrownBy(() -> plan.getPattern(CRAFTING_TABLE_PATTERN).ingredients().get(0).put(OAK_LOG, 1L)) .isInstanceOf(UnsupportedOperationException.class); } } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskUtil.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskUtil.java index ff4559040..a66549cbe 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskUtil.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskUtil.java @@ -9,19 +9,23 @@ import com.refinedmods.refinedstorage.api.storage.root.RootStorage; import static com.refinedmods.refinedstorage.api.autocrafting.PatternBuilder.pattern; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.COBBLESTONE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_INGOT; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_ORE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_PICKAXE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_PLANKS; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SMOOTH_STONE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_LOG; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_PLANKS; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STICKS; -import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskCraftingCalculatorListener.calculatePlan; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STONE; +import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskPlanCraftingCalculatorListener.calculatePlan; import static org.assertj.core.api.Assertions.assertThat; final class TaskUtil { + static final ExternalPatternInputSink EMPTY_SINK = (pattern, resources, action) -> false; static final Pattern OAK_PLANKS_PATTERN = pattern() .ingredient(OAK_LOG, 1) .output(OAK_PLANKS, 4) @@ -48,38 +52,46 @@ final class TaskUtil { .ingredient(STICKS, 2) .output(IRON_PICKAXE, 1) .build(); + static final Pattern STONE_PATTERN = pattern(PatternType.EXTERNAL) + .ingredient(COBBLESTONE, 1) + .output(STONE, 1) + .build(); + static final Pattern SMOOTH_STONE_PATTERN = pattern(PatternType.EXTERNAL) + .ingredient(STONE, 1) + .output(SMOOTH_STONE, 1) + .build(); private TaskUtil() { } - static TaskImpl getTask(final RootStorage storage, - final PatternRepository patterns, - final ResourceKey resource, - final long amount) { + static Task getTask(final RootStorage storage, + final PatternRepository patterns, + final ResourceKey resource, + final long amount) { final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); - final TaskImpl task = calculatePlan(sut, resource, amount).map(TaskImpl::new).orElseThrow(); + final Task task = calculatePlan(sut, resource, amount).map(TaskImpl::fromPlan).orElseThrow(); storage.addListener(task); return task; } - static TaskImpl getRunningTask(final RootStorage storage, - final PatternRepository patterns, - final ExternalPatternInputSink externalPatternInputSink, - final ResourceKey resource, - final long amount) { - final TaskImpl task = getTask(storage, patterns, resource, amount); + static Task getRunningTask(final RootStorage storage, + final PatternRepository patterns, + final ExternalPatternInputSink externalPatternInputSink, + final ResourceKey resource, + final long amount) { + final Task task = getTask(storage, patterns, resource, amount); assertThat(task.getState()).isEqualTo(TaskState.READY); task.step(storage, externalPatternInputSink); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); return task; } - static TaskImpl getTaskReadyToReturnInternalStorage(final RootStorage storage, - final PatternRepository patterns, - final ExternalPatternInputSink externalPatternInputSink, - final ResourceKey resource, - final long amount) { - final TaskImpl task = getRunningTask(storage, patterns, externalPatternInputSink, resource, amount); + static Task getTaskReadyToReturnInternalStorage(final RootStorage storage, + final PatternRepository patterns, + final ExternalPatternInputSink externalPatternInputSink, + final ResourceKey resource, + final long amount) { + final Task task = getRunningTask(storage, patterns, externalPatternInputSink, resource, amount); int tries = 0; while (task.getState() != TaskState.RETURNING_INTERNAL_STORAGE && tries < 10) { task.step(storage, externalPatternInputSink); diff --git a/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/autocrafting/PatternProviderExternalPatternInputSinkFactory.java b/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/autocrafting/PatternProviderExternalPatternInputSinkFactory.java new file mode 100644 index 000000000..7a207548f --- /dev/null +++ b/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/autocrafting/PatternProviderExternalPatternInputSinkFactory.java @@ -0,0 +1,14 @@ +package com.refinedmods.refinedstorage.common.api.autocrafting; + +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import org.apiguardian.api.API; + +@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") +@FunctionalInterface +public interface PatternProviderExternalPatternInputSinkFactory { + PatternProviderExternalPatternInputSink create(ServerLevel level, BlockPos pos, Direction direction); +} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/AbstractModInitializer.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/AbstractModInitializer.java index ace0b4849..22d59edc4 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/AbstractModInitializer.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/AbstractModInitializer.java @@ -28,7 +28,6 @@ import com.refinedmods.refinedstorage.common.autocrafting.monitor.AutocraftingMonitorBlockEntity; import com.refinedmods.refinedstorage.common.autocrafting.monitor.AutocraftingMonitorContainerMenu; import com.refinedmods.refinedstorage.common.autocrafting.monitor.AutocraftingMonitorData; -import com.refinedmods.refinedstorage.common.autocrafting.monitor.TaskStatusProviderImpl; import com.refinedmods.refinedstorage.common.autocrafting.monitor.WirelessAutocraftingMonitorContainerMenu; import com.refinedmods.refinedstorage.common.autocrafting.patterngrid.PatternGridBlockEntity; import com.refinedmods.refinedstorage.common.autocrafting.patterngrid.PatternGridContainerMenu; @@ -277,7 +276,6 @@ private void registerNetworkComponents() { AutocraftingNetworkComponent.class, network -> new AutocraftingNetworkComponentImpl( () -> network.getComponent(StorageNetworkComponent.class), - new TaskStatusProviderImpl(), ServerListener.getAutocraftingPool() ) ); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/Platform.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/Platform.java index 6ddd37732..15cc38c2b 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/Platform.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/Platform.java @@ -4,6 +4,7 @@ import com.refinedmods.refinedstorage.api.grid.view.GridResourceFactory; import com.refinedmods.refinedstorage.api.network.energy.EnergyStorage; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.api.grid.strategy.GridInsertionStrategyFactory; import com.refinedmods.refinedstorage.common.api.support.network.NetworkNodeContainerProvider; import com.refinedmods.refinedstorage.common.api.support.resource.FluidOperationResult; @@ -70,6 +71,8 @@ public interface Platform { GridInsertionStrategyFactory getDefaultGridInsertionStrategyFactory(); + PatternProviderExternalPatternInputSinkFactory getPatternProviderExternalPatternInputSinkFactory(); + FluidRenderer getFluidRenderer(); Optional drainContainer(ItemStack container); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/PlatformProxy.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/PlatformProxy.java index 42b762805..6a0d41694 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/PlatformProxy.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/PlatformProxy.java @@ -4,6 +4,7 @@ import com.refinedmods.refinedstorage.api.grid.view.GridResourceFactory; import com.refinedmods.refinedstorage.api.network.energy.EnergyStorage; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.api.grid.strategy.GridInsertionStrategyFactory; import com.refinedmods.refinedstorage.common.api.support.network.NetworkNodeContainerProvider; import com.refinedmods.refinedstorage.common.api.support.resource.FluidOperationResult; @@ -103,6 +104,11 @@ public GridInsertionStrategyFactory getDefaultGridInsertionStrategyFactory() { return ensureLoaded().getDefaultGridInsertionStrategyFactory(); } + @Override + public PatternProviderExternalPatternInputSinkFactory getPatternProviderExternalPatternInputSinkFactory() { + return ensureLoaded().getPatternProviderExternalPatternInputSinkFactory(); + } + @Override public FluidRenderer getFluidRenderer() { return ensureLoaded().getFluidRenderer(); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/PatternItem.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/PatternItem.java index 4d63dda66..74eddd56b 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/PatternItem.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/PatternItem.java @@ -142,13 +142,13 @@ public Optional getPattern(final ItemStack stack, final Level level) { return Optional.empty(); } return switch (state.type()) { - case CRAFTING -> resolver.getCraftingPattern(stack, level) + case CRAFTING -> resolver.getCraftingPattern(stack, level, state) .map(PatternResolver.ResolvedCraftingPattern::pattern); - case PROCESSING -> resolver.getProcessingPattern(stack) + case PROCESSING -> resolver.getProcessingPattern(state, stack) .map(PatternResolver.ResolvedProcessingPattern::pattern); - case STONECUTTER -> resolver.getStonecutterPattern(stack, level) + case STONECUTTER -> resolver.getStonecutterPattern(stack, level, state) .map(PatternResolver.ResolvedStonecutterPattern::pattern); - case SMITHING_TABLE -> resolver.getSmithingTablePattern(stack, level) + case SMITHING_TABLE -> resolver.getSmithingTablePattern(state, stack, level) .map(PatternResolver.ResolvedSmithingTablePattern::pattern); }; } @@ -187,7 +187,7 @@ private Optional getCachedCraftingPatte final Level level) { final PatternResolver.ResolvedCraftingPattern pattern = CRAFTING_PATTERN_CACHE.get(state.id()); if (pattern == null) { - return resolver.getCraftingPattern(stack, level).map(resolved -> { + return resolver.getCraftingPattern(stack, level, state).map(resolved -> { CRAFTING_PATTERN_CACHE.put(state.id(), resolved); return resolved; }); @@ -202,7 +202,7 @@ private Optional getCachedSmithing ) { final PatternResolver.ResolvedSmithingTablePattern pattern = SMITHING_TABLE_PATTERN_CACHE.get(state.id()); if (pattern == null) { - return resolver.getSmithingTablePattern(stack, level).map(resolved -> { + return resolver.getSmithingTablePattern(state, stack, level).map(resolved -> { SMITHING_TABLE_PATTERN_CACHE.put(state.id(), resolved); return resolved; }); @@ -217,7 +217,7 @@ private Optional getCachedStonecutte ) { final PatternResolver.ResolvedStonecutterPattern pattern = STONE_CUTTER_PATTERN_CACHE.get(state.id()); if (pattern == null) { - return resolver.getStonecutterPattern(stack, level).map(resolved -> { + return resolver.getStonecutterPattern(stack, level, state).map(resolved -> { STONE_CUTTER_PATTERN_CACHE.put(state.id(), resolved); return resolved; }); @@ -231,7 +231,7 @@ private Optional getCachedProcessingP ) { final PatternResolver.ResolvedProcessingPattern pattern = PROCESSING_PATTERN_CACHE.get(state.id()); if (pattern == null) { - return resolver.getProcessingPattern(stack).map(resolved -> { + return resolver.getProcessingPattern(state, stack).map(resolved -> { PROCESSING_PATTERN_CACHE.put(state.id(), resolved); return resolved; }); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/PatternResolver.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/PatternResolver.java index c71446a85..63ce511b0 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/PatternResolver.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/PatternResolver.java @@ -14,6 +14,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.stream.Stream; import net.minecraft.world.item.ItemStack; @@ -29,15 +30,18 @@ public class PatternResolver { PatternResolver() { } - Optional getCraftingPattern(final ItemStack stack, final Level level) { + Optional getCraftingPattern(final ItemStack stack, + final Level level, + final PatternState patternState) { final CraftingPatternState craftingState = stack.get(DataComponents.INSTANCE.getCraftingPatternState()); if (craftingState == null) { return Optional.empty(); } - return getCraftingPattern(level, craftingState); + return getCraftingPattern(level, patternState, craftingState); } private Optional getCraftingPattern(final Level level, + final PatternState patternState, final CraftingPatternState state) { final RecipeMatrixContainer craftingMatrix = getFilledCraftingMatrix(state); final CraftingInput.Positioned positionedCraftingInput = craftingMatrix.asPositionedCraftInput(); @@ -45,7 +49,7 @@ private Optional getCraftingPattern(final Level level, return level.getRecipeManager() .getRecipeFor(RecipeType.CRAFTING, craftingInput, level) .map(RecipeHolder::value) - .map(recipe -> toCraftingPattern(level, recipe, craftingInput, state)); + .map(recipe -> toCraftingPattern(level, recipe, craftingInput, state, patternState)); } private RecipeMatrixContainer getFilledCraftingMatrix(final CraftingPatternState state) { @@ -61,11 +65,12 @@ private RecipeMatrixContainer getFilledCraftingMatrix(final CraftingPatternState private ResolvedCraftingPattern toCraftingPattern(final Level level, final CraftingRecipe recipe, final CraftingInput craftingInput, - final CraftingPatternState state) { + final CraftingPatternState state, + final PatternState patternState) { final List> inputs = getInputs(recipe, state); final ResourceAmount output = getOutput(level, recipe, craftingInput); final List byproducts = getByproducts(recipe, craftingInput); - return new ResolvedCraftingPattern(inputs, output, byproducts); + return new ResolvedCraftingPattern(patternState.id(), inputs, output, byproducts); } private List> getInputs(final CraftingRecipe recipe, final CraftingPatternState state) { @@ -101,7 +106,7 @@ private List getByproducts(final CraftingRecipe recipe, final Cr .toList(); } - Optional getProcessingPattern(final ItemStack stack) { + Optional getProcessingPattern(final PatternState patternState, final ItemStack stack) { final ProcessingPatternState state = stack.get( DataComponents.INSTANCE.getProcessingPatternState() ); @@ -109,20 +114,22 @@ Optional getProcessingPattern(final ItemStack stack) return Optional.empty(); } return Optional.of( - new ResolvedProcessingPattern(state.getIngredients(), state.getFlatOutputs()) + new ResolvedProcessingPattern(patternState.id(), state.getIngredients(), state.getFlatOutputs()) ); } Optional getStonecutterPattern(final ItemStack stack, - final Level level) { + final Level level, + final PatternState patternState) { final StonecutterPatternState state = stack.get(DataComponents.INSTANCE.getStonecutterPatternState()); if (state == null) { return Optional.empty(); } - return getStonecutterPattern(level, state); + return getStonecutterPattern(level, patternState, state); } private Optional getStonecutterPattern(final Level level, + final PatternState patternState, final StonecutterPatternState state) { final SingleRecipeInput input = new SingleRecipeInput(state.input().toItemStack()); final ItemStack selectedOutput = state.selectedOutput().toItemStack(); @@ -131,6 +138,7 @@ private Optional getStonecutterPattern(final Level l final ItemStack output = recipe.value().assemble(input, level.registryAccess()); if (ItemStack.isSameItemSameComponents(output, selectedOutput)) { return Optional.of(new ResolvedStonecutterPattern( + patternState.id(), state.input(), ItemResource.ofItemStack(output) )); @@ -139,16 +147,18 @@ private Optional getStonecutterPattern(final Level l return Optional.empty(); } - Optional getSmithingTablePattern(final ItemStack stack, + Optional getSmithingTablePattern(final PatternState patternState, + final ItemStack stack, final Level level) { final SmithingTablePatternState state = stack.get(DataComponents.INSTANCE.getSmithingTablePatternState()); if (state == null) { return Optional.empty(); } - return getSmithingTablePattern(level, state); + return getSmithingTablePattern(level, patternState, state); } private Optional getSmithingTablePattern(final Level level, + final PatternState patternState, final SmithingTablePatternState state) { final SmithingRecipeInput input = new SmithingRecipeInput( state.template().toItemStack(), @@ -158,6 +168,7 @@ private Optional getSmithingTablePattern(final Lev return level.getRecipeManager() .getRecipeFor(RecipeType.SMITHING, input, level) .map(recipe -> new ResolvedSmithingTablePattern( + patternState.id(), state.template(), state.base(), state.addition(), @@ -168,10 +179,12 @@ private Optional getSmithingTablePattern(final Lev public record ResolvedCraftingPattern(List> inputs, ResourceAmount output, Pattern pattern) { - ResolvedCraftingPattern(final List> inputs, + ResolvedCraftingPattern(final UUID id, + final List> inputs, final ResourceAmount output, final List byproducts) { this(inputs, output, new Pattern( + id, inputs.stream() .filter(i -> !i.isEmpty()) .map(i -> new Ingredient(1, i)) @@ -183,16 +196,19 @@ public record ResolvedCraftingPattern(List> inputs, } public record ResolvedProcessingPattern(Pattern pattern) { - ResolvedProcessingPattern(final List ingredients, final List outputs) { - this(new Pattern(ingredients, outputs, PatternType.EXTERNAL)); + ResolvedProcessingPattern(final UUID id, + final List ingredients, + final List outputs) { + this(new Pattern(id, ingredients, outputs, PatternType.EXTERNAL)); } } public record ResolvedStonecutterPattern(ItemResource input, ItemResource output, Pattern pattern) { - ResolvedStonecutterPattern(final ItemResource input, final ItemResource output) { + ResolvedStonecutterPattern(final UUID id, final ItemResource input, final ItemResource output) { this(input, output, new Pattern( + id, List.of(new Ingredient(1, List.of(input))), List.of(new ResourceAmount(output, 1)), PatternType.INTERNAL @@ -205,11 +221,13 @@ public record ResolvedSmithingTablePattern(ItemResource template, ItemResource addition, ItemResource output, Pattern pattern) { - ResolvedSmithingTablePattern(final ItemResource template, + ResolvedSmithingTablePattern(final UUID id, + final ItemResource template, final ItemResource base, final ItemResource addition, final ItemResource output) { this(template, base, addition, output, new Pattern( + id, List.of(single(template), single(base), single(addition)), List.of(new ResourceAmount(output, 1)), PatternType.INTERNAL diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterBlockEntity.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterBlockEntity.java index 753933612..4167507bb 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterBlockEntity.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterBlockEntity.java @@ -28,6 +28,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.codec.StreamEncoder; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.Nameable; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -302,6 +303,16 @@ public void setLevel(final Level level) { } } + @Override + protected void initialize(final ServerLevel level, final Direction direction) { + super.initialize(level, direction); + final Direction incomingDirection = direction.getOpposite(); + final BlockPos sourcePosition = worldPosition.relative(direction); + mainNetworkNode.setExternalPatternInputSink( + Platform.INSTANCE.getPatternProviderExternalPatternInputSinkFactory() + .create(level, sourcePosition, incomingDirection)); + } + @Override public void patternChanged(final int slot) { if (level == null) { diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/TaskStatusProviderImpl.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/TaskStatusProviderImpl.java deleted file mode 100644 index 5657c23aa..000000000 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/TaskStatusProviderImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.refinedmods.refinedstorage.common.autocrafting.monitor; - -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusProvider; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class TaskStatusProviderImpl implements TaskStatusProvider { - private final List statuses = new ArrayList<>(); - private final Set listeners = new HashSet<>(); - - @Override - public List getStatuses() { - return Collections.unmodifiableList(statuses); - } - - @Override - public void addListener(final TaskStatusListener listener) { - listeners.add(listener); - } - - @Override - public void removeListener(final TaskStatusListener listener) { - listeners.remove(listener); - } - - @Override - public void cancel(final TaskId taskId) { - final TaskStatus status = statuses.stream() - .filter(s -> s.info().id().equals(taskId)) - .findFirst() - .orElse(null); - if (status != null) { - statuses.remove(status); - listeners.forEach(l -> l.taskRemoved(taskId)); - } - } - - @Override - public void cancelAll() { - final List copy = new ArrayList<>(statuses); - statuses.clear(); - copy.forEach(s -> listeners.forEach(l -> l.taskRemoved(s.info().id()))); - } -} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewContainerMenu.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewContainerMenu.java index 19da141a1..f0885ce2e 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewContainerMenu.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewContainerMenu.java @@ -94,8 +94,8 @@ void sendRequest(final double amount, final boolean notify) { currentRequest.sendRequest(amount, notify); } - public void responseReceived(final UUID id, final boolean started) { - if (!currentRequest.getId().equals(id) || !started) { + public void responseReceived(final UUID id, final boolean success) { + if (!currentRequest.getId().equals(id) || !success) { return; } requests.remove(currentRequest); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridBlockEntity.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridBlockEntity.java index b7dab814a..b7ae79d25 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridBlockEntity.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridBlockEntity.java @@ -118,10 +118,13 @@ public CompletableFuture getMaxAmount(final ResourceKey resource) { } @Override - public boolean startTask(final ResourceKey resource, final long amount, final Actor actor, final boolean notify) { + public CompletableFuture startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final boolean notify) { final Network network = mainNetworkNode.getNetwork(); if (network == null) { - return false; + return CompletableFuture.completedFuture(false); } return network.getComponent(AutocraftingNetworkComponent.class).startTask(resource, amount, actor, notify); } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridContainerMenu.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridContainerMenu.java index 4782bf89b..ca1ca42b2 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridContainerMenu.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridContainerMenu.java @@ -471,7 +471,10 @@ public CompletableFuture getMaxAmount(final ResourceKey resource) { } @Override - public boolean startTask(final ResourceKey resource, final long amount, final Actor actor, final boolean notify) { + public CompletableFuture startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final boolean notify) { return requireNonNull(grid).startTask(resource, amount, actor, notify); } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/ClientCraftingGrid.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/ClientCraftingGrid.java index f5b6091b7..cc881bdc5 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/ClientCraftingGrid.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/ClientCraftingGrid.java @@ -116,7 +116,10 @@ public CompletableFuture getMaxAmount(final ResourceKey resource) { } @Override - public boolean startTask(final ResourceKey resource, final long amount, final Actor actor, final boolean notify) { + public CompletableFuture startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final boolean notify) { throw new UnsupportedOperationException(); } } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/WirelessGrid.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/WirelessGrid.java index 803147c92..a1e5f9033 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/WirelessGrid.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/WirelessGrid.java @@ -131,9 +131,12 @@ public CompletableFuture getMaxAmount(final ResourceKey resource) { } @Override - public boolean startTask(final ResourceKey resource, final long amount, final Actor actor, final boolean notify) { + public CompletableFuture startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final boolean notify) { return getAutocrafting() .map(autocrafting -> autocrafting.startTask(resource, amount, actor, notify)) - .orElse(false); + .orElse(CompletableFuture.completedFuture(false)); } } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storage/portablegrid/PortableGrid.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storage/portablegrid/PortableGrid.java index 2e0f77e6a..ce171b8dc 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storage/portablegrid/PortableGrid.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storage/portablegrid/PortableGrid.java @@ -142,7 +142,10 @@ public CompletableFuture getMaxAmount(final ResourceKey resource) { } @Override - public boolean startTask(final ResourceKey resource, final long amount, final Actor actor, final boolean notify) { - return false; + public CompletableFuture startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final boolean notify) { + return CompletableFuture.completedFuture(false); } } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/AutocraftingStorageMonitorContainerMenu.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/AutocraftingStorageMonitorContainerMenu.java index cbee785a5..ef347cbb2 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/AutocraftingStorageMonitorContainerMenu.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/AutocraftingStorageMonitorContainerMenu.java @@ -51,7 +51,10 @@ public CompletableFuture getMaxAmount(final ResourceKey resource) { } @Override - public boolean startTask(final ResourceKey resource, final long amount, final Actor actor, final boolean notify) { + public CompletableFuture startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final boolean notify) { return requireNonNull(storageMonitor).startTask(resource, amount, actor, notify); } } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/StorageMonitorBlockEntity.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/StorageMonitorBlockEntity.java index 98e76086f..163ff2d37 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/StorageMonitorBlockEntity.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/StorageMonitorBlockEntity.java @@ -385,10 +385,13 @@ public CompletableFuture getMaxAmount(final ResourceKey resource) { } @Override - public boolean startTask(final ResourceKey resource, final long amount, final Actor actor, final boolean notify) { + public CompletableFuture startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final boolean notify) { final Network network = mainNetworkNode.getNetwork(); if (network == null) { - return false; + return CompletableFuture.completedFuture(false); } return network.getComponent(AutocraftingNetworkComponent.class).startTask(resource, amount, actor, notify); } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/AutocraftingRequestPacket.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/AutocraftingRequestPacket.java index 720fadd81..98a4c652a 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/AutocraftingRequestPacket.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/AutocraftingRequestPacket.java @@ -39,10 +39,8 @@ public static void handle(final AutocraftingRequestPacket packet, final PacketCo final Player player = ctx.getPlayer(); if (player.containerMenu instanceof PreviewProvider provider) { final PlayerActor playerActor = new PlayerActor(player); - final boolean started = provider.startTask( - packet.resource, packet.amount, playerActor, packet.notifyPlayer - ); - S2CPackets.sendAutocraftingResponse((ServerPlayer) player, packet.id, started); + provider.startTask(packet.resource, packet.amount, playerActor, packet.notifyPlayer) + .thenAccept(success -> S2CPackets.sendAutocraftingResponse((ServerPlayer) player, packet.id, success)); } } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingResponsePacket.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingResponsePacket.java index 1b1467d63..161ec4f98 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingResponsePacket.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingResponsePacket.java @@ -12,7 +12,7 @@ import static com.refinedmods.refinedstorage.common.util.IdentifierUtil.createIdentifier; -public record AutocraftingResponsePacket(UUID id, boolean started) implements CustomPacketPayload { +public record AutocraftingResponsePacket(UUID id, boolean success) implements CustomPacketPayload { public static final Type PACKET_TYPE = new Type<>( createIdentifier("autocrafting_response") ); @@ -20,12 +20,12 @@ public record AutocraftingResponsePacket(UUID id, boolean started) implements Cu public static final StreamCodec STREAM_CODEC = StreamCodec.composite( UUIDUtil.STREAM_CODEC, AutocraftingResponsePacket::id, - ByteBufCodecs.BOOL, AutocraftingResponsePacket::started, + ByteBufCodecs.BOOL, AutocraftingResponsePacket::success, AutocraftingResponsePacket::new ); public static void handle(final AutocraftingResponsePacket packet) { - ClientPlatformUtil.autocraftingResponseReceived(packet.id, packet.started); + ClientPlatformUtil.autocraftingResponseReceived(packet.id, packet.success); } @Override diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/S2CPackets.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/S2CPackets.java index e603b91c7..4b0b6dfeb 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/S2CPackets.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/S2CPackets.java @@ -105,8 +105,8 @@ public static void sendAutocraftingPreviewMaxAmountResponse(final ServerPlayer p public static void sendAutocraftingResponse(final ServerPlayer player, final UUID id, - final boolean started) { - Platform.INSTANCE.sendPacketToClient(player, new AutocraftingResponsePacket(id, started)); + final boolean success) { + Platform.INSTANCE.sendPacketToClient(player, new AutocraftingResponsePacket(id, success)); } public static void sendAutocraftingMonitorTaskAdded(final ServerPlayer player, final TaskStatus taskStatus) { diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java index b47b15fe0..179d5ad8a 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java @@ -46,9 +46,9 @@ public static void autocraftingPreviewResponseReceived(final UUID id, final Prev } } - public static void autocraftingResponseReceived(final UUID id, final boolean started) { + public static void autocraftingResponseReceived(final UUID id, final boolean success) { if (Minecraft.getInstance().screen instanceof AutocraftingPreviewScreen screen) { - screen.getMenu().responseReceived(id, started); + screen.getMenu().responseReceived(id, success); } } diff --git a/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/cancel.png b/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/cancel.png index ac98defee61018550f1c35990563b68efc4fd9fb..6199afc4fb5cbc0dcdfda0ff6184f644f8071013 100644 GIT binary patch delta 211 zcmV;^04)ET0>lE4R)4=qL_t(2Q+1Dx34<^cgkM4(M2LcG2;HF*bce3u1l^(F8iE8m zh+vv~^+FQ#gZ$sUcQI1&ZkljxmB+RfnslJh@4KFfvsPCcb)vWpL0(;lZDL&4!gSMJ z1eIH3?A#ZAt^(OC+WkCP{ delta 171 zcmV;c0960P0-FMmR)3aBL_t(2QYPmD2pT8TxrRoY!r<+A zJA#Sv6-c~q^P9+xuCBN9Q|YHE$XJ8HGRw40X7BY)Q6C6bc`nPkN;{50#i41m5JX6+ z_8uTyIA;NJFOZ@-rhy+Z2^nN<1Q3%*Ab>CFA%K`)@_XB;nN%*#QV!VhN5HAa9c%0Vb?%N)-SA diff --git a/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/reset.png b/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/reset.png index c4dd7b8372d0c5e7727c4a2e13f80f3a25bec47a..dce66e8ecf6bc5eafe68f47b8412b4d8b6e6be6d 100644 GIT binary patch delta 161 zcmV;S0ABy+0g?icR)392L_t(2Q+<)a4uBvGL(%YM9JuqP+&S=NBxP&ozj=2^# z*-~Jwbv#QW=ls%mqQY9M#W99O;6@4&ODXnP;et78z9AD8Mrgu%bLU0uSD`MmU&V{E z0d$zjPtam(&A#>CzduF$`vV%E6Um=R>pNbYBN{cQ#(U&(dKX!M!a1jD-9X}fP^zKwEmAWWRbxp4y{ee delta 137 zcmV;40CxY90_OpcR)1niL_t(2QK;Uh?VNNO0f!{q{hG(X z%ob-rK$yi0AC@AE)wJb79#80?u8~YmYoUJZ7{LQG#etVE{Q3jXPO5+#a$bo|s(kxY r2b*l`bkkl$ZYwxT+{uRpi2wiq4rN$LW=%~1DgXcg2mk;80FnU`U+Oo< diff --git a/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/start.png b/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/start.png index 5f362e5f39e3abd4a5680f128d7a48f246cee5ac..8ca423d58c136c55bd113976bce659f37cb8327e 100644 GIT binary patch delta 166 zcmV;X09pU}0ha=hR)3O7L_t(2Q)7SumUiv`P$pi$2qM6WmADuf?tKFb{;9kQ7RLsd zpfm)4GyyR_+fX&Y08|q;ji^pi;sy&c-1`clK>*E}IBWoFglR@|CUy-V8(_A8Yye`_ zJ#1hus*{ib)G089*Y}|lYPuYZE+OP!a% za_E2wN`nCtKNAB;BM9TN5yb|O1{nDF@gJB*w-Ln#WB}5PZX;M6EK35wsR8695C%C3 rgkes?@B=Ql!vKl~5Fiw+Z~)Q-^B5HY0|NlEP(4!BFOb^+0Fm<~r$RIl diff --git a/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/FabricStorageExternalPatternInputSinkStrategy.java b/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/FabricStorageExternalPatternInputSinkStrategy.java new file mode 100644 index 000000000..27fa022f6 --- /dev/null +++ b/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/FabricStorageExternalPatternInputSinkStrategy.java @@ -0,0 +1,13 @@ +package com.refinedmods.refinedstorage.fabric.api; + +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; + +import java.util.Collection; + +import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; +import org.apiguardian.api.API; + +@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") +public interface FabricStorageExternalPatternInputSinkStrategy { + boolean accept(Transaction tx, Collection resources); +} diff --git a/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/FabricStorageExternalPatternInputSinkStrategyFactory.java b/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/FabricStorageExternalPatternInputSinkStrategyFactory.java new file mode 100644 index 000000000..e75df1f1f --- /dev/null +++ b/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/FabricStorageExternalPatternInputSinkStrategyFactory.java @@ -0,0 +1,12 @@ +package com.refinedmods.refinedstorage.fabric.api; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import org.apiguardian.api.API; + +@FunctionalInterface +@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") +public interface FabricStorageExternalPatternInputSinkStrategyFactory { + FabricStorageExternalPatternInputSinkStrategy create(ServerLevel level, BlockPos pos, Direction direction); +} diff --git a/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/RefinedStorageFabricApi.java b/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/RefinedStorageFabricApi.java index 557acb7ea..deaeeeb7c 100644 --- a/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/RefinedStorageFabricApi.java +++ b/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/RefinedStorageFabricApi.java @@ -1,6 +1,7 @@ package com.refinedmods.refinedstorage.fabric.api; import com.refinedmods.refinedstorage.api.core.NullableType; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.api.support.network.NetworkNodeContainerProvider; import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup; @@ -12,4 +13,9 @@ public interface RefinedStorageFabricApi { RefinedStorageFabricApi INSTANCE = new RefinedStorageFabricApiProxy(); BlockApiLookup getNetworkNodeContainerProviderLookup(); + + void addStorageExternalPatternInputSinkStrategyFactory( + FabricStorageExternalPatternInputSinkStrategyFactory factory); + + PatternProviderExternalPatternInputSinkFactory getPatternProviderExternalPatternInputSinkFactory(); } diff --git a/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/RefinedStorageFabricApiProxy.java b/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/RefinedStorageFabricApiProxy.java index 26b313b03..c14a2188d 100644 --- a/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/RefinedStorageFabricApiProxy.java +++ b/refinedstorage-fabric-api/src/main/java/com/refinedmods/refinedstorage/fabric/api/RefinedStorageFabricApiProxy.java @@ -1,5 +1,6 @@ package com.refinedmods.refinedstorage.fabric.api; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.api.support.network.NetworkNodeContainerProvider; import javax.annotation.Nullable; @@ -29,4 +30,15 @@ private RefinedStorageFabricApi ensureLoaded() { public BlockApiLookup getNetworkNodeContainerProviderLookup() { return ensureLoaded().getNetworkNodeContainerProviderLookup(); } + + @Override + public void addStorageExternalPatternInputSinkStrategyFactory( + final FabricStorageExternalPatternInputSinkStrategyFactory factory) { + ensureLoaded().addStorageExternalPatternInputSinkStrategyFactory(factory); + } + + @Override + public PatternProviderExternalPatternInputSinkFactory getPatternProviderExternalPatternInputSinkFactory() { + return ensureLoaded().getPatternProviderExternalPatternInputSinkFactory(); + } } diff --git a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ModInitializerImpl.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ModInitializerImpl.java index b4b2812f6..2121bf262 100644 --- a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ModInitializerImpl.java +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ModInitializerImpl.java @@ -85,6 +85,7 @@ import com.refinedmods.refinedstorage.fabric.api.RefinedStorageFabricApi; import com.refinedmods.refinedstorage.fabric.api.RefinedStorageFabricApiProxy; import com.refinedmods.refinedstorage.fabric.api.RefinedStoragePlugin; +import com.refinedmods.refinedstorage.fabric.autocrafting.FabricStorageExternalPatternInputSinkStrategyFactoryImpl; import com.refinedmods.refinedstorage.fabric.constructordestructor.FabricConstructorBlockEntity; import com.refinedmods.refinedstorage.fabric.constructordestructor.FabricDestructorBlockEntity; import com.refinedmods.refinedstorage.fabric.exporter.FabricExporterBlockEntity; @@ -198,6 +199,7 @@ public void onInitialize() { registerImporterTransferStrategyFactories(); registerExporterTransferStrategyFactories(); registerExternalStorageProviderFactories(); + registerExternalPatternInputSinkStrategyFactories(); registerContent(); registerPackets(); registerPacketHandlers(); @@ -297,6 +299,23 @@ private void registerExternalStorageProviderFactories() { ); } + private void registerExternalPatternInputSinkStrategyFactories() { + RefinedStorageFabricApi.INSTANCE.addStorageExternalPatternInputSinkStrategyFactory( + new FabricStorageExternalPatternInputSinkStrategyFactoryImpl<>( + ItemStorage.SIDED, + resource -> resource instanceof ItemResource itemResource + ? toItemVariant(itemResource) : null + ) + ); + RefinedStorageFabricApi.INSTANCE.addStorageExternalPatternInputSinkStrategyFactory( + new FabricStorageExternalPatternInputSinkStrategyFactoryImpl<>( + FluidStorage.SIDED, + resource -> resource instanceof FluidResource fluidResource + ? toFluidVariant(fluidResource) : null + ) + ); + } + private void registerContent() { registerBlocks(new DirectRegistryCallback<>(BuiltInRegistries.BLOCK), BLOCK_ENTITY_PROVIDERS); final DirectRegistryCallback itemRegistryCallback = new DirectRegistryCallback<>(BuiltInRegistries.ITEM); diff --git a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/PlatformImpl.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/PlatformImpl.java index b1674abd4..b69568c33 100644 --- a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/PlatformImpl.java +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/PlatformImpl.java @@ -6,6 +6,7 @@ import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.common.AbstractPlatform; import com.refinedmods.refinedstorage.common.Config; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.api.support.network.NetworkNodeContainerProvider; import com.refinedmods.refinedstorage.common.api.support.resource.FluidOperationResult; import com.refinedmods.refinedstorage.common.support.containermenu.TransferManager; @@ -150,6 +151,11 @@ public GridResourceFactory getFluidGridResourceFactory() { return new FabricFluidGridResourceFactory(); } + @Override + public PatternProviderExternalPatternInputSinkFactory getPatternProviderExternalPatternInputSinkFactory() { + return RefinedStorageFabricApi.INSTANCE.getPatternProviderExternalPatternInputSinkFactory(); + } + @Override public Optional drainContainer(final ItemStack container) { if (container.isEmpty()) { diff --git a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/RefinedStorageFabricApiImpl.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/RefinedStorageFabricApiImpl.java index e129a4bdf..99a0b66f4 100644 --- a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/RefinedStorageFabricApiImpl.java +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/RefinedStorageFabricApiImpl.java @@ -1,8 +1,15 @@ package com.refinedmods.refinedstorage.fabric; import com.refinedmods.refinedstorage.api.core.NullableType; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.api.support.network.NetworkNodeContainerProvider; +import com.refinedmods.refinedstorage.fabric.api.FabricStorageExternalPatternInputSinkStrategyFactory; import com.refinedmods.refinedstorage.fabric.api.RefinedStorageFabricApi; +import com.refinedmods.refinedstorage.fabric.autocrafting.FabricStoragePatternProviderExternalPatternInputSinkFactory; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup; import net.minecraft.core.Direction; @@ -16,9 +23,24 @@ public class RefinedStorageFabricApiImpl implements RefinedStorageFabricApi { NetworkNodeContainerProvider.class, Direction.class ); + private final Set storageExternalPatternInputSinkStrategies + = new HashSet<>(); @Override public BlockApiLookup getNetworkNodeContainerProviderLookup() { return networkNodeContainerProvider; } + + @Override + public void addStorageExternalPatternInputSinkStrategyFactory( + final FabricStorageExternalPatternInputSinkStrategyFactory factory) { + storageExternalPatternInputSinkStrategies.add(factory); + } + + @Override + public PatternProviderExternalPatternInputSinkFactory getPatternProviderExternalPatternInputSinkFactory() { + return new FabricStoragePatternProviderExternalPatternInputSinkFactory( + Collections.unmodifiableSet(storageExternalPatternInputSinkStrategies) + ); + } } diff --git a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStorageExternalPatternInputSinkStrategyFactoryImpl.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStorageExternalPatternInputSinkStrategyFactoryImpl.java new file mode 100644 index 000000000..0ce1182ee --- /dev/null +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStorageExternalPatternInputSinkStrategyFactoryImpl.java @@ -0,0 +1,41 @@ +package com.refinedmods.refinedstorage.fabric.autocrafting; + +import com.refinedmods.refinedstorage.api.core.NullableType; +import com.refinedmods.refinedstorage.api.resource.ResourceKey; +import com.refinedmods.refinedstorage.fabric.api.FabricStorageExternalPatternInputSinkStrategy; +import com.refinedmods.refinedstorage.fabric.api.FabricStorageExternalPatternInputSinkStrategyFactory; + +import java.util.function.Function; + +import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup; +import net.fabricmc.fabric.api.transfer.v1.storage.Storage; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; + +public class FabricStorageExternalPatternInputSinkStrategyFactoryImpl + implements FabricStorageExternalPatternInputSinkStrategyFactory { + private final BlockApiLookup, Direction> lookup; + private final Function toPlatformMapper; + + public FabricStorageExternalPatternInputSinkStrategyFactoryImpl( + final BlockApiLookup, Direction> lookup, + final Function toPlatformMapper + ) { + this.lookup = lookup; + this.toPlatformMapper = toPlatformMapper; + } + + @Override + public FabricStorageExternalPatternInputSinkStrategy create(final ServerLevel level, + final BlockPos pos, + final Direction direction) { + return new FabricStorageExternalPatternInputSinkStrategyImpl<>( + lookup, + toPlatformMapper, + level, + pos, + direction + ); + } +} diff --git a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStorageExternalPatternInputSinkStrategyImpl.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStorageExternalPatternInputSinkStrategyImpl.java new file mode 100644 index 000000000..d04fedb94 --- /dev/null +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStorageExternalPatternInputSinkStrategyImpl.java @@ -0,0 +1,53 @@ +package com.refinedmods.refinedstorage.fabric.autocrafting; + +import com.refinedmods.refinedstorage.api.core.NullableType; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.resource.ResourceKey; +import com.refinedmods.refinedstorage.fabric.api.FabricStorageExternalPatternInputSinkStrategy; + +import java.util.Collection; +import java.util.function.Function; + +import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache; +import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup; +import net.fabricmc.fabric.api.transfer.v1.storage.Storage; +import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; + +class FabricStorageExternalPatternInputSinkStrategyImpl implements FabricStorageExternalPatternInputSinkStrategy { + private final BlockApiCache, Direction> cache; + private final Function toPlatformMapper; + private final Direction direction; + + FabricStorageExternalPatternInputSinkStrategyImpl( + final BlockApiLookup, Direction> lookup, + final Function toPlatformMapper, + final ServerLevel serverLevel, + final BlockPos pos, + final Direction direction + ) { + this.cache = BlockApiCache.create(lookup, serverLevel, pos); + this.toPlatformMapper = toPlatformMapper; + this.direction = direction; + } + + @Override + public boolean accept(final Transaction tx, final Collection resources) { + for (final ResourceAmount resourceAmount : resources) { + final T platformResource = toPlatformMapper.apply(resourceAmount.resource()); + if (platformResource == null) { + continue; + } + final Storage storage = cache.find(direction); + if (storage == null) { + return false; + } + if (storage.insert(platformResource, resourceAmount.amount(), tx) != resourceAmount.amount()) { + return false; + } + } + return true; + } +} diff --git a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStoragePatternProviderExternalPatternInputSink.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStoragePatternProviderExternalPatternInputSink.java new file mode 100644 index 000000000..0a8f5bbbe --- /dev/null +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStoragePatternProviderExternalPatternInputSink.java @@ -0,0 +1,36 @@ +package com.refinedmods.refinedstorage.fabric.autocrafting; + +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.fabric.api.FabricStorageExternalPatternInputSinkStrategy; + +import java.util.Collection; +import java.util.Set; + +import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; + +class FabricStoragePatternProviderExternalPatternInputSink implements PatternProviderExternalPatternInputSink { + private final Set strategies; + + FabricStoragePatternProviderExternalPatternInputSink( + final Set strategies + ) { + this.strategies = strategies; + } + + @Override + public boolean accept(final Collection resources, final Action action) { + try (Transaction tx = Transaction.openOuter()) { + for (final FabricStorageExternalPatternInputSinkStrategy strategy : strategies) { + if (!strategy.accept(tx, resources)) { + return false; + } + } + if (action == Action.EXECUTE) { + tx.commit(); + } + } + return true; + } +} diff --git a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStoragePatternProviderExternalPatternInputSinkFactory.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStoragePatternProviderExternalPatternInputSinkFactory.java new file mode 100644 index 000000000..21b59c8ee --- /dev/null +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStoragePatternProviderExternalPatternInputSinkFactory.java @@ -0,0 +1,33 @@ +package com.refinedmods.refinedstorage.fabric.autocrafting; + +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; +import com.refinedmods.refinedstorage.fabric.api.FabricStorageExternalPatternInputSinkStrategyFactory; + +import java.util.Set; +import java.util.stream.Collectors; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; + +public class FabricStoragePatternProviderExternalPatternInputSinkFactory implements + PatternProviderExternalPatternInputSinkFactory { + private final Set factories; + + public FabricStoragePatternProviderExternalPatternInputSinkFactory( + final Set factories + ) { + this.factories = factories; + } + + @Override + public PatternProviderExternalPatternInputSink create(final ServerLevel level, + final BlockPos pos, + final Direction direction) { + return new FabricStoragePatternProviderExternalPatternInputSink(factories + .stream() + .map(factory -> factory.create(level, pos, direction)) + .collect(Collectors.toSet())); + } +} diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/AutocraftingNetworkComponent.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/AutocraftingNetworkComponent.java index 8cfc6a8b2..dd52b4c1f 100644 --- a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/AutocraftingNetworkComponent.java +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/AutocraftingNetworkComponent.java @@ -3,6 +3,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewProvider; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusProvider; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSink; import com.refinedmods.refinedstorage.api.network.NetworkComponent; import com.refinedmods.refinedstorage.api.resource.ResourceKey; @@ -12,7 +13,8 @@ import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.8") -public interface AutocraftingNetworkComponent extends NetworkComponent, PreviewProvider, TaskStatusProvider { +public interface AutocraftingNetworkComponent + extends NetworkComponent, PreviewProvider, TaskStatusProvider, ExternalPatternInputSink { void addListener(PatternListener listener); void removeListener(PatternListener listener); diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/ParentContainer.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/ParentContainer.java index 808c953b0..e2c48244f 100644 --- a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/ParentContainer.java +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/ParentContainer.java @@ -6,9 +6,9 @@ @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.8") public interface ParentContainer { - void add(Pattern pattern, int priority); + void add(PatternProvider provider, Pattern pattern, int priority); - void remove(Pattern pattern); + void remove(PatternProvider provider, Pattern pattern); void update(Pattern pattern, int priority); } diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java index 0cfaa1052..9d871f9fb 100644 --- a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java @@ -1,9 +1,11 @@ package com.refinedmods.refinedstorage.api.network.autocrafting; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; + import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.8") -public interface PatternProvider { +public interface PatternProvider extends PatternProviderExternalPatternInputSink { void onAddedIntoContainer(ParentContainer parentContainer); void onRemovedFromContainer(ParentContainer parentContainer); @@ -11,4 +13,6 @@ public interface PatternProvider { default boolean contains(AutocraftingNetworkComponent component) { return false; } + + void addTask(Task task); } diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java new file mode 100644 index 000000000..962171f04 --- /dev/null +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java @@ -0,0 +1,14 @@ +package com.refinedmods.refinedstorage.api.network.autocrafting; + +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; + +import java.util.Collection; + +import org.apiguardian.api.API; + +@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") +@FunctionalInterface +public interface PatternProviderExternalPatternInputSink { + boolean accept(Collection resources, Action action); +} diff --git a/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/FakeTaskStatusProvider.java b/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/FakeTaskStatusProvider.java deleted file mode 100644 index c3b0e8b02..000000000 --- a/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/FakeTaskStatusProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.refinedmods.refinedstorage.network.test.fixtures; - -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusProvider; - -import java.util.List; - -public class FakeTaskStatusProvider implements TaskStatusProvider { - @Override - public List getStatuses() { - return List.of(); - } - - @Override - public void addListener(final TaskStatusListener listener) { - // no op - } - - @Override - public void removeListener(final TaskStatusListener listener) { - // no op - } - - @Override - public void cancel(final TaskId taskId) { - // no op - } - - @Override - public void cancelAll() { - // no op - } -} diff --git a/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/NetworkTestFixtures.java b/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/NetworkTestFixtures.java index ad000b12a..c1827a0d8 100644 --- a/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/NetworkTestFixtures.java +++ b/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/NetworkTestFixtures.java @@ -42,10 +42,7 @@ public final class NetworkTestFixtures { NETWORK_COMPONENT_MAP_FACTORY.addFactory( AutocraftingNetworkComponent.class, network -> new AutocraftingNetworkComponentImpl( - () -> { - throw new UnsupportedOperationException("Storage not accessible from here (yet)"); - }, - new FakeTaskStatusProvider(), + () -> network.getComponent(StorageNetworkComponent.class), Executors.newSingleThreadExecutor() ) ); diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java index 1c9b093b8..b4d9ab389 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java @@ -2,25 +2,33 @@ import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.PatternRepositoryImpl; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculator; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculatorImpl; import com.refinedmods.refinedstorage.api.autocrafting.preview.Preview; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewCraftingCalculatorListener; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusProvider; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskImpl; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskPlan; +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.core.CoreValidations; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternListener; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider; import com.refinedmods.refinedstorage.api.network.node.container.NetworkNodeContainer; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.storage.Actor; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -30,29 +38,28 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AutocraftingNetworkComponentImpl implements AutocraftingNetworkComponent { +import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskPlanCraftingCalculatorListener.calculatePlan; + +public class AutocraftingNetworkComponentImpl implements AutocraftingNetworkComponent, ParentContainer { private static final Logger LOGGER = LoggerFactory.getLogger(AutocraftingNetworkComponentImpl.class); private final Supplier rootStorageProvider; - private final TaskStatusProvider taskStatusProvider; private final ExecutorService executorService; private final Set providers = new HashSet<>(); + private final Map providerByPattern = new HashMap<>(); private final Set listeners = new HashSet<>(); private final PatternRepositoryImpl patternRepository = new PatternRepositoryImpl(); - private final ParentContainer parentContainer = new ParentContainerImpl(); public AutocraftingNetworkComponentImpl(final Supplier rootStorageProvider, - final TaskStatusProvider taskStatusProvider, final ExecutorService executorService) { this.rootStorageProvider = rootStorageProvider; - this.taskStatusProvider = taskStatusProvider; this.executorService = executorService; } @Override public void onContainerAdded(final NetworkNodeContainer container) { if (container.getNode() instanceof PatternProvider provider) { - provider.onAddedIntoContainer(parentContainer); + provider.onAddedIntoContainer(this); providers.add(provider); } } @@ -60,7 +67,7 @@ public void onContainerAdded(final NetworkNodeContainer container) { @Override public void onContainerRemoved(final NetworkNodeContainer container) { if (container.getNode() instanceof PatternProvider provider) { - provider.onRemovedFromContainer(parentContainer); + provider.onRemovedFromContainer(this); providers.remove(provider); } } @@ -95,8 +102,30 @@ public CompletableFuture getMaxAmount(final ResourceKey resource) { } @Override - public boolean startTask(final ResourceKey resource, final long amount, final Actor actor, final boolean notify) { - LOGGER.info("{} started a task for {}x {} with notify={}", actor, amount, resource, notify); + public CompletableFuture startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final boolean notify) { + return CompletableFuture.supplyAsync(() -> { + final RootStorage rootStorage = rootStorageProvider.get(); + final CraftingCalculator calculator = new CraftingCalculatorImpl(patternRepository, rootStorage); + return calculatePlan(calculator, resource, amount) + .map(plan -> startTask(resource, amount, actor, plan)) + .orElse(false); + }); + } + + private boolean startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final TaskPlan plan) { + final Task task = TaskImpl.fromPlan(plan); + LOGGER.info("Created task {} for {}x {} for {}", task.getId(), amount, resource, actor); + final PatternProvider patternProvider = CoreValidations.validateNotNull( + providerByPattern.get(plan.pattern()), + "No provider for pattern " + plan.pattern() + ); + patternProvider.addTask(task); return true; } @@ -107,7 +136,7 @@ public void addListener(final PatternListener listener) { @Override public void addListener(final TaskStatusListener listener) { - taskStatusProvider.addListener(listener); + // TODO(feat): autocrafting monitor } @Override @@ -117,7 +146,7 @@ public void removeListener(final PatternListener listener) { @Override public void removeListener(final TaskStatusListener listener) { - taskStatusProvider.removeListener(listener); + // TODO(feat): autocrafting monitor } @Override @@ -132,35 +161,46 @@ public List getPatternsByOutput(final ResourceKey output) { @Override public List getStatuses() { - return taskStatusProvider.getStatuses(); + // TODO(feat): autocrafting monitor + return List.of(); } @Override public void cancel(final TaskId taskId) { - taskStatusProvider.cancel(taskId); + // TODO(feat): autocrafting monitor } @Override public void cancelAll() { - taskStatusProvider.cancelAll(); + // TODO(feat): autocrafting monitor } - private class ParentContainerImpl implements ParentContainer { - @Override - public void add(final Pattern pattern, final int priority) { - patternRepository.add(pattern, priority); - listeners.forEach(listener -> listener.onAdded(pattern)); - } + @Override + public void add(final PatternProvider provider, final Pattern pattern, final int priority) { + patternRepository.add(pattern, priority); + providerByPattern.put(pattern, provider); + listeners.forEach(listener -> listener.onAdded(pattern)); + } - @Override - public void remove(final Pattern pattern) { - listeners.forEach(listener -> listener.onRemoved(pattern)); - patternRepository.remove(pattern); - } + @Override + public void remove(final PatternProvider provider, final Pattern pattern) { + listeners.forEach(listener -> listener.onRemoved(pattern)); + providerByPattern.remove(pattern); + patternRepository.remove(pattern); + } - @Override - public void update(final Pattern pattern, final int priority) { - patternRepository.update(pattern, priority); + @Override + public void update(final Pattern pattern, final int priority) { + patternRepository.update(pattern, priority); + } + + // TODO(feat): processing pattern balancing + @Override + public boolean accept(final Pattern pattern, final Collection resources, final Action action) { + final PatternProvider patternProvider = providerByPattern.get(pattern); + if (patternProvider == null) { + return false; } + return patternProvider.accept(resources, action); } } diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java index 597854cea..26a0f801f 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java @@ -1,32 +1,73 @@ package com.refinedmods.refinedstorage.api.network.impl.node.patternprovider; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSink; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskState; +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.network.Network; +import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider; +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; import com.refinedmods.refinedstorage.api.network.impl.node.SimpleNetworkNode; +import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.Nullable; +// TODO(feat): return root results as soon as finished +// TODO(feat): persistence of tasks +// TODO(feat): autocrafter locking support +// TODO(feat): autocrafting monitor support +// TODO(feat): throttling and step behavior control +// TODO: processing sink on NeoForge public class PatternProviderNetworkNode extends SimpleNetworkNode implements PatternProvider { private final Pattern[] patterns; private final Set parents = new HashSet<>(); + private final List tasks = new CopyOnWriteArrayList<>(); private int priority; + @Nullable + private PatternProviderExternalPatternInputSink externalPatternInputSink; public PatternProviderNetworkNode(final long energyUsage, final int patterns) { super(energyUsage); this.patterns = new Pattern[patterns]; } + public void setExternalPatternInputSink(final PatternProviderExternalPatternInputSink externalPatternInputSink) { + this.externalPatternInputSink = externalPatternInputSink; + } + public void setPattern(final int index, @Nullable final Pattern pattern) { final Pattern oldPattern = patterns[index]; if (oldPattern != null) { - parents.forEach(parent -> parent.remove(oldPattern)); + parents.forEach(parent -> parent.remove(this, oldPattern)); } patterns[index] = pattern; if (pattern != null) { - parents.forEach(parent -> parent.add(pattern, priority)); + parents.forEach(parent -> parent.add(this, pattern, priority)); + } + } + + @Override + public void setNetwork(@Nullable final Network network) { + if (this.network != null) { + for (final Task task : tasks) { + this.network.getComponent(StorageNetworkComponent.class).removeListener(task); + } + } + super.setNetwork(network); + if (network != null) { + final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (final Task task : tasks) { + storage.addListener(task); + } } } @@ -36,14 +77,14 @@ protected void onActiveChanged(final boolean newActive) { if (!newActive) { for (final Pattern pattern : patterns) { if (pattern != null) { - parents.forEach(parent -> parent.remove(pattern)); + parents.forEach(parent -> parent.remove(this, pattern)); } } return; } for (final Pattern pattern : patterns) { if (pattern != null) { - parents.forEach(parent -> parent.add(pattern, priority)); + parents.forEach(parent -> parent.add(this, pattern, priority)); } } } @@ -53,7 +94,7 @@ public void onAddedIntoContainer(final ParentContainer parentContainer) { parents.add(parentContainer); for (final Pattern pattern : patterns) { if (pattern != null) { - parentContainer.add(pattern, priority); + parentContainer.add(this, pattern, priority); } } } @@ -63,11 +104,46 @@ public void onRemovedFromContainer(final ParentContainer parentContainer) { parents.remove(parentContainer); for (final Pattern pattern : patterns) { if (pattern != null) { - parentContainer.remove(pattern); + parentContainer.remove(this, pattern); } } } + @Override + public void addTask(final Task task) { + tasks.add(task); + if (network != null) { + network.getComponent(StorageNetworkComponent.class).addListener(task); + } + } + + @Override + public boolean accept(final Collection resources, final Action action) { + if (externalPatternInputSink == null) { + return false; + } + return externalPatternInputSink.accept(resources, action); + } + + public List getTasks() { + return tasks; + } + + @Override + public void doWork() { + super.doWork(); + if (network == null || !isActive()) { + return; + } + final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + final ExternalPatternInputSink outerExternalPatternInputSink = + network.getComponent(AutocraftingNetworkComponent.class); + tasks.removeIf(task -> { + task.step(storage, outerExternalPatternInputSink); + return task.getState() == TaskState.COMPLETED; + }); + } + public int getPriority() { return priority; } diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java index 9424bb9c5..53f8d1548 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java @@ -1,5 +1,7 @@ package com.refinedmods.refinedstorage.api.network.impl.node.relay; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.core.Action; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider; @@ -13,11 +15,13 @@ import com.refinedmods.refinedstorage.api.network.security.SecurityNetworkComponent; import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; import com.refinedmods.refinedstorage.api.network.storage.StorageProvider; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.resource.filter.FilterMode; import com.refinedmods.refinedstorage.api.storage.AccessMode; import com.refinedmods.refinedstorage.api.storage.Storage; +import java.util.Collection; import java.util.Set; import java.util.function.UnaryOperator; import javax.annotation.Nullable; @@ -129,6 +133,11 @@ public boolean contains(final AutocraftingNetworkComponent component) { return patternProvider.contains(component); } + @Override + public void addTask(final Task task) { + // TODO(feat): relay support + } + @Override public SecurityDecision isAllowed(final Permission permission, final SecurityActor actor) { if (securityDelegate == null || securityDelegate.contains(securityDelegate)) { @@ -156,4 +165,9 @@ public void onAddedIntoContainer(final ParentContainer parentContainer) { public void onRemovedFromContainer(final ParentContainer parentContainer) { patternProvider.onRemovedFromContainer(parentContainer); } + + @Override + public boolean accept(final Collection resources, final Action action) { + return false; // TODO(feat): relay support + } } diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java index 9736b785c..803c19e13 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java @@ -1,6 +1,8 @@ package com.refinedmods.refinedstorage.api.network.impl.node.relay; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.core.Action; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternListener; @@ -10,6 +12,7 @@ import com.refinedmods.refinedstorage.api.resource.filter.Filter; import com.refinedmods.refinedstorage.api.resource.filter.FilterMode; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -44,12 +47,12 @@ private void reset(final Runnable action) { void setDelegate(@Nullable final AutocraftingNetworkComponent delegate) { if (this.delegate != null) { - parents.forEach(parent -> getPatterns().forEach(parent::remove)); + parents.forEach(parent -> getPatterns().forEach(pattern -> parent.remove(this, pattern))); this.delegate.removeListener(this); } this.delegate = delegate; if (delegate != null) { - parents.forEach(parent -> getPatterns().forEach(pattern -> parent.add(pattern, 0))); + parents.forEach(parent -> getPatterns().forEach(pattern -> parent.add(this, pattern, 0))); delegate.addListener(this); } } @@ -74,7 +77,7 @@ public void onAdded(final Pattern pattern) { if (delegate == null || !isPatternAllowed(pattern) || delegate.contains(delegate)) { return; } - parents.forEach(parent -> parent.add(pattern, 0)); + parents.forEach(parent -> parent.add(this, pattern, 0)); } @Override @@ -82,7 +85,7 @@ public void onRemoved(final Pattern pattern) { if (delegate == null || !isPatternAllowed(pattern) || delegate.contains(delegate)) { return; } - parents.forEach(parent -> parent.remove(pattern)); + parents.forEach(parent -> parent.remove(this, pattern)); } @Override @@ -90,10 +93,15 @@ public boolean contains(final AutocraftingNetworkComponent component) { return component == delegate || (delegate != null && delegate.contains(component)); } + @Override + public void addTask(final Task task) { + // TODO(feat): relay support + } + @Override public void onAddedIntoContainer(final ParentContainer parentContainer) { if (delegate != null) { - delegate.getPatterns().forEach(pattern -> parentContainer.add(pattern, 0)); + delegate.getPatterns().forEach(pattern -> parentContainer.add(this, pattern, 0)); } parents.add(parentContainer); } @@ -101,8 +109,13 @@ public void onAddedIntoContainer(final ParentContainer parentContainer) { @Override public void onRemovedFromContainer(final ParentContainer parentContainer) { if (delegate != null) { - delegate.getPatterns().forEach(parentContainer::remove); + delegate.getPatterns().forEach(pattern -> parentContainer.remove(this, pattern)); } parents.remove(parentContainer); } + + @Override + public boolean accept(final Collection resources, final Action action) { + return false; // TODO(feat): relay support + } } diff --git a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java index acd74effb..b27938ea3 100644 --- a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java +++ b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java @@ -1,26 +1,19 @@ package com.refinedmods.refinedstorage.api.network.impl.autocrafting; -import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.preview.Preview; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewItem; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.core.Action; -import com.refinedmods.refinedstorage.api.network.autocrafting.PatternListener; import com.refinedmods.refinedstorage.api.network.impl.node.patternprovider.PatternProviderNetworkNode; import com.refinedmods.refinedstorage.api.network.node.container.NetworkNodeContainer; import com.refinedmods.refinedstorage.api.storage.Actor; import com.refinedmods.refinedstorage.api.storage.StorageImpl; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; import com.refinedmods.refinedstorage.api.storage.root.RootStorageImpl; -import com.refinedmods.refinedstorage.network.test.fixtures.FakeTaskStatusProvider; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; @@ -40,49 +33,7 @@ class AutocraftingNetworkComponentImplTest { @BeforeEach void setUp() { rootStorage = new RootStorageImpl(); - sut = new AutocraftingNetworkComponentImpl( - () -> rootStorage, - new FakeTaskStatusProvider(), - Executors.newSingleThreadExecutor() - ); - } - - @Test - void temporaryCoverage() { - final PatternListener listener = new PatternListener() { - @Override - public void onAdded(final Pattern pattern) { - // no op - } - - @Override - public void onRemoved(final Pattern pattern) { - // no op - } - }; - sut.addListener(listener); - sut.removeListener(listener); - final TaskStatusListener listener2 = new TaskStatusListener() { - @Override - public void taskStatusChanged(final TaskStatus status) { - // no op - } - - @Override - public void taskRemoved(final TaskId id) { - // no op - } - - @Override - public void taskAdded(final TaskStatus status) { - // no op - } - }; - sut.addListener(listener2); - sut.removeListener(listener2); - sut.getStatuses(); - sut.cancel(new TaskId(UUID.randomUUID())); - sut.cancelAll(); + sut = new AutocraftingNetworkComponentImpl(() -> rootStorage, Executors.newSingleThreadExecutor()); } @Test @@ -115,11 +66,6 @@ void shouldRemovePatternsFromPatternProvider() { assertThat(sut.getOutputs()).usingRecursiveFieldByFieldElementComparator().isEmpty(); } - @Test - void shouldStartTask() { - sut.startTask(A, 10, Actor.EMPTY, true); - } - @Test void shouldGetPreview() throws ExecutionException, InterruptedException { // Arrange @@ -158,4 +104,39 @@ void shouldGetMaxAmount() throws ExecutionException, InterruptedException { // Assert assertThat(maxAmount).isEqualTo(16); } + + @Test + void shouldStartTask() throws ExecutionException, InterruptedException { + // Arrange + rootStorage.addSource(new StorageImpl()); + rootStorage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + final PatternProviderNetworkNode provider = new PatternProviderNetworkNode(0, 5); + provider.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + final NetworkNodeContainer container = () -> provider; + sut.onContainerAdded(container); + + // Act + final boolean success = sut.startTask(B, 1, Actor.EMPTY, false).get(); + + // Assert + assertThat(success).isTrue(); + assertThat(provider.getTasks()).hasSize(1); + } + + @Test + void shouldNotStartTaskWhenThereAreMissingIngredients() throws ExecutionException, InterruptedException { + // Arrange + final PatternProviderNetworkNode provider = new PatternProviderNetworkNode(0, 5); + provider.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + final NetworkNodeContainer container = () -> provider; + sut.onContainerAdded(container); + + // Act + final boolean success = sut.startTask(B, 2, Actor.EMPTY, false).get(); + + // Assert + assertThat(success).isFalse(); + assertThat(provider.getTasks()).isEmpty(); + } } diff --git a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNodeTest.java b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNodeTest.java index 6ba04dee7..99a3036e2 100644 --- a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNodeTest.java +++ b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNodeTest.java @@ -1,13 +1,25 @@ package com.refinedmods.refinedstorage.api.network.impl.node.patternprovider; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.PatternType; +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.network.Network; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; +import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.storage.Actor; +import com.refinedmods.refinedstorage.api.storage.Storage; +import com.refinedmods.refinedstorage.api.storage.StorageImpl; import com.refinedmods.refinedstorage.network.test.AddNetworkNode; +import com.refinedmods.refinedstorage.network.test.InjectNetwork; import com.refinedmods.refinedstorage.network.test.InjectNetworkAutocraftingComponent; +import com.refinedmods.refinedstorage.network.test.InjectNetworkStorageComponent; import com.refinedmods.refinedstorage.network.test.NetworkTest; import com.refinedmods.refinedstorage.network.test.SetupNetwork; import com.refinedmods.refinedstorage.network.test.nodefactory.PatternProviderNetworkNodeFactory; +import java.util.concurrent.ExecutionException; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -104,6 +116,374 @@ void shouldAddPatternsFromNetworkWhenActive( assertThat(autocrafting.getOutputs()).containsExactly(A); } + @Test + void shouldNotStepTasksWithoutNetwork( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act + sut.setNetwork(null); + sut.doWork(); + + // Assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + } + + @Test + void shouldNotStepTasksWhenInactive( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act + sut.setActive(false); + sut.doWork(); + + // Assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + } + + @Test + void shouldStepTasks( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act & assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 1) + ); + assertThat(sut.getTasks()).isEmpty(); + } + + @Test + void shouldUseProviderAsSinkForExternalPatternInputsWhenSinkIsAttached( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + final Storage sinkContents = new StorageImpl(); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 1).build()); + sut.setExternalPatternInputSink((resources, action) -> { + if (action == Action.EXECUTE) { + resources.forEach(resource -> + sinkContents.insert(resource.resource(), resource.amount(), Action.EXECUTE, Actor.EMPTY)); + } + return true; + }); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act & assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sinkContents.getAll()).isEmpty(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sinkContents.getAll()).isEmpty(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sinkContents.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + } + + @Test + void shouldUseProviderAsSinkForExternalPatternInputsWhenSinkIsAttachedAndDoesNotAcceptResources( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 1).build()); + sut.setExternalPatternInputSink((resources, action) -> false); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act & assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + } + + @Test + void shouldNotUseProviderAsSinkForExternalPatternInputsWhenThereIsNoSinkAttached( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 1).build()); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act & assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + } + + @Test + void shouldInterceptNetworkInsertionsWhenWaitingForExternalPatternOutputs( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 5).build()); + // swallow resources + sut.setExternalPatternInputSink((resources, action) -> true); + + // Act & assert + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + storage.insert(B, 3, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 3)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + storage.insert(B, 4, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 5)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 2) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 5)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 2) + ); + + sut.doWork(); + assertThat(sut.getTasks()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 7) + ); + } + + @Nested + @SetupNetwork(id = "other") + class NetworkChangeTest { + @Test + void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting, + @InjectNetwork("other") final Network otherNetwork, + @InjectNetworkStorageComponent(networkId = "other") final StorageNetworkComponent otherStorage + ) { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + otherStorage.addSource(new StorageImpl()); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 5).build()); + // swallow resources + sut.setExternalPatternInputSink((resources, action) -> true); + + // Act & assert + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).join()).isTrue(); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + sut.setNetwork(otherNetwork); + storage.insert(B, 3, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 3) + ); + assertThat(otherStorage.getAll()).isEmpty(); + + otherStorage.insert(B, 4, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 4)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 3) + ); + assertThat(otherStorage.getAll()).isEmpty(); + + otherStorage.insert(B, 2, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 5)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 3) + ); + assertThat(otherStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(B, 1) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + + sut.doWork(); + assertThat(sut.getTasks()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 3) + ); + assertThat(otherStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(B, 6) + ); + } + } + @Nested class PriorityTest { @AddNetworkNode(properties = { @@ -111,6 +491,53 @@ class PriorityTest { }) private PatternProviderNetworkNode other; + @Test + void shouldNotUseProviderAsSinkForExternalChildPatternWhenProviderIsRemovedAndRootProviderIsStillPresent( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(C, 10, Action.EXECUTE, Actor.EMPTY); + + final Pattern patternForA = pattern().output(A, 1).ingredient(B, 1).build(); + sut.setPattern(0, patternForA); + + final Pattern patternForB = pattern(PatternType.EXTERNAL) + .output(B, 1) + .ingredient(C, 1) + .build(); + other.setPattern(0, patternForB); + + // Act & assert + assertThat(autocrafting.startTask(A, 1, Actor.EMPTY, false).join()).isTrue(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 10) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(C, 1)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 9) + ); + + autocrafting.onContainerRemoved(() -> other); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(C, 1)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 9) + ); + } + @Test void shouldSetPatternsRespectingPriority( @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting