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..f316639d3 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,7 +26,11 @@ public static PatternBuilder pattern() { } public static PatternBuilder pattern(final PatternType type) { - return new PatternBuilder(type); + return pattern(UUID.randomUUID(), type); + } + + public static PatternBuilder pattern(final UUID id, final PatternType type) { + return new PatternBuilder(id, type); } public IngredientBuilder ingredient(final long amount) { @@ -46,7 +53,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/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..44083475d 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,19 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; +import java.util.Collection; + import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") public interface Task { + 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..56dc6ed0d 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 @@ -29,7 +29,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 +39,10 @@ public TaskImpl(final TaskPlan plan) { plan.initialRequirements().forEach(initialRequirements::add); } + public static TaskImpl 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 +51,11 @@ private static AbstractTaskPattern createTaskPattern(final Pattern pattern, }; } + @Override + public TaskId getId() { + return id; + } + @Override public TaskState getState() { return state; @@ -97,7 +106,8 @@ private void returnInternalStorageAndTryCompleteTask(final RootStorage rootStora } } - Collection copyInternalStorageState() { + @Override + public Collection copyInternalStorageState() { return internalStorage.copyState(); } 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/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/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..6b69fd3f5 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 @@ -18,7 +18,7 @@ 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.task.TaskPlanCraftingCalculatorListener.calculatePlan; import static org.assertj.core.api.Assertions.assertThat; final class TaskUtil { @@ -57,7 +57,7 @@ static TaskImpl getTask(final RootStorage storage, 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 TaskImpl task = calculatePlan(sut, resource, amount).map(TaskImpl::fromPlan).orElseThrow(); storage.addListener(task); return task; } 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/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/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-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/NetworkTestFixtures.java b/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/NetworkTestFixtures.java index ad000b12a..f98b93252 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 @@ -17,6 +17,7 @@ import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; public final class NetworkTestFixtures { public static final ComponentMapFactory NETWORK_COMPONENT_MAP_FACTORY = @@ -42,9 +43,7 @@ public final class NetworkTestFixtures { NETWORK_COMPONENT_MAP_FACTORY.addFactory( AutocraftingNetworkComponent.class, network -> new AutocraftingNetworkComponentImpl( - () -> { - throw new UnsupportedOperationException("Storage not accessible from here (yet)"); - }, + () -> network.getComponent(StorageNetworkComponent.class), new FakeTaskStatusProvider(), 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..b165119b5 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,7 +2,6 @@ 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; @@ -10,17 +9,26 @@ 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.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,16 +38,18 @@ 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, @@ -52,7 +62,7 @@ public AutocraftingNetworkComponentImpl(final Supplier rootStorageP @Override public void onContainerAdded(final NetworkNodeContainer container) { if (container.getNode() instanceof PatternProvider provider) { - provider.onAddedIntoContainer(parentContainer); + provider.onAddedIntoContainer(this); providers.add(provider); } } @@ -60,7 +70,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 +105,32 @@ 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, rootStorage)) + .orElse(false); + }); + } + + private boolean startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final TaskPlan plan, + final RootStorage rootStorage) { + final TaskImpl 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() + ); + rootStorage.addListener(task); // TODO: unlisten + patternProvider.addTask(task); return true; } @@ -145,22 +179,32 @@ public void cancelAll() { taskStatusProvider.cancelAll(); } - 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..a84736218 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,49 +1,79 @@ 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 stuff as soon as finished +// TODO(feat): persistence of tasks +// TODO(feat): crafter locking support +// TODO(feat): autocrafting monitor support 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) { + super.setNetwork(network); + // TODO: handle pattern removals + tasks.clear(); // TODO: should return internal storage before clearing + } + @Override protected void onActiveChanged(final boolean newActive) { super.onActiveChanged(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 +83,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 +93,43 @@ 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); + } + + @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..b4edcc6b5 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,14 +1,9 @@ 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; @@ -20,7 +15,6 @@ 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; @@ -47,44 +41,6 @@ void setUp() { ); } - @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(); - } - @Test void shouldAddPatternsFromPatternProvider() { // Arrange @@ -115,11 +71,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 +109,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..fec5be0d5 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,23 @@ 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.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.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 +114,203 @@ void shouldAddPatternsFromNetworkWhenActive( assertThat(autocrafting.getOutputs()).containsExactly(A); } + @Test + void shouldClearTasksWhenNetworkChanges( + @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(); + assertThat(sut.getTasks()).hasSize(1); + + // Act + sut.setNetwork(null); + sut.doWork(); + + // Assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).isEmpty(); + } + + @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(); + assertThat(sut.getTasks()).hasSize(1); + + // Act + sut.setNetwork(null); + sut.doWork(); + + // Assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).isEmpty(); + } + + @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 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) + ); + } + @Nested class PriorityTest { @AddNetworkNode(properties = { @@ -111,6 +318,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