diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/Pattern.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/Pattern.java index 742d5a36f..f8e046d99 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/Pattern.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/Pattern.java @@ -4,15 +4,21 @@ import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import java.util.List; +import java.util.UUID; import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.6") -public record Pattern(List ingredients, List outputs, PatternType type) { - public Pattern(final List ingredients, final List outputs, final PatternType type) { +public record Pattern(UUID id, List ingredients, List outputs, PatternType type) { + public Pattern(final UUID id, + final List ingredients, + final List outputs, + final PatternType type) { + CoreValidations.validateNotNull(id, "ID cannot be null"); CoreValidations.validateNotEmpty(ingredients, "Ingredients cannot be empty"); CoreValidations.validateNotEmpty(outputs, "Outputs cannot be empty"); CoreValidations.validateNotNull(type, "Type cannot be null"); + this.id = id; this.ingredients = List.copyOf(ingredients); this.outputs = List.copyOf(outputs); this.type = type; diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/PatternBuilder.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/PatternBuilder.java index 0ad8938fd..5790112b3 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/PatternBuilder.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/PatternBuilder.java @@ -5,16 +5,19 @@ import java.util.ArrayList; import java.util.List; +import java.util.UUID; import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") public class PatternBuilder { private final PatternType type; + private final UUID id; private final List ingredients = new ArrayList<>(); private final List outputs = new ArrayList<>(); - private PatternBuilder(final PatternType type) { + private PatternBuilder(final UUID id, final PatternType type) { + this.id = id; this.type = type; } @@ -23,16 +26,15 @@ public static PatternBuilder pattern() { } public static PatternBuilder pattern(final PatternType type) { - return new PatternBuilder(type); + return pattern(UUID.randomUUID(), type); } - public IngredientBuilder ingredient(final long amount) { - return new IngredientBuilder(amount); + public static PatternBuilder pattern(final UUID id, final PatternType type) { + return new PatternBuilder(id, type); } - public PatternBuilder ingredient(final Ingredient ingredient) { - ingredients.add(ingredient); - return this; + public IngredientBuilder ingredient(final long amount) { + return new IngredientBuilder(amount); } public PatternBuilder ingredient(final ResourceKey input, final long amount) { @@ -46,7 +48,7 @@ public PatternBuilder output(final ResourceKey output, final long amount) { } public Pattern build() { - return new Pattern(ingredients, outputs, type); + return new Pattern(id, ingredients, outputs, type); } public class IngredientBuilder { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/Amount.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/Amount.java index 60386dd6a..abbfa3da1 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/Amount.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/Amount.java @@ -1,10 +1,16 @@ package com.refinedmods.refinedstorage.api.autocrafting.calculation; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.core.CoreValidations; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.resource.ResourceKey; public record Amount(long iterations, long amountPerIteration) { + public Amount { + CoreValidations.validateLargerThanZero(iterations, "Iterations"); + CoreValidations.validateLargerThanZero(amountPerIteration, "Amount per iteration"); + } + public long getTotal() { return iterations * amountPerIteration; } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorListener.java index 02d6891c1..3384e2ecc 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorListener.java @@ -7,13 +7,13 @@ @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") public interface CraftingCalculatorListener { - CraftingCalculatorListener childCalculationStarted(Pattern pattern, ResourceKey resource, Amount amount); + CraftingCalculatorListener childCalculationStarted(Pattern childPattern, ResourceKey resource, Amount amount); void childCalculationCompleted(CraftingCalculatorListener childListener); void ingredientsExhausted(ResourceKey resource, long amount); - void ingredientUsed(Pattern pattern, int ingredientIndex, ResourceKey resource, long amount); + void ingredientUsed(Pattern ingredientPattern, int ingredientIndex, ResourceKey resource, long amount); void ingredientExtractedFromStorage(ResourceKey resource, long amount); diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java index 578fb8ed8..5c22f0f7a 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java @@ -20,7 +20,7 @@ boolean isMissingResources() { } @Override - public CraftingCalculatorListener childCalculationStarted(final Pattern pattern, + public CraftingCalculatorListener childCalculationStarted(final Pattern childPattern, final ResourceKey resource, final Amount amount) { return new MissingResourcesCraftingCalculatorListener(missingResources); @@ -37,7 +37,7 @@ public void ingredientsExhausted(final ResourceKey resource, final long amount) } @Override - public void ingredientUsed(final Pattern pattern, + public void ingredientUsed(final Pattern ingredientPattern, final int ingredientIndex, final ResourceKey resource, final long amount) { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java index 26e29f10c..669f6f760 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java @@ -42,7 +42,7 @@ public static Preview calculatePreview(final CraftingCalculator calculator, } @Override - public CraftingCalculatorListener childCalculationStarted(final Pattern pattern, + public CraftingCalculatorListener childCalculationStarted(final Pattern childPattern, final ResourceKey resource, final Amount amount) { LOGGER.debug("{} - Child calculation starting for {}x {}", listenerId, amount, resource); @@ -64,7 +64,7 @@ public void ingredientsExhausted(final ResourceKey resource, final long amount) } @Override - public void ingredientUsed(final Pattern pattern, + public void ingredientUsed(final Pattern ingredientPattern, final int ingredientIndex, final ResourceKey resource, final long amount) { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java index b22c567d4..718d984d4 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java @@ -14,5 +14,5 @@ public interface PreviewProvider { CompletableFuture getMaxAmount(ResourceKey resource); - boolean startTask(ResourceKey resource, long amount, Actor actor, boolean notify); + CompletableFuture startTask(ResourceKey resource, long amount, Actor actor, boolean notify); } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/AbstractTaskPattern.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/AbstractTaskPattern.java index 3d607a2b6..290c40685 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/AbstractTaskPattern.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/AbstractTaskPattern.java @@ -43,7 +43,7 @@ protected final boolean extractAll(final ResourceList inputs, } if (action == Action.EXECUTE) { internalStorage.remove(inputResource, inputAmount); - LOGGER.info("Extracted {}x {} from internal storage", inputAmount, inputResource); + LOGGER.debug("Extracted {}x {} from internal storage", inputAmount, inputResource); } } return true; diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java index b7048d2ac..acf5a59a6 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java @@ -11,7 +11,5 @@ @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") @FunctionalInterface public interface ExternalPatternInputSink { - ExternalPatternInputSink EMPTY = (pattern, resources, action) -> false; - boolean accept(Pattern pattern, Collection resources, Action action); } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java index c940f03d2..0bc352984 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java @@ -30,14 +30,12 @@ boolean step(final MutableResourceList internalStorage, final ExternalPatternInp return true; } if (iterationsToSendToSink == 0) { - // TODO: coverage return false; } if (!acceptsIterationInputs(internalStorage, externalPatternInputSink)) { - // TODO: coverage return false; } - LOGGER.info("Stepped {} with {} iterations remaining", pattern, iterationsToSendToSink); + LOGGER.debug("Stepped {} with {} iterations remaining", pattern, iterationsToSendToSink); iterationsToSendToSink--; return false; } @@ -50,7 +48,6 @@ long interceptInsertion(final ResourceKey resource, final long amount) { expectedOutputs.remove(resource, available); return available; } - // TODO: coverage return 0; } @@ -58,18 +55,24 @@ private boolean acceptsIterationInputs(final MutableResourceList internalStorage final ExternalPatternInputSink externalPatternInputSink) { final ResourceList iterationInputsSimulated = calculateIterationInputs(Action.SIMULATE); if (!extractAll(iterationInputsSimulated, internalStorage, Action.SIMULATE)) { - // TODO: coverage return false; } if (!externalPatternInputSink.accept(pattern, iterationInputsSimulated.copyState(), Action.SIMULATE)) { - // TODO: coverage return false; } final ResourceList iterationInputs = calculateIterationInputs(Action.EXECUTE); extractAll(iterationInputs, internalStorage, Action.EXECUTE); + // If the sink does not accept the inputs + // we cannot return the extracted resources to the internal storage + // because we have already deducted from the iteration inputs + // and because the sink might have still accepted some resources halfway. + // If we returned the extracted resources to the internal storage and correct the + // iteration inputs, it would potentially duplicate the resources + // across the sink and the internal storage. + // The end result is that we lie, do as if the insertion was successful, + // and potentially void the extracted resources from the internal storage. if (!externalPatternInputSink.accept(pattern, iterationInputs.copyState(), Action.EXECUTE)) { - // TODO: coverage - return false; + LOGGER.warn("External sink {} did not accept all inputs for pattern {}", externalPatternInputSink, pattern); } return true; } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/InternalTaskPattern.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/InternalTaskPattern.java index edf47500a..6573b8a90 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/InternalTaskPattern.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/InternalTaskPattern.java @@ -25,11 +25,11 @@ boolean step(final MutableResourceList internalStorage, final ExternalPatternInp if (!extractAll(iterationInputsSimulated, internalStorage, Action.SIMULATE)) { return false; } - LOGGER.info("Stepping {}", pattern); + LOGGER.debug("Stepping {}", pattern); final ResourceList iterationInputs = calculateIterationInputs(Action.EXECUTE); extractAll(iterationInputs, internalStorage, Action.EXECUTE); pattern.outputs().forEach(output -> { - LOGGER.info("Inserting {}x {} into internal storage", output.amount(), output.resource()); + LOGGER.debug("Inserting {}x {} into internal storage", output.amount(), output.resource()); internalStorage.add(output); }); return useIteration(); @@ -42,7 +42,7 @@ long interceptInsertion(final ResourceKey resource, final long amount) { protected boolean useIteration() { iterationsRemaining--; - LOGGER.info("Stepped {} with {} iterations remaining", pattern, iterationsRemaining); + LOGGER.debug("Stepped {} with {} iterations remaining", pattern, iterationsRemaining); return iterationsRemaining == 0; } } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/MutableTaskPlan.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/MutableTaskPlan.java index 123c7925d..65e5b7506 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/MutableTaskPlan.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/MutableTaskPlan.java @@ -10,52 +10,62 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import javax.annotation.Nullable; import static java.util.Objects.requireNonNull; public class MutableTaskPlan { + @Nullable + private final Pattern pattern; private final Map patterns; private final MutableResourceList initialRequirements; private boolean missing; MutableTaskPlan() { - this(new LinkedHashMap<>(), MutableResourceListImpl.create(), false); + this(null, new LinkedHashMap<>(), MutableResourceListImpl.create(), false); } - private MutableTaskPlan(final Map patterns, + private MutableTaskPlan(@Nullable final Pattern pattern, + final Map patterns, final MutableResourceList initialRequirements, final boolean missing) { + this.pattern = pattern; this.patterns = patterns; this.initialRequirements = initialRequirements; this.missing = missing; } - void addOrUpdatePattern(final Pattern pattern, final long iterations) { - patterns.computeIfAbsent(pattern, MutablePatternPlan::new).addIterations(iterations); + void addOrUpdatePattern(final Pattern usedPattern, final long iterations) { + patterns.computeIfAbsent(usedPattern, MutablePatternPlan::new).addIterations(iterations); } void addToExtract(final ResourceKey resource, final long amount) { initialRequirements.add(resource, amount); } - void addUsedIngredient(final Pattern pattern, + void addUsedIngredient(final Pattern ingredientPattern, final int ingredientIndex, final ResourceKey resource, final long amount) { - final MutablePatternPlan patternPlan = requireNonNull(patterns.get(pattern)); + final MutablePatternPlan patternPlan = requireNonNull(patterns.get(ingredientPattern)); patternPlan.addUsedIngredient(ingredientIndex, resource, amount); } - MutableTaskPlan copy() { + MutableTaskPlan copy(final Pattern childPattern) { final Map patternsCopy = new LinkedHashMap<>(); for (final Map.Entry entry : patterns.entrySet()) { patternsCopy.put(entry.getKey(), entry.getValue().copy()); } - return new MutableTaskPlan(patternsCopy, initialRequirements.copy(), missing); + return new MutableTaskPlan( + pattern == null ? childPattern : pattern, + patternsCopy, + initialRequirements.copy(), + missing + ); } Optional getPlan() { - if (missing) { + if (missing || pattern == null) { return Optional.empty(); } final Map finalPatterns = Collections.unmodifiableMap(patterns.entrySet() @@ -66,7 +76,7 @@ Optional getPlan() { (a, b) -> a, LinkedHashMap::new ))); - return Optional.of(new TaskPlan(finalPatterns, initialRequirements.copyState())); + return Optional.of(new TaskPlan(pattern, finalPatterns, initialRequirements.copyState())); } void setMissing() { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/Task.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/Task.java index 0fcf94ae2..81addb9a6 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/Task.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/Task.java @@ -1,12 +1,20 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; +import com.refinedmods.refinedstorage.api.storage.root.RootStorageListener; + +import java.util.Collection; import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") -public interface Task { +public interface Task extends RootStorageListener { + TaskId getId(); + TaskState getState(); + + Collection copyInternalStorageState(); void step(RootStorage rootStorage, ExternalPatternInputSink externalPatternInputSink); } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskId.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskId.java index 338ec75ff..f843b6300 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskId.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskId.java @@ -9,4 +9,9 @@ public record TaskId(UUID id) { public static TaskId create() { return new TaskId(UUID.randomUUID()); } + + @Override + public String toString() { + return id.toString(); + } } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImpl.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImpl.java index c469d1748..b683f7e29 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImpl.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImpl.java @@ -8,7 +8,6 @@ import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl; import com.refinedmods.refinedstorage.api.storage.Actor; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; -import com.refinedmods.refinedstorage.api.storage.root.RootStorageListener; import java.util.Collection; import java.util.HashSet; @@ -20,7 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class TaskImpl implements Task, RootStorageListener { +public class TaskImpl implements Task { private static final Logger LOGGER = LoggerFactory.getLogger(TaskImpl.class); private final TaskId id = TaskId.create(); @@ -29,7 +28,7 @@ public class TaskImpl implements Task, RootStorageListener { private final MutableResourceList internalStorage = MutableResourceListImpl.create(); private TaskState state = TaskState.READY; - public TaskImpl(final TaskPlan plan) { + private TaskImpl(final TaskPlan plan) { this.patterns = plan.patterns().entrySet().stream().collect(Collectors.toMap( Map.Entry::getKey, e -> createTaskPattern(e.getKey(), e.getValue()), @@ -39,6 +38,10 @@ public TaskImpl(final TaskPlan plan) { plan.initialRequirements().forEach(initialRequirements::add); } + public static Task fromPlan(final TaskPlan plan) { + return new TaskImpl(plan); + } + private static AbstractTaskPattern createTaskPattern(final Pattern pattern, final TaskPlan.PatternPlan patternPlan) { return switch (pattern.type()) { @@ -47,13 +50,18 @@ private static AbstractTaskPattern createTaskPattern(final Pattern pattern, }; } + @Override + public TaskId getId() { + return id; + } + @Override public TaskState getState() { return state; } private void updateState(final TaskState newState) { - LOGGER.info("Task {} state changed from {} to {}", id.id(), state, newState); + LOGGER.debug("Task {} state changed from {} to {}", id.id(), state, newState); this.state = newState; } @@ -82,7 +90,7 @@ private void stepPatterns(final ExternalPatternInputSink externalPatternInputSin patterns.entrySet().removeIf(pattern -> { final boolean completed = pattern.getValue().step(internalStorage, externalPatternInputSink); if (completed) { - LOGGER.info("{} completed", pattern.getKey()); + LOGGER.debug("{} completed", pattern.getKey()); } return completed; }); @@ -97,7 +105,8 @@ private void returnInternalStorageAndTryCompleteTask(final RootStorage rootStora } } - Collection copyInternalStorageState() { + @Override + public Collection copyInternalStorageState() { return internalStorage.copyState(); } @@ -107,7 +116,7 @@ private boolean extractInitialResources(final RootStorage rootStorage) { for (final ResourceKey resource : resources) { final long needed = initialRequirements.get(resource); final long extracted = rootStorage.extract(resource, needed, Action.EXECUTE, Actor.EMPTY); - LOGGER.info("Extracted {}x {} from storage", extracted, resource); + LOGGER.debug("Extracted {}x {} from storage", extracted, resource); if (extracted != needed) { extractedAll = false; } @@ -125,7 +134,7 @@ private boolean returnInternalStorage(final RootStorage rootStorage) { for (final ResourceKey resource : resources) { final long amount = internalStorage.get(resource); final long inserted = rootStorage.insert(resource, amount, Action.EXECUTE, Actor.EMPTY); - LOGGER.info("Returned {}x {} into storage", inserted, resource); + LOGGER.debug("Returned {}x {} into storage", inserted, resource); if (inserted != amount) { returnedAll = false; } @@ -138,20 +147,18 @@ private boolean returnInternalStorage(final RootStorage rootStorage) { @Override public long beforeInsert(final ResourceKey resource, final long amount, final Actor actor) { - // TODO: coverage - long totalIntercepted = 0; + long remaining = amount; for (final AbstractTaskPattern pattern : patterns.values()) { - final long remaining = amount - totalIntercepted; final long intercepted = pattern.interceptInsertion(resource, remaining); if (intercepted > 0) { internalStorage.add(resource, intercepted); } - totalIntercepted += intercepted; - if (totalIntercepted == amount) { - break; + remaining -= intercepted; + if (remaining == 0) { + return amount; } } - return totalIntercepted; + return amount - remaining; } @Override diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlan.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlan.java index 8d344a752..04d8cd46e 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlan.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlan.java @@ -10,9 +10,11 @@ import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") -public record TaskPlan(Map patterns, Collection initialRequirements) { - public PatternPlan pattern(final Pattern pattern) { - return patterns.get(pattern); +public record TaskPlan(Pattern pattern, + Map patterns, + Collection initialRequirements) { + public PatternPlan getPattern(final Pattern p) { + return patterns.get(p); } public record PatternPlan(long iterations, Map> ingredients) { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskCraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanCraftingCalculatorListener.java similarity index 71% rename from refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskCraftingCalculatorListener.java rename to refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanCraftingCalculatorListener.java index 6a8c4743b..37b6f745d 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskCraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanCraftingCalculatorListener.java @@ -8,28 +8,32 @@ import java.util.Optional; -public class TaskCraftingCalculatorListener implements CraftingCalculatorListener { +public class TaskPlanCraftingCalculatorListener implements CraftingCalculatorListener { private MutableTaskPlan task; - private TaskCraftingCalculatorListener(final MutableTaskPlan task) { + private TaskPlanCraftingCalculatorListener(final MutableTaskPlan task) { this.task = task; } + private TaskPlanCraftingCalculatorListener() { + this(new MutableTaskPlan()); + } + public static Optional calculatePlan(final CraftingCalculator calculator, final ResourceKey resource, final long amount) { - final TaskCraftingCalculatorListener listener = new TaskCraftingCalculatorListener(new MutableTaskPlan()); + final TaskPlanCraftingCalculatorListener listener = new TaskPlanCraftingCalculatorListener(); calculator.calculate(resource, amount, listener); return listener.task.getPlan(); } @Override - public CraftingCalculatorListener childCalculationStarted(final Pattern pattern, + public CraftingCalculatorListener childCalculationStarted(final Pattern childPattern, final ResourceKey resource, final Amount amount) { - final MutableTaskPlan copy = task.copy(); - copy.addOrUpdatePattern(pattern, amount.iterations()); - return new TaskCraftingCalculatorListener(copy); + final MutableTaskPlan copy = task.copy(childPattern); + copy.addOrUpdatePattern(childPattern, amount.iterations()); + return new TaskPlanCraftingCalculatorListener(copy); } @Override @@ -43,11 +47,11 @@ public void ingredientsExhausted(final ResourceKey resource, final long amount) } @Override - public void ingredientUsed(final Pattern pattern, + public void ingredientUsed(final Pattern ingredientPattern, final int ingredientIndex, final ResourceKey resource, final long amount) { - task.addUsedIngredient(pattern, ingredientIndex, resource, amount); + task.addUsedIngredient(ingredientPattern, ingredientIndex, resource, amount); } @Override diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingHelpers.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingUtil.java similarity index 93% rename from refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingHelpers.java rename to refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingUtil.java index 1ba4ab07b..69b3b93a9 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingHelpers.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingUtil.java @@ -7,8 +7,8 @@ import com.refinedmods.refinedstorage.api.storage.root.RootStorage; import com.refinedmods.refinedstorage.api.storage.root.RootStorageImpl; -public final class AutocraftingHelpers { - private AutocraftingHelpers() { +public final class AutocraftingUtil { + private AutocraftingUtil() { } public static RootStorage storage(final ResourceAmount... resourceAmounts) { diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternFixtures.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternFixtures.java new file mode 100644 index 000000000..1443d7251 --- /dev/null +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternFixtures.java @@ -0,0 +1,55 @@ +package com.refinedmods.refinedstorage.api.autocrafting; + +import static com.refinedmods.refinedstorage.api.autocrafting.PatternBuilder.pattern; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.COBBLESTONE; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_INGOT; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_ORE; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_PICKAXE; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_PLANKS; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SMOOTH_STONE; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_LOG; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_PLANKS; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STICKS; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STONE; + +public final class PatternFixtures { + public static final Pattern OAK_PLANKS_PATTERN = pattern() + .ingredient(OAK_LOG, 1) + .output(OAK_PLANKS, 4) + .build(); + public static final Pattern SPRUCE_PLANKS_PATTERN = pattern() + .ingredient(SPRUCE_LOG, 1) + .output(SPRUCE_PLANKS, 4) + .build(); + public static final Pattern CRAFTING_TABLE_PATTERN = pattern() + .ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end() + .ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end() + .ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end() + .ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end() + .output(CRAFTING_TABLE, 1) + .build(); + public static final Pattern IRON_INGOT_PATTERN = pattern(PatternType.EXTERNAL) + .ingredient(IRON_ORE, 1) + .output(IRON_INGOT, 1) + .build(); + public static final Pattern IRON_PICKAXE_PATTERN = pattern() + .ingredient(IRON_INGOT, 1) + .ingredient(IRON_INGOT, 1) + .ingredient(IRON_INGOT, 1) + .ingredient(STICKS, 2) + .output(IRON_PICKAXE, 1) + .build(); + public static final Pattern STONE_PATTERN = pattern(PatternType.EXTERNAL) + .ingredient(COBBLESTONE, 1) + .output(STONE, 1) + .build(); + public static final Pattern SMOOTH_STONE_PATTERN = pattern(PatternType.EXTERNAL) + .ingredient(STONE, 1) + .output(SMOOTH_STONE, 1) + .build(); + + private PatternFixtures() { + } +} diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternTest.java index 921b20181..676c3b742 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternTest.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.UUID; import org.assertj.core.api.ThrowableAssert; import org.junit.jupiter.api.Test; @@ -21,6 +22,7 @@ class PatternTest { void testPattern() { // Act final Pattern sut = new Pattern( + UUID.randomUUID(), List.of( new Ingredient(1, List.of(A, B)), new Ingredient(2, List.of(C)) @@ -51,6 +53,7 @@ void testPattern() { void shouldNotCreatePatternWithoutIngredients() { // Act final ThrowableAssert.ThrowingCallable action = () -> new Pattern( + UUID.randomUUID(), List.of(), List.of( new ResourceAmount(OAK_LOG, 3), @@ -67,6 +70,7 @@ void shouldNotCreatePatternWithoutIngredients() { void shouldNotCreatePatternWithoutOutputs() { // Act final ThrowableAssert.ThrowingCallable action = () -> new Pattern( + UUID.randomUUID(), List.of( new Ingredient(1, List.of(A, B)), new Ingredient(2, List.of(C)) @@ -86,7 +90,7 @@ void shouldCopyIngredientsAndOutputs() { ingredients.add(new Ingredient(1, List.of(A, B))); final List outputs = new ArrayList<>(); outputs.add(new ResourceAmount(OAK_LOG, 3)); - final Pattern sut = new Pattern(ingredients, outputs, PatternType.INTERNAL); + final Pattern sut = new Pattern(UUID.randomUUID(), ingredients, outputs, PatternType.INTERNAL); // Act ingredients.add(new Ingredient(2, List.of(C))); @@ -101,6 +105,7 @@ void shouldCopyIngredientsAndOutputs() { void shouldNotBeAbleToModifyIngredientsAndOutputs() { // Arrange final Pattern sut = new Pattern( + UUID.randomUUID(), List.of(new Ingredient(1, List.of(A))), List.of(new ResourceAmount(OAK_LOG, 3)), PatternType.INTERNAL @@ -125,6 +130,7 @@ void shouldNotBeAbleToModifyIngredientsAndOutputs() { void shouldNotCreatePatternWithoutPatternType() { // Act final ThrowableAssert.ThrowingCallable action = () -> new Pattern( + UUID.randomUUID(), List.of( new Ingredient(1, List.of(A, B)), new Ingredient(2, List.of(C)) @@ -139,4 +145,25 @@ void shouldNotCreatePatternWithoutPatternType() { // Assert assertThatThrownBy(action).isInstanceOf(NullPointerException.class); } + + @Test + @SuppressWarnings("ConstantConditions") + void shouldNotCreateWithoutId() { + // Act + final ThrowableAssert.ThrowingCallable action = () -> new Pattern( + null, + List.of( + new Ingredient(1, List.of(A, B)), + new Ingredient(2, List.of(C)) + ), + List.of( + new ResourceAmount(OAK_LOG, 3), + new ResourceAmount(OAK_PLANKS, 4) + ), + PatternType.INTERNAL + ); + + // Assert + assertThatThrownBy(action).isInstanceOf(NullPointerException.class); + } } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/ResourceFixtures.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/ResourceFixtures.java index 083e5ac50..76cf883c9 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/ResourceFixtures.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/ResourceFixtures.java @@ -15,5 +15,9 @@ public enum ResourceFixtures implements ResourceKey { SIGN, IRON_ORE, IRON_INGOT, - IRON_PICKAXE + IRON_PICKAXE, + COBBLESTONE, + STONE, + STONE_BRICKS, + SMOOTH_STONE } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/AmountTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/AmountTest.java new file mode 100644 index 000000000..169d336dc --- /dev/null +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/AmountTest.java @@ -0,0 +1,53 @@ +package com.refinedmods.refinedstorage.api.autocrafting.calculation; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class AmountTest { + @ParameterizedTest + @ValueSource(longs = {-1L, 0L}) + void testInvalidIterations(final long iterations) { + // Act + final Executable action = () -> new Amount(iterations, 1); + + // Assert + assertThrows(IllegalArgumentException.class, action); + } + + @ParameterizedTest + @ValueSource(longs = {-1L, 0L}) + void testInvalidAmountPerIteration(final long amountPerIteration) { + // Act + final Executable action = () -> new Amount(1, amountPerIteration); + + // Assert + assertThrows(IllegalArgumentException.class, action); + } + + @Test + void testMinimumValid() { + // Act + final Amount amount = new Amount(1, 1); + + // Assert + assertThat(amount.iterations()).isEqualTo(1); + assertThat(amount.amountPerIteration()).isEqualTo(1); + } + + @Test + void testTotal() { + // Arrange + final Amount amount = new Amount(2, 3); + + // Act + final long total = amount.getTotal(); + + // Assert + assertThat(total).isEqualTo(6); + } +} diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImplTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImplTest.java index e0b84def0..91a1fac0b 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImplTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImplTest.java @@ -8,8 +8,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.patterns; -import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.storage; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingUtil.patterns; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingUtil.storage; import static com.refinedmods.refinedstorage.api.autocrafting.PatternBuilder.pattern; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java index 1f046f45d..64108d20e 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java @@ -17,8 +17,8 @@ import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.patterns; -import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.storage; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingUtil.patterns; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingUtil.storage; import static com.refinedmods.refinedstorage.api.autocrafting.PatternBuilder.pattern; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java index 00cb3f535..f34547ea9 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java @@ -21,10 +21,10 @@ static ExternalPatternInputSinkBuilder externalPatternInputSink() { return new ExternalPatternInputSinkBuilder(); } - Storage storageSink(final Pattern pattern) { - final Storage storage = new StorageImpl(); - sinks.put(pattern, new StorageSink(storage)); - return storage; + Sink storageSink(final Pattern pattern) { + final Sink sink = new Sink(new StorageImpl()); + sinks.put(pattern, sink); + return sink; } ExternalPatternInputSink build() { @@ -34,19 +34,26 @@ ExternalPatternInputSink build() { }; } - private interface Sink { - boolean accept(Collection resources, Action action); - } - - private static class StorageSink implements Sink { + static class Sink { private final Storage storage; + private boolean enabled = true; - private StorageSink(final Storage storage) { + private Sink(final Storage storage) { this.storage = storage; } - @Override - public boolean accept(final Collection resources, final Action action) { + Collection getAll() { + return storage.getAll(); + } + + void setEnabled(final boolean enabled) { + this.enabled = enabled; + } + + private boolean accept(final Collection resources, final Action action) { + if (!enabled) { + return false; + } if (action == Action.EXECUTE) { return accept(resources); } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java index 5aa85efbc..ee9f31b3a 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java @@ -1,10 +1,11 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.PatternRepository; +import com.refinedmods.refinedstorage.api.autocrafting.PatternType; import com.refinedmods.refinedstorage.api.core.Action; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.storage.Actor; -import com.refinedmods.refinedstorage.api.storage.Storage; import com.refinedmods.refinedstorage.api.storage.StorageImpl; import com.refinedmods.refinedstorage.api.storage.limited.LimitedStorageImpl; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; @@ -12,8 +13,18 @@ import org.junit.jupiter.api.Test; -import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.patterns; -import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.storage; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingUtil.patterns; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingUtil.storage; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternBuilder.pattern; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.CRAFTING_TABLE_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.IRON_INGOT_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.IRON_PICKAXE_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.OAK_PLANKS_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.SMOOTH_STONE_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.SPRUCE_PLANKS_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.STONE_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.A; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.COBBLESTONE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_INGOT; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_ORE; @@ -21,21 +32,21 @@ import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_PLANKS; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SIGN; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SMOOTH_STONE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_LOG; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_PLANKS; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STICKS; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STONE; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STONE_BRICKS; import static com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkBuilder.externalPatternInputSink; -import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.CRAFTING_TABLE_PATTERN; -import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.IRON_INGOT_PATTERN; -import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.IRON_PICKAXE_PATTERN; -import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.OAK_PLANKS_PATTERN; -import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.SPRUCE_PLANKS_PATTERN; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.getRunningTask; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.getTask; import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskUtil.getTaskReadyToReturnInternalStorage; import static org.assertj.core.api.Assertions.assertThat; class TaskImplTest { + private static final ExternalPatternInputSink EMPTY_SINK = (pattern, resources, action) -> false; + @Test void testInitialState() { // Arrange @@ -47,10 +58,11 @@ void testInitialState() { final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); // Act - final TaskImpl task = getTask(storage, patterns, CRAFTING_TABLE, 3); + final Task task = getTask(storage, patterns, CRAFTING_TABLE, 3); // Assert assertThat(task.getState()).isEqualTo(TaskState.READY); + assertThat(task.getId()).isNotNull(); } @Test @@ -63,10 +75,10 @@ void shouldExtractAllResources() { new ResourceAmount(SIGN, 10) ); final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); - final TaskImpl task = getTask(storage, patterns, CRAFTING_TABLE, 3); + final Task task = getTask(storage, patterns, CRAFTING_TABLE, 3); // Act - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); // Assert assertThat(task.getState()).isEqualTo(TaskState.RUNNING); @@ -92,25 +104,25 @@ void shouldPartiallyExtractAllResources() { new ResourceAmount(SIGN, 10) ); final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); - final TaskImpl task = getTask(storage, patterns, CRAFTING_TABLE, 3); + final Task task = getTask(storage, patterns, CRAFTING_TABLE, 3); storage.extract(OAK_PLANKS, 4, Action.EXECUTE, Actor.EMPTY); // Act & assert - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.EXTRACTING_INITIAL_RESOURCES); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(SIGN, 10) ); storage.insert(OAK_PLANKS, 2, Action.EXECUTE, Actor.EMPTY); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.EXTRACTING_INITIAL_RESOURCES); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(SIGN, 10) ); storage.insert(OAK_PLANKS, 3, Action.EXECUTE, Actor.EMPTY); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(SIGN, 10), @@ -128,10 +140,10 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(SIGN, 10) ); final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); - final TaskImpl task = getRunningTask(storage, patterns, ExternalPatternInputSink.EMPTY, CRAFTING_TABLE, 3); + final Task task = getRunningTask(storage, patterns, EMPTY_SINK, CRAFTING_TABLE, 3); // Act & assert - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -140,7 +152,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(OAK_PLANKS, 8) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -150,7 +162,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(OAK_PLANKS, 5) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -160,7 +172,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(OAK_PLANKS, 2) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -171,7 +183,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(SIGN, 10) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); assertThat(task.copyInternalStorageState()).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -179,7 +191,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(CRAFTING_TABLE, 3) ); - task.step(storage, ExternalPatternInputSink.EMPTY); + task.step(storage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); assertThat(task.copyInternalStorageState()).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -197,9 +209,9 @@ void shouldCompleteTaskWithExternalPattern() { ); final PatternRepository patterns = patterns(IRON_INGOT_PATTERN, IRON_PICKAXE_PATTERN); final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); - final Storage ironOreSink = sinkBuilder.storageSink(IRON_INGOT_PATTERN); + final ExternalPatternInputSinkBuilder.Sink ironOreSink = sinkBuilder.storageSink(IRON_INGOT_PATTERN); final ExternalPatternInputSink sink = sinkBuilder.build(); - final TaskImpl task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); + final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); assertThat(storage.getAll()).isEmpty(); assertThat(task.copyInternalStorageState()) @@ -277,9 +289,11 @@ void shouldCompleteTaskWithExternalPattern() { ); storage.insert(IRON_INGOT, 5, Action.EXECUTE, Actor.EMPTY); + storage.insert(STONE, 2, Action.EXECUTE, Actor.EMPTY); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( - new ResourceAmount(IRON_INGOT, 3) + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(IRON_INGOT, 3), + new ResourceAmount(STONE, 2) ); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -290,8 +304,9 @@ void shouldCompleteTaskWithExternalPattern() { task.step(storage, sink); assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); - assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( - new ResourceAmount(IRON_INGOT, 3) + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(IRON_INGOT, 3), + new ResourceAmount(STONE, 2) ); assertThat(task.copyInternalStorageState()) .usingRecursiveFieldByFieldElementComparator() @@ -303,9 +318,311 @@ void shouldCompleteTaskWithExternalPattern() { assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(IRON_INGOT, 3), - new ResourceAmount(IRON_PICKAXE, 1) + new ResourceAmount(IRON_PICKAXE, 1), + new ResourceAmount(STONE, 2) + ); + assertThat(task.copyInternalStorageState()).isEmpty(); + } + + @Test + void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(COBBLESTONE, 64) + ); + final PatternRepository patterns = patterns(STONE_PATTERN, SMOOTH_STONE_PATTERN); + final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); + final ExternalPatternInputSinkBuilder.Sink cobblestoneSink = sinkBuilder.storageSink(STONE_PATTERN); + final ExternalPatternInputSinkBuilder.Sink stoneSink = sinkBuilder.storageSink(SMOOTH_STONE_PATTERN); + final ExternalPatternInputSink sink = sinkBuilder.build(); + final Task task = getRunningTask(storage, patterns, sink, SMOOTH_STONE, 4); + + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 60) + ); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + + // Act & assert + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly( + new ResourceAmount(COBBLESTONE, 3) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 1) + ); + assertThat(stoneSink.getAll()).isEmpty(); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly( + new ResourceAmount(COBBLESTONE, 2) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 2) + ); + assertThat(stoneSink.getAll()).isEmpty(); + + storage.insert(STONE, 2, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(COBBLESTONE, 2), + new ResourceAmount(STONE, 2) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 2) + ); + assertThat(stoneSink.getAll()).isEmpty(); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(COBBLESTONE, 1), + new ResourceAmount(STONE, 1) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 3) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 1) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 2) + ); + + storage.insert(STONE, 2, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly( + new ResourceAmount(STONE, 2) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 2) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 1) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 3) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 4) + ); + + storage.insert(SMOOTH_STONE, 4, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(SMOOTH_STONE, 4) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 4) + ); + assertThat(stoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 4) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(COBBLESTONE, 60), + new ResourceAmount(SMOOTH_STONE, 4) + ); + } + + @Test + void shouldCompleteTaskWithExternalPatternsThatShareTheSameOutputResources() { + // Arrange + final Pattern aToStonePattern = pattern(PatternType.EXTERNAL) + .ingredient(A, 1) + .output(STONE, 1) + .build(); + final Pattern stoneBricksPattern = pattern() + .ingredient(STONE, 1) + .ingredient(STONE, 1) + .output(STONE_BRICKS, 1) + .build(); + final RootStorage storage = storage( + new ResourceAmount(COBBLESTONE, 1), + new ResourceAmount(A, 1) + ); + final PatternRepository patterns = patterns(STONE_PATTERN, aToStonePattern, stoneBricksPattern); + final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); + final ExternalPatternInputSinkBuilder.Sink cobblestoneSink = sinkBuilder.storageSink(STONE_PATTERN); + final ExternalPatternInputSinkBuilder.Sink aSink = sinkBuilder.storageSink(aToStonePattern); + final ExternalPatternInputSink sink = sinkBuilder.build(); + final Task task = getRunningTask(storage, patterns, sink, STONE_BRICKS, 1); + + assertThat(storage.getAll()).isEmpty(); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(COBBLESTONE, 1), + new ResourceAmount(A, 1) + ); + + // Act & assert + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(storage.getAll()).isEmpty(); + assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 1) + ); + assertThat(aSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(A, 1) + ); + + storage.insert(STONE, 1, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(storage.getAll()).isEmpty(); + assertThat(task.copyInternalStorageState()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(STONE, 1) + ); + assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(COBBLESTONE, 1) + ); + assertThat(aSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(A, 1) + ); + } + + @Test + void shouldNotCompleteTaskWithExternalPatternIfSinkDoesNotAcceptResources() { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(STICKS, 2 * 2), + new ResourceAmount(IRON_ORE, 3 * 2) + ); + final PatternRepository patterns = patterns(IRON_INGOT_PATTERN, IRON_PICKAXE_PATTERN); + final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); + final ExternalPatternInputSinkBuilder.Sink ironOreSink = sinkBuilder.storageSink(IRON_INGOT_PATTERN); + ironOreSink.setEnabled(false); + final ExternalPatternInputSink sink = sinkBuilder.build(); + final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 2); + + assertThat(storage.getAll()).isEmpty(); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 3 * 2), + new ResourceAmount(STICKS, 2 * 2) + ); + + // Act & assert + for (int i = 0; i < 6; ++i) { + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 3 * 2), + new ResourceAmount(STICKS, 2 * 2) + ); + } + + ironOreSink.setEnabled(true); + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, (3 * 2) - 1), + new ResourceAmount(STICKS, 2 * 2) + ); + assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 1) + ); + } + + @Test + void shouldNotCompleteTaskWithExternalPatternIfSinkDoesNotAcceptResourcesOnlyWhenExecuting() { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(STICKS, 2), + new ResourceAmount(IRON_ORE, 3) + ); + final PatternRepository patterns = patterns(IRON_INGOT_PATTERN, IRON_PICKAXE_PATTERN); + final ExternalPatternInputSink sink = (pattern, resources, action) -> + action == Action.SIMULATE; + final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); + + assertThat(storage.getAll()).isEmpty(); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 3), + new ResourceAmount(STICKS, 2) + ); + + // Act & assert + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 2), // we have voided 1 iron ore + new ResourceAmount(STICKS, 2) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 1), // we have voided 1 iron ore + new ResourceAmount(STICKS, 2) + ); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly(new ResourceAmount(STICKS, 2)); + + task.step(storage, sink); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactly(new ResourceAmount(STICKS, 2)); } @Test @@ -318,8 +635,8 @@ void shouldPartiallyReturnInternalStorage() { new ResourceAmount(SIGN, 10) ); final PatternRepository patterns = patterns(OAK_PLANKS_PATTERN, SPRUCE_PLANKS_PATTERN, CRAFTING_TABLE_PATTERN); - final TaskImpl task = getTaskReadyToReturnInternalStorage( - storage, patterns, ExternalPatternInputSink.EMPTY, CRAFTING_TABLE, 3 + final Task task = getTaskReadyToReturnInternalStorage( + storage, patterns, EMPTY_SINK, CRAFTING_TABLE, 3 ); // Act & assert @@ -330,20 +647,20 @@ void shouldPartiallyReturnInternalStorage() { final RootStorage returnStorage = new RootStorageImpl(); returnStorage.addSource(new LimitedStorageImpl(2)); - task.step(returnStorage, ExternalPatternInputSink.EMPTY); + task.step(returnStorage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); assertThat(returnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(CRAFTING_TABLE, 2) ); - task.step(returnStorage, ExternalPatternInputSink.EMPTY); + task.step(returnStorage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); assertThat(returnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(CRAFTING_TABLE, 2) ); returnStorage.addSource(new StorageImpl()); - task.step(returnStorage, ExternalPatternInputSink.EMPTY); + task.step(returnStorage, EMPTY_SINK); assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); assertThat(returnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(CRAFTING_TABLE, 3) diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskPlanTest.java index e84ad72c9..aab058628 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 @@ -1,27 +1,30 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.PatternRepository; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculator; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculatorImpl; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; +import java.util.Collection; import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.Test; -import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.patterns; -import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.storage; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingUtil.patterns; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingUtil.storage; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.CRAFTING_TABLE_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.OAK_PLANKS_PATTERN; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternFixtures.SPRUCE_PLANKS_PATTERN; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_PLANKS; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.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.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; +import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskPlanCraftingCalculatorListener.calculatePlan; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -58,13 +61,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 +76,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) @@ -87,8 +91,9 @@ void testPlanTaskWithIngredientsUsedFromRootStorageAndInternalStorageWithChildPa @Test void shouldNotModifyPlan() { // Arrange + final ResourceAmount oakLog = new ResourceAmount(OAK_LOG, 1); final RootStorage storage = storage( - new ResourceAmount(OAK_LOG, 1), + oakLog, new ResourceAmount(SPRUCE_LOG, 1), new ResourceAmount(OAK_PLANKS, 4) ); @@ -102,13 +107,18 @@ void shouldNotModifyPlan() { assertThat(optionalPlan).isPresent(); final TaskPlan plan = optionalPlan.get(); - assertThatThrownBy(() -> plan.initialRequirements().add(new ResourceAmount(OAK_LOG, 1))) + final Collection initialRequirements = plan.initialRequirements(); + assertThatThrownBy(() -> initialRequirements.add(oakLog)) .isInstanceOf(UnsupportedOperationException.class); - assertThatThrownBy(() -> plan.patterns().clear()) + final Map planPatterns = plan.patterns(); + assertThatThrownBy(planPatterns::clear) .isInstanceOf(UnsupportedOperationException.class); - assertThatThrownBy(() -> plan.pattern(CRAFTING_TABLE_PATTERN).ingredients().clear()) + final Map> craftingTableIngredients = + plan.getPattern(CRAFTING_TABLE_PATTERN).ingredients(); + assertThatThrownBy(craftingTableIngredients::clear) .isInstanceOf(UnsupportedOperationException.class); - assertThatThrownBy(() -> plan.pattern(CRAFTING_TABLE_PATTERN).ingredients().get(0).put(OAK_LOG, 1L)) + final Map firstIngredient = craftingTableIngredients.get(0); + assertThatThrownBy(() -> firstIngredient.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..502f7c23f 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 @@ -1,92 +1,54 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; -import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.PatternRepository; -import com.refinedmods.refinedstorage.api.autocrafting.PatternType; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculator; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculatorImpl; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; -import static com.refinedmods.refinedstorage.api.autocrafting.PatternBuilder.pattern; -import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; -import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_INGOT; -import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_ORE; -import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.IRON_PICKAXE; -import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; -import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_PLANKS; -import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.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 { - static final Pattern OAK_PLANKS_PATTERN = pattern() - .ingredient(OAK_LOG, 1) - .output(OAK_PLANKS, 4) - .build(); - static final Pattern SPRUCE_PLANKS_PATTERN = pattern() - .ingredient(SPRUCE_LOG, 1) - .output(SPRUCE_PLANKS, 4) - .build(); - static final Pattern CRAFTING_TABLE_PATTERN = pattern() - .ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end() - .ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end() - .ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end() - .ingredient(1).input(OAK_PLANKS).input(SPRUCE_PLANKS).end() - .output(CRAFTING_TABLE, 1) - .build(); - static final Pattern IRON_INGOT_PATTERN = pattern(PatternType.EXTERNAL) - .ingredient(IRON_ORE, 1) - .output(IRON_INGOT, 1) - .build(); - static final Pattern IRON_PICKAXE_PATTERN = pattern() - .ingredient(IRON_INGOT, 1) - .ingredient(IRON_INGOT, 1) - .ingredient(IRON_INGOT, 1) - .ingredient(STICKS, 2) - .output(IRON_PICKAXE, 1) - .build(); - private TaskUtil() { } - static TaskImpl getTask(final RootStorage storage, - final PatternRepository patterns, - final ResourceKey resource, - final long amount) { + static Task getTask(final RootStorage storage, + final PatternRepository patterns, + final ResourceKey resource, + final long amount) { final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); - final TaskImpl task = calculatePlan(sut, resource, amount).map(TaskImpl::new).orElseThrow(); + final Task task = calculatePlan(sut, resource, amount).map(TaskImpl::fromPlan).orElseThrow(); storage.addListener(task); return task; } - static TaskImpl getRunningTask(final RootStorage storage, - final PatternRepository patterns, - final ExternalPatternInputSink externalPatternInputSink, - final ResourceKey resource, - final long amount) { - final TaskImpl task = getTask(storage, patterns, resource, amount); + static Task getRunningTask(final RootStorage storage, + final PatternRepository patterns, + final ExternalPatternInputSink externalPatternInputSink, + final ResourceKey resource, + final long amount) { + final Task task = getTask(storage, patterns, resource, amount); assertThat(task.getState()).isEqualTo(TaskState.READY); task.step(storage, externalPatternInputSink); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); return task; } - static TaskImpl getTaskReadyToReturnInternalStorage(final RootStorage storage, - final PatternRepository patterns, - final ExternalPatternInputSink externalPatternInputSink, - final ResourceKey resource, - final long amount) { - final TaskImpl task = getRunningTask(storage, patterns, externalPatternInputSink, resource, amount); + static Task getTaskReadyToReturnInternalStorage(final RootStorage storage, + final PatternRepository patterns, + final ExternalPatternInputSink externalPatternInputSink, + final ResourceKey resource, + final long amount) { + final Task task = getRunningTask(storage, patterns, externalPatternInputSink, resource, amount); int tries = 0; while (task.getState() != TaskState.RETURNING_INTERNAL_STORAGE && tries < 10) { task.step(storage, externalPatternInputSink); tries++; } - assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE) - .describedAs("Task did not reach RETURNING_INTERNAL_STORAGE state"); + assertThat(task.getState()) + .withFailMessage("Task did not reach RETURNING_INTERNAL_STORAGE state in " + tries + " tries") + .isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); return task; } } diff --git a/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/RefinedStorageApi.java b/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/RefinedStorageApi.java index 5041b9902..4104e5dac 100644 --- a/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/RefinedStorageApi.java +++ b/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/RefinedStorageApi.java @@ -7,6 +7,7 @@ import com.refinedmods.refinedstorage.api.network.energy.EnergyStorage; import com.refinedmods.refinedstorage.api.network.node.NetworkNode; import com.refinedmods.refinedstorage.api.network.security.SecurityPolicy; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.api.constructordestructor.ConstructorStrategyFactory; import com.refinedmods.refinedstorage.common.api.constructordestructor.DestructorStrategyFactory; import com.refinedmods.refinedstorage.common.api.exporter.ExporterTransferStrategyFactory; @@ -107,6 +108,10 @@ public interface RefinedStorageApi { StorageMonitorInsertionStrategy getStorageMonitorInsertionStrategy(); + void addPatternProviderExternalPatternInputSinkFactory(PatternProviderExternalPatternInputSinkFactory factory); + + PatternProviderExternalPatternInputSinkFactory getPatternProviderExternalPatternInputSinkFactory(); + ComponentMapFactory getNetworkComponentMapFactory(); PlatformRegistry getGridSynchronizerRegistry(); diff --git a/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/RefinedStorageApiProxy.java b/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/RefinedStorageApiProxy.java index 85e16c7a3..55d18c72c 100644 --- a/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/RefinedStorageApiProxy.java +++ b/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/RefinedStorageApiProxy.java @@ -7,6 +7,7 @@ import com.refinedmods.refinedstorage.api.network.energy.EnergyStorage; import com.refinedmods.refinedstorage.api.network.node.NetworkNode; import com.refinedmods.refinedstorage.api.network.security.SecurityPolicy; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.api.constructordestructor.ConstructorStrategyFactory; import com.refinedmods.refinedstorage.common.api.constructordestructor.DestructorStrategyFactory; import com.refinedmods.refinedstorage.common.api.exporter.ExporterTransferStrategyFactory; @@ -164,6 +165,17 @@ public StorageMonitorInsertionStrategy getStorageMonitorInsertionStrategy() { return ensureLoaded().getStorageMonitorInsertionStrategy(); } + @Override + public void addPatternProviderExternalPatternInputSinkFactory( + final PatternProviderExternalPatternInputSinkFactory factory) { + ensureLoaded().addPatternProviderExternalPatternInputSinkFactory(factory); + } + + @Override + public PatternProviderExternalPatternInputSinkFactory getPatternProviderExternalPatternInputSinkFactory() { + return ensureLoaded().getPatternProviderExternalPatternInputSinkFactory(); + } + @Override public ComponentMapFactory getNetworkComponentMapFactory() { return ensureLoaded().getNetworkComponentMapFactory(); diff --git a/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/autocrafting/PatternProviderExternalPatternInputSinkFactory.java b/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/autocrafting/PatternProviderExternalPatternInputSinkFactory.java new file mode 100644 index 000000000..7a207548f --- /dev/null +++ b/refinedstorage-common-api/src/main/java/com/refinedmods/refinedstorage/common/api/autocrafting/PatternProviderExternalPatternInputSinkFactory.java @@ -0,0 +1,14 @@ +package com.refinedmods.refinedstorage.common.api.autocrafting; + +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import org.apiguardian.api.API; + +@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") +@FunctionalInterface +public interface PatternProviderExternalPatternInputSinkFactory { + PatternProviderExternalPatternInputSink create(ServerLevel level, BlockPos pos, Direction direction); +} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/AbstractModInitializer.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/AbstractModInitializer.java index ace0b4849..22d59edc4 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/AbstractModInitializer.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/AbstractModInitializer.java @@ -28,7 +28,6 @@ import com.refinedmods.refinedstorage.common.autocrafting.monitor.AutocraftingMonitorBlockEntity; import com.refinedmods.refinedstorage.common.autocrafting.monitor.AutocraftingMonitorContainerMenu; import com.refinedmods.refinedstorage.common.autocrafting.monitor.AutocraftingMonitorData; -import com.refinedmods.refinedstorage.common.autocrafting.monitor.TaskStatusProviderImpl; import com.refinedmods.refinedstorage.common.autocrafting.monitor.WirelessAutocraftingMonitorContainerMenu; import com.refinedmods.refinedstorage.common.autocrafting.patterngrid.PatternGridBlockEntity; import com.refinedmods.refinedstorage.common.autocrafting.patterngrid.PatternGridContainerMenu; @@ -277,7 +276,6 @@ private void registerNetworkComponents() { AutocraftingNetworkComponent.class, network -> new AutocraftingNetworkComponentImpl( () -> network.getComponent(StorageNetworkComponent.class), - new TaskStatusProviderImpl(), ServerListener.getAutocraftingPool() ) ); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/RefinedStorageApiImpl.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/RefinedStorageApiImpl.java index 9c7013ddb..14ede3cc2 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/RefinedStorageApiImpl.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/RefinedStorageApiImpl.java @@ -11,6 +11,7 @@ import com.refinedmods.refinedstorage.api.network.node.NetworkNode; import com.refinedmods.refinedstorage.api.network.security.SecurityPolicy; import com.refinedmods.refinedstorage.common.api.RefinedStorageApi; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderItem; import com.refinedmods.refinedstorage.common.api.constructordestructor.ConstructorStrategyFactory; import com.refinedmods.refinedstorage.common.api.constructordestructor.DestructorStrategyFactory; @@ -48,6 +49,7 @@ import com.refinedmods.refinedstorage.common.api.support.slotreference.SlotReferenceProvider; import com.refinedmods.refinedstorage.common.api.upgrade.UpgradeRegistry; import com.refinedmods.refinedstorage.common.api.wirelesstransmitter.WirelessTransmitterRangeModifier; +import com.refinedmods.refinedstorage.common.autocrafting.CompositePatternProviderExternalPatternInputSinkFactory; import com.refinedmods.refinedstorage.common.content.ContentIds; import com.refinedmods.refinedstorage.common.grid.NoopGridSynchronizer; import com.refinedmods.refinedstorage.common.grid.strategy.CompositeGridExtractionStrategy; @@ -172,6 +174,8 @@ public class RefinedStorageApiImpl implements RefinedStorageApi { private final PlatformRegistry permissionRegistry = new PlatformRegistryImpl<>(); private final List resourceExtractStrategies = new ArrayList<>(); private final Map patternCache = new HashMap<>(); + private final CompositePatternProviderExternalPatternInputSinkFactory + patternProviderExternalPatternInputSinkFactory = new CompositePatternProviderExternalPatternInputSinkFactory(); public RefinedStorageApiImpl() { gridSynchronizerRegistry.register(createIdentifier("off"), NoopGridSynchronizer.INSTANCE); @@ -268,6 +272,17 @@ public StorageMonitorInsertionStrategy getStorageMonitorInsertionStrategy() { return storageMonitorInsertionStrategy; } + @Override + public void addPatternProviderExternalPatternInputSinkFactory( + final PatternProviderExternalPatternInputSinkFactory factory) { + patternProviderExternalPatternInputSinkFactory.addFactory(factory); + } + + @Override + public PatternProviderExternalPatternInputSinkFactory getPatternProviderExternalPatternInputSinkFactory() { + return patternProviderExternalPatternInputSinkFactory; + } + @Override public ComponentMapFactory getNetworkComponentMapFactory() { return networkComponentMapFactory; diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/CompositePatternProviderExternalPatternInputSink.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/CompositePatternProviderExternalPatternInputSink.java new file mode 100644 index 000000000..65c1bd285 --- /dev/null +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/CompositePatternProviderExternalPatternInputSink.java @@ -0,0 +1,27 @@ +package com.refinedmods.refinedstorage.common.autocrafting; + +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; + +import java.util.Collection; +import java.util.Set; + +class CompositePatternProviderExternalPatternInputSink + implements PatternProviderExternalPatternInputSink { + private final Set sinks; + + CompositePatternProviderExternalPatternInputSink(final Set sinks) { + this.sinks = sinks; + } + + @Override + public boolean accept(final Collection resources, final Action action) { + for (final PatternProviderExternalPatternInputSink sink : sinks) { + if (!sink.accept(resources, action)) { + return false; + } + } + return true; + } +} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/CompositePatternProviderExternalPatternInputSinkFactory.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/CompositePatternProviderExternalPatternInputSinkFactory.java new file mode 100644 index 000000000..72a1a2651 --- /dev/null +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/CompositePatternProviderExternalPatternInputSinkFactory.java @@ -0,0 +1,30 @@ +package com.refinedmods.refinedstorage.common.autocrafting; + +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; + +import java.util.HashSet; +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 CompositePatternProviderExternalPatternInputSinkFactory + implements PatternProviderExternalPatternInputSinkFactory { + private final Set factories = new HashSet<>(); + + public void addFactory(final PatternProviderExternalPatternInputSinkFactory factory) { + factories.add(factory); + } + + @Override + public PatternProviderExternalPatternInputSink create(final ServerLevel level, + final BlockPos pos, + final Direction direction) { + return new CompositePatternProviderExternalPatternInputSink(factories.stream() + .map(factory -> factory.create(level, pos, direction)) + .collect(Collectors.toSet())); + } +} 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..d7679b42f 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( + RefinedStorageApi.INSTANCE.getPatternProviderExternalPatternInputSinkFactory() + .create(level, sourcePosition, incomingDirection)); + } + @Override public void patternChanged(final int slot) { if (level == null) { diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AbstractAutocraftingMonitorContainerMenu.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AbstractAutocraftingMonitorContainerMenu.java index ac082b622..59af2e604 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AbstractAutocraftingMonitorContainerMenu.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AbstractAutocraftingMonitorContainerMenu.java @@ -1,8 +1,8 @@ package com.refinedmods.refinedstorage.common.autocrafting.monitor; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.common.support.AbstractBaseContainerMenu; import com.refinedmods.refinedstorage.common.support.packet.c2s.C2SPackets; import com.refinedmods.refinedstorage.common.support.packet.s2c.S2CPackets; diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorBlockEntity.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorBlockEntity.java index 419146a69..ffb0ad678 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorBlockEntity.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorBlockEntity.java @@ -1,8 +1,8 @@ package com.refinedmods.refinedstorage.common.autocrafting.monitor; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.network.Network; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.impl.node.SimpleNetworkNode; diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorListener.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorListener.java index ae9d98f55..7421d233a 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorListener.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorListener.java @@ -1,7 +1,7 @@ package com.refinedmods.refinedstorage.common.autocrafting.monitor; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import javax.annotation.Nullable; diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorScreen.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorScreen.java index e785ea671..7ad133e57 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorScreen.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorScreen.java @@ -1,7 +1,7 @@ package com.refinedmods.refinedstorage.common.autocrafting.monitor; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.common.api.RefinedStorageClientApi; import com.refinedmods.refinedstorage.common.api.support.resource.ResourceRendering; import com.refinedmods.refinedstorage.common.support.AbstractBaseScreen; diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorStreamCodecs.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorStreamCodecs.java index 941cec2b1..a7c9a3cc3 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorStreamCodecs.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorStreamCodecs.java @@ -1,7 +1,7 @@ package com.refinedmods.refinedstorage.common.autocrafting.monitor; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.common.api.support.resource.PlatformResourceKey; import com.refinedmods.refinedstorage.common.support.resource.ResourceCodecs; import com.refinedmods.refinedstorage.common.util.PlatformUtil; diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingTaskButton.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingTaskButton.java index e4b5c8974..37a776d27 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingTaskButton.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingTaskButton.java @@ -1,7 +1,7 @@ package com.refinedmods.refinedstorage.common.autocrafting.monitor; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.common.api.RefinedStorageClientApi; import com.refinedmods.refinedstorage.common.api.support.resource.ResourceRendering; diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/TaskStatusProviderImpl.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/TaskStatusProviderImpl.java deleted file mode 100644 index 5657c23aa..000000000 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/TaskStatusProviderImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.refinedmods.refinedstorage.common.autocrafting.monitor; - -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusProvider; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class TaskStatusProviderImpl implements TaskStatusProvider { - private final List statuses = new ArrayList<>(); - private final Set listeners = new HashSet<>(); - - @Override - public List getStatuses() { - return Collections.unmodifiableList(statuses); - } - - @Override - public void addListener(final TaskStatusListener listener) { - listeners.add(listener); - } - - @Override - public void removeListener(final TaskStatusListener listener) { - listeners.remove(listener); - } - - @Override - public void cancel(final TaskId taskId) { - final TaskStatus status = statuses.stream() - .filter(s -> s.info().id().equals(taskId)) - .findFirst() - .orElse(null); - if (status != null) { - statuses.remove(status); - listeners.forEach(l -> l.taskRemoved(taskId)); - } - } - - @Override - public void cancelAll() { - final List copy = new ArrayList<>(statuses); - statuses.clear(); - copy.forEach(s -> listeners.forEach(l -> l.taskRemoved(s.info().id()))); - } -} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/WirelessAutocraftingMonitor.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/WirelessAutocraftingMonitor.java index d33dc41a4..6afaf3d63 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/WirelessAutocraftingMonitor.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/WirelessAutocraftingMonitor.java @@ -1,8 +1,8 @@ package com.refinedmods.refinedstorage.common.autocrafting.monitor; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.energy.EnergyNetworkComponent; import com.refinedmods.refinedstorage.common.Platform; 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/grid/screen/AbstractGridScreen.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/screen/AbstractGridScreen.java index 3523a5716..4b00d6d91 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/screen/AbstractGridScreen.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/screen/AbstractGridScreen.java @@ -92,7 +92,7 @@ protected AbstractGridScreen(final T menu, @Override protected void init(final int rows) { - LOGGER.info("Initializing grid screen - this shouldn't happen too much!"); + LOGGER.debug("Initializing grid screen - this shouldn't happen too much!"); if (searchField == null) { searchField = new GridSearchBoxWidget( 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..dc22006dd 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 @@ -1,8 +1,8 @@ package com.refinedmods.refinedstorage.common.support.packet.s2c; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.preview.Preview; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.storage.tracked.TrackedResource; import com.refinedmods.refinedstorage.common.Platform; @@ -105,8 +105,8 @@ public static void sendAutocraftingPreviewMaxAmountResponse(final ServerPlayer p public static void sendAutocraftingResponse(final ServerPlayer player, final UUID id, - final boolean started) { - Platform.INSTANCE.sendPacketToClient(player, new AutocraftingResponsePacket(id, started)); + final boolean success) { + Platform.INSTANCE.sendPacketToClient(player, new AutocraftingResponsePacket(id, success)); } public static void sendAutocraftingMonitorTaskAdded(final ServerPlayer player, final TaskStatus taskStatus) { diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java index b47b15fe0..179d5ad8a 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java @@ -46,9 +46,9 @@ public static void autocraftingPreviewResponseReceived(final UUID id, final Prev } } - public static void autocraftingResponseReceived(final UUID id, final boolean started) { + public static void autocraftingResponseReceived(final UUID id, final boolean success) { if (Minecraft.getInstance().screen instanceof AutocraftingPreviewScreen screen) { - screen.getMenu().responseReceived(id, started); + screen.getMenu().responseReceived(id, success); } } diff --git a/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/cancel.png b/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/cancel.png index ac98defee..6199afc4f 100644 Binary files a/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/cancel.png and b/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/cancel.png differ diff --git a/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/reset.png b/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/reset.png index c4dd7b837..dce66e8ec 100644 Binary files a/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/reset.png and b/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/reset.png differ diff --git a/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/start.png b/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/start.png index 5f362e5f3..8ca423d58 100644 Binary files a/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/start.png and b/refinedstorage-common/src/main/resources/assets/refinedstorage/textures/gui/sprites/start.png differ 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..45c85bc78 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 @@ -12,4 +12,8 @@ public interface RefinedStorageFabricApi { RefinedStorageFabricApi INSTANCE = new RefinedStorageFabricApiProxy(); BlockApiLookup getNetworkNodeContainerProviderLookup(); + + void addStorageExternalPatternInputSinkStrategyFactory( + FabricStorageExternalPatternInputSinkStrategyFactory factory + ); } 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..5276cfa14 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 @@ -29,4 +29,10 @@ private RefinedStorageFabricApi ensureLoaded() { public BlockApiLookup getNetworkNodeContainerProviderLookup() { return ensureLoaded().getNetworkNodeContainerProviderLookup(); } + + @Override + public void addStorageExternalPatternInputSinkStrategyFactory( + final FabricStorageExternalPatternInputSinkStrategyFactory factory) { + ensureLoaded().addStorageExternalPatternInputSinkStrategyFactory(factory); + } } 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..617ee0b98 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; @@ -190,7 +191,7 @@ public void onInitialize() { PlatformProxy.loadPlatform(new PlatformImpl()); initializePlatformApi(); ((RefinedStorageFabricApiProxy) RefinedStorageFabricApi.INSTANCE).setDelegate( - new RefinedStorageFabricApiImpl() + new RefinedStorageFabricApiImpl(RefinedStorageApi.INSTANCE) ); registerAdditionalGridInsertionStrategyFactories(); registerGridExtractionStrategyFactories(); @@ -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/RefinedStorageFabricApiImpl.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/RefinedStorageFabricApiImpl.java index e129a4bdf..9abaab5ec 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,11 @@ package com.refinedmods.refinedstorage.fabric; import com.refinedmods.refinedstorage.api.core.NullableType; +import com.refinedmods.refinedstorage.common.api.RefinedStorageApi; 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 net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup; import net.minecraft.core.Direction; @@ -16,9 +19,24 @@ public class RefinedStorageFabricApiImpl implements RefinedStorageFabricApi { NetworkNodeContainerProvider.class, Direction.class ); + private final FabricStoragePatternProviderExternalPatternInputSinkFactory + storagePatternProviderExternalPatternInputSinkFactory = + new FabricStoragePatternProviderExternalPatternInputSinkFactory(); + + public RefinedStorageFabricApiImpl(final RefinedStorageApi refinedStorageApi) { + refinedStorageApi.addPatternProviderExternalPatternInputSinkFactory( + storagePatternProviderExternalPatternInputSinkFactory + ); + } @Override public BlockApiLookup getNetworkNodeContainerProviderLookup() { return networkNodeContainerProvider; } + + @Override + public void addStorageExternalPatternInputSinkStrategyFactory( + final FabricStorageExternalPatternInputSinkStrategyFactory factory) { + storagePatternProviderExternalPatternInputSinkFactory.addFactory(factory); + } } 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..1f9e557f3 --- /dev/null +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/autocrafting/FabricStoragePatternProviderExternalPatternInputSinkFactory.java @@ -0,0 +1,32 @@ +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.HashSet; +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 = new HashSet<>(); + + public void addFactory(final FabricStorageExternalPatternInputSinkStrategyFactory factory) { + factories.add(factory); + } + + @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-grid-api/src/main/java/com/refinedmods/refinedstorage/api/grid/view/GridViewImpl.java b/refinedstorage-grid-api/src/main/java/com/refinedmods/refinedstorage/api/grid/view/GridViewImpl.java index af9b066a1..5730e434b 100644 --- a/refinedstorage-grid-api/src/main/java/com/refinedmods/refinedstorage/api/grid/view/GridViewImpl.java +++ b/refinedstorage-grid-api/src/main/java/com/refinedmods/refinedstorage/api/grid/view/GridViewImpl.java @@ -107,7 +107,7 @@ public boolean isAutocraftable(final ResourceKey resource) { @Override public void sort() { - LOGGER.info("Sorting grid view"); + LOGGER.debug("Sorting grid view"); viewList = createViewList(); notifyListener(); } diff --git a/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/ModInitializer.java b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/ModInitializer.java index d81ae69fc..81eab99a3 100644 --- a/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/ModInitializer.java +++ b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/ModInitializer.java @@ -84,6 +84,8 @@ import com.refinedmods.refinedstorage.common.util.ServerListener; import com.refinedmods.refinedstorage.neoforge.api.RefinedStorageNeoForgeApi; import com.refinedmods.refinedstorage.neoforge.api.RefinedStorageNeoForgeApiProxy; +import com.refinedmods.refinedstorage.neoforge.autocrafting.FluidHandlerExternalPatternProviderInputSinkFactory; +import com.refinedmods.refinedstorage.neoforge.autocrafting.ItemHandlerExternalPatternProviderInputSinkFactory; import com.refinedmods.refinedstorage.neoforge.constructordestructor.ForgeConstructorBlockEntity; import com.refinedmods.refinedstorage.neoforge.constructordestructor.ForgeDestructorBlockEntity; import com.refinedmods.refinedstorage.neoforge.exporter.FluidHandlerExporterTransferStrategyFactory; @@ -214,6 +216,7 @@ public ModInitializer(final IEventBus eventBus, final ModContainer modContainer) registerImporterTransferStrategyFactories(); registerExporterTransferStrategyFactories(); registerExternalStorageProviderFactories(); + registerPatternProviderInputSinkFactories(); registerContent(eventBus); registerSounds(eventBus); registerRecipeSerializers(eventBus); @@ -285,6 +288,15 @@ private void registerExternalStorageProviderFactories() { ); } + private void registerPatternProviderInputSinkFactories() { + RefinedStorageApi.INSTANCE.addPatternProviderExternalPatternInputSinkFactory( + new ItemHandlerExternalPatternProviderInputSinkFactory() + ); + RefinedStorageApi.INSTANCE.addPatternProviderExternalPatternInputSinkFactory( + new FluidHandlerExternalPatternProviderInputSinkFactory() + ); + } + private void registerContent(final IEventBus eventBus) { registerBlocks(eventBus); registerItems(eventBus); diff --git a/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/FluidHandlerExternalPatternProviderInputSink.java b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/FluidHandlerExternalPatternProviderInputSink.java new file mode 100644 index 000000000..07d316310 --- /dev/null +++ b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/FluidHandlerExternalPatternProviderInputSink.java @@ -0,0 +1,67 @@ +package com.refinedmods.refinedstorage.neoforge.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.common.support.resource.FluidResource; +import com.refinedmods.refinedstorage.neoforge.storage.CapabilityCache; + +import java.util.Collection; + +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.refinedmods.refinedstorage.neoforge.support.resource.VariantUtil.toFluidStack; + +class FluidHandlerExternalPatternProviderInputSink implements PatternProviderExternalPatternInputSink { + private static final Logger LOGGER = LoggerFactory.getLogger(FluidHandlerExternalPatternProviderInputSink.class); + + private final CapabilityCache capabilityCache; + + FluidHandlerExternalPatternProviderInputSink(final CapabilityCache capabilityCache) { + this.capabilityCache = capabilityCache; + } + + @Override + public boolean accept(final Collection resources, final Action action) { + return capabilityCache.getFluidHandler() + .map(handler -> accept(resources, action, handler)) + .orElse(true); + } + + private boolean accept(final Collection resources, + final Action action, + final IFluidHandler handler) { + for (final ResourceAmount resource : resources) { + if (resource.resource() instanceof FluidResource fluidResource + && !accept(action, handler, resource.amount(), fluidResource)) { + return false; + } + } + return true; + } + + private boolean accept(final Action action, + final IFluidHandler handler, + final long amount, + final FluidResource fluidResource) { + final FluidStack fluidStack = toFluidStack(fluidResource, amount); + final int filled = handler.fill( + fluidStack, + action == Action.SIMULATE ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE + ); + if (filled != fluidStack.getAmount()) { + if (action == Action.EXECUTE) { + LOGGER.warn( + "{} unexpectedly didn't accept all of {}, the remainder has been voided", + handler, + fluidStack + ); + } + return false; + } + return true; + } +} diff --git a/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/FluidHandlerExternalPatternProviderInputSinkFactory.java b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/FluidHandlerExternalPatternProviderInputSinkFactory.java new file mode 100644 index 000000000..abe84f14a --- /dev/null +++ b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/FluidHandlerExternalPatternProviderInputSinkFactory.java @@ -0,0 +1,19 @@ +package com.refinedmods.refinedstorage.neoforge.autocrafting; + +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; +import com.refinedmods.refinedstorage.neoforge.storage.CapabilityCacheImpl; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; + +public class FluidHandlerExternalPatternProviderInputSinkFactory + implements PatternProviderExternalPatternInputSinkFactory { + @Override + public PatternProviderExternalPatternInputSink create(final ServerLevel level, + final BlockPos pos, + final Direction direction) { + return new FluidHandlerExternalPatternProviderInputSink(new CapabilityCacheImpl(level, pos, direction)); + } +} diff --git a/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/ItemHandlerExternalPatternProviderInputSink.java b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/ItemHandlerExternalPatternProviderInputSink.java new file mode 100644 index 000000000..57b3dbe18 --- /dev/null +++ b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/ItemHandlerExternalPatternProviderInputSink.java @@ -0,0 +1,90 @@ +package com.refinedmods.refinedstorage.neoforge.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.common.support.resource.ItemResource; +import com.refinedmods.refinedstorage.neoforge.storage.CapabilityCache; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.items.IItemHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class ItemHandlerExternalPatternProviderInputSink implements PatternProviderExternalPatternInputSink { + private static final Logger LOGGER = LoggerFactory.getLogger(ItemHandlerExternalPatternProviderInputSink.class); + + private final CapabilityCache capabilityCache; + + ItemHandlerExternalPatternProviderInputSink(final CapabilityCache capabilityCache) { + this.capabilityCache = capabilityCache; + } + + @Override + public boolean accept(final Collection resources, final Action action) { + return capabilityCache.getItemHandler() + .map(handler -> accept(resources, action, handler)) + .orElse(true); + } + + private boolean accept(final Collection resources, + final Action action, + final IItemHandler handler) { + final Deque stacks = getStacks(resources); + ItemStack current = stacks.poll(); + final List availableSlots = IntStream.range(0, handler.getSlots()) + .boxed() + .collect(Collectors.toList()); + while (current != null && !availableSlots.isEmpty()) { + final ItemStack remainder = insert(action, handler, availableSlots, current); + if (remainder.isEmpty()) { + current = stacks.poll(); + } else if (current.getCount() == remainder.getCount()) { + break; + } else { + current = remainder; + } + } + final boolean success = current == null && stacks.isEmpty(); + if (!success && action == Action.EXECUTE) { + LOGGER.warn( + "{} unexpectedly left {} as a remainder, which has been voided", + handler, + stacks + ); + } + return success; + } + + private ItemStack insert(final Action action, + final IItemHandler handler, + final List availableSlots, + final ItemStack current) { + ItemStack remainder = ItemStack.EMPTY; + for (int i = 0; i < availableSlots.size(); ++i) { + final int slot = availableSlots.get(i); + remainder = handler.insertItem(slot, current.copy(), action == Action.SIMULATE); + if (remainder.isEmpty() || current.getCount() != remainder.getCount()) { + availableSlots.remove(i); + break; + } + } + return remainder; + } + + private static ArrayDeque getStacks(final Collection resources) { + return new ArrayDeque<>(resources.stream() + .filter(resourceAmount -> resourceAmount.resource() instanceof ItemResource) + .map(resourceAmount -> { + final ItemResource itemResource = (ItemResource) resourceAmount.resource(); + return itemResource.toItemStack(resourceAmount.amount()); + }).toList()); + } +} diff --git a/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/ItemHandlerExternalPatternProviderInputSinkFactory.java b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/ItemHandlerExternalPatternProviderInputSinkFactory.java new file mode 100644 index 000000000..59421d9e4 --- /dev/null +++ b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/autocrafting/ItemHandlerExternalPatternProviderInputSinkFactory.java @@ -0,0 +1,19 @@ +package com.refinedmods.refinedstorage.neoforge.autocrafting; + +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderExternalPatternInputSinkFactory; +import com.refinedmods.refinedstorage.neoforge.storage.CapabilityCacheImpl; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; + +public class ItemHandlerExternalPatternProviderInputSinkFactory + implements PatternProviderExternalPatternInputSinkFactory { + @Override + public PatternProviderExternalPatternInputSink create(final ServerLevel level, + final BlockPos pos, + final Direction direction) { + return new ItemHandlerExternalPatternProviderInputSink(new CapabilityCacheImpl(level, pos, direction)); + } +} diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/AutocraftingNetworkComponent.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/AutocraftingNetworkComponent.java index 8cfc6a8b2..dd52b4c1f 100644 --- a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/AutocraftingNetworkComponent.java +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/AutocraftingNetworkComponent.java @@ -3,6 +3,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewProvider; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusProvider; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSink; import com.refinedmods.refinedstorage.api.network.NetworkComponent; import com.refinedmods.refinedstorage.api.resource.ResourceKey; @@ -12,7 +13,8 @@ import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.8") -public interface AutocraftingNetworkComponent extends NetworkComponent, PreviewProvider, TaskStatusProvider { +public interface AutocraftingNetworkComponent + extends NetworkComponent, PreviewProvider, TaskStatusProvider, ExternalPatternInputSink { void addListener(PatternListener listener); void removeListener(PatternListener listener); diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/ParentContainer.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/ParentContainer.java index 808c953b0..e2c48244f 100644 --- a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/ParentContainer.java +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/ParentContainer.java @@ -6,9 +6,9 @@ @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.8") public interface ParentContainer { - void add(Pattern pattern, int priority); + void add(PatternProvider provider, Pattern pattern, int priority); - void remove(Pattern pattern); + void remove(PatternProvider provider, Pattern pattern); void update(Pattern pattern, int priority); } diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java index 0cfaa1052..9d871f9fb 100644 --- a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java @@ -1,9 +1,11 @@ package com.refinedmods.refinedstorage.api.network.autocrafting; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; + import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.8") -public interface PatternProvider { +public interface PatternProvider extends PatternProviderExternalPatternInputSink { void onAddedIntoContainer(ParentContainer parentContainer); void onRemovedFromContainer(ParentContainer parentContainer); @@ -11,4 +13,6 @@ public interface PatternProvider { default boolean contains(AutocraftingNetworkComponent component) { return false; } + + void addTask(Task task); } diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java new file mode 100644 index 000000000..962171f04 --- /dev/null +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java @@ -0,0 +1,14 @@ +package com.refinedmods.refinedstorage.api.network.autocrafting; + +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; + +import java.util.Collection; + +import org.apiguardian.api.API; + +@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") +@FunctionalInterface +public interface PatternProviderExternalPatternInputSink { + boolean accept(Collection resources, Action action); +} diff --git a/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/FakeTaskStatusProvider.java b/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/FakeTaskStatusProvider.java deleted file mode 100644 index c3b0e8b02..000000000 --- a/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/FakeTaskStatusProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.refinedmods.refinedstorage.network.test.fixtures; - -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusProvider; - -import java.util.List; - -public class FakeTaskStatusProvider implements TaskStatusProvider { - @Override - public List getStatuses() { - return List.of(); - } - - @Override - public void addListener(final TaskStatusListener listener) { - // no op - } - - @Override - public void removeListener(final TaskStatusListener listener) { - // no op - } - - @Override - public void cancel(final TaskId taskId) { - // no op - } - - @Override - public void cancelAll() { - // no op - } -} diff --git a/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/NetworkTestFixtures.java b/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/NetworkTestFixtures.java index ad000b12a..c1827a0d8 100644 --- a/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/NetworkTestFixtures.java +++ b/refinedstorage-network-test/src/main/java/com/refinedmods/refinedstorage/network/test/fixtures/NetworkTestFixtures.java @@ -42,10 +42,7 @@ public final class NetworkTestFixtures { NETWORK_COMPONENT_MAP_FACTORY.addFactory( AutocraftingNetworkComponent.class, network -> new AutocraftingNetworkComponentImpl( - () -> { - throw new UnsupportedOperationException("Storage not accessible from here (yet)"); - }, - new FakeTaskStatusProvider(), + () -> network.getComponent(StorageNetworkComponent.class), Executors.newSingleThreadExecutor() ) ); diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java index 1c9b093b8..e2c62cac8 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java @@ -2,25 +2,33 @@ import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.PatternRepositoryImpl; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculator; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculatorImpl; import com.refinedmods.refinedstorage.api.autocrafting.preview.Preview; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewCraftingCalculatorListener; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusProvider; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskImpl; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskPlan; +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.core.CoreValidations; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternListener; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider; import com.refinedmods.refinedstorage.api.network.node.container.NetworkNodeContainer; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.storage.Actor; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -30,29 +38,28 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AutocraftingNetworkComponentImpl implements AutocraftingNetworkComponent { +import static com.refinedmods.refinedstorage.api.autocrafting.task.TaskPlanCraftingCalculatorListener.calculatePlan; + +public class AutocraftingNetworkComponentImpl implements AutocraftingNetworkComponent, ParentContainer { private static final Logger LOGGER = LoggerFactory.getLogger(AutocraftingNetworkComponentImpl.class); private final Supplier rootStorageProvider; - private final TaskStatusProvider taskStatusProvider; private final ExecutorService executorService; private final Set providers = new HashSet<>(); + private final Map providerByPattern = new HashMap<>(); private final Set listeners = new HashSet<>(); private final PatternRepositoryImpl patternRepository = new PatternRepositoryImpl(); - private final ParentContainer parentContainer = new ParentContainerImpl(); public AutocraftingNetworkComponentImpl(final Supplier rootStorageProvider, - final TaskStatusProvider taskStatusProvider, final ExecutorService executorService) { this.rootStorageProvider = rootStorageProvider; - this.taskStatusProvider = taskStatusProvider; this.executorService = executorService; } @Override public void onContainerAdded(final NetworkNodeContainer container) { if (container.getNode() instanceof PatternProvider provider) { - provider.onAddedIntoContainer(parentContainer); + provider.onAddedIntoContainer(this); providers.add(provider); } } @@ -60,7 +67,7 @@ public void onContainerAdded(final NetworkNodeContainer container) { @Override public void onContainerRemoved(final NetworkNodeContainer container) { if (container.getNode() instanceof PatternProvider provider) { - provider.onRemovedFromContainer(parentContainer); + provider.onRemovedFromContainer(this); providers.remove(provider); } } @@ -95,8 +102,30 @@ public CompletableFuture getMaxAmount(final ResourceKey resource) { } @Override - public boolean startTask(final ResourceKey resource, final long amount, final Actor actor, final boolean notify) { - LOGGER.info("{} started a task for {}x {} with notify={}", actor, amount, resource, notify); + public CompletableFuture startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final boolean notify) { + return CompletableFuture.supplyAsync(() -> { + final RootStorage rootStorage = rootStorageProvider.get(); + final CraftingCalculator calculator = new CraftingCalculatorImpl(patternRepository, rootStorage); + return calculatePlan(calculator, resource, amount) + .map(plan -> startTask(resource, amount, actor, plan)) + .orElse(false); + }); + } + + private boolean startTask(final ResourceKey resource, + final long amount, + final Actor actor, + final TaskPlan plan) { + final Task task = TaskImpl.fromPlan(plan); + LOGGER.debug("Created task {} for {}x {} for {}", task.getId(), amount, resource, actor); + final PatternProvider patternProvider = CoreValidations.validateNotNull( + providerByPattern.get(plan.pattern()), + "No provider for pattern " + plan.pattern() + ); + patternProvider.addTask(task); return true; } @@ -107,7 +136,7 @@ public void addListener(final PatternListener listener) { @Override public void addListener(final TaskStatusListener listener) { - taskStatusProvider.addListener(listener); + // TODO(feat): autocrafting monitor } @Override @@ -117,7 +146,7 @@ public void removeListener(final PatternListener listener) { @Override public void removeListener(final TaskStatusListener listener) { - taskStatusProvider.removeListener(listener); + // TODO(feat): autocrafting monitor } @Override @@ -132,35 +161,46 @@ public List getPatternsByOutput(final ResourceKey output) { @Override public List getStatuses() { - return taskStatusProvider.getStatuses(); + // TODO(feat): autocrafting monitor + return List.of(); } @Override public void cancel(final TaskId taskId) { - taskStatusProvider.cancel(taskId); + // TODO(feat): autocrafting monitor } @Override public void cancelAll() { - taskStatusProvider.cancelAll(); + // TODO(feat): autocrafting monitor } - private class ParentContainerImpl implements ParentContainer { - @Override - public void add(final Pattern pattern, final int priority) { - patternRepository.add(pattern, priority); - listeners.forEach(listener -> listener.onAdded(pattern)); - } + @Override + public void add(final PatternProvider provider, final Pattern pattern, final int priority) { + patternRepository.add(pattern, priority); + providerByPattern.put(pattern, provider); + listeners.forEach(listener -> listener.onAdded(pattern)); + } - @Override - public void remove(final Pattern pattern) { - listeners.forEach(listener -> listener.onRemoved(pattern)); - patternRepository.remove(pattern); - } + @Override + public void remove(final PatternProvider provider, final Pattern pattern) { + listeners.forEach(listener -> listener.onRemoved(pattern)); + providerByPattern.remove(pattern); + patternRepository.remove(pattern); + } - @Override - public void update(final Pattern pattern, final int priority) { - patternRepository.update(pattern, priority); + @Override + public void update(final Pattern pattern, final int priority) { + patternRepository.update(pattern, priority); + } + + // TODO(feat): processing pattern balancing + @Override + public boolean accept(final Pattern pattern, final Collection resources, final Action action) { + final PatternProvider patternProvider = providerByPattern.get(pattern); + if (patternProvider == null) { + return false; } + return patternProvider.accept(resources, action); } } diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java index 597854cea..29cc545e8 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java @@ -1,32 +1,72 @@ package com.refinedmods.refinedstorage.api.network.impl.node.patternprovider; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSink; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskState; +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.network.Network; +import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider; +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProviderExternalPatternInputSink; import com.refinedmods.refinedstorage.api.network.impl.node.SimpleNetworkNode; +import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.Nullable; +// TODO(feat): return root results as soon as finished +// TODO(feat): persistence of tasks +// TODO(feat): autocrafter locking support +// TODO(feat): autocrafting monitor support +// TODO(feat): throttling and step behavior control public class PatternProviderNetworkNode extends SimpleNetworkNode implements PatternProvider { private final Pattern[] patterns; private final Set parents = new HashSet<>(); + private final List tasks = new CopyOnWriteArrayList<>(); private int priority; + @Nullable + private PatternProviderExternalPatternInputSink externalPatternInputSink; public PatternProviderNetworkNode(final long energyUsage, final int patterns) { super(energyUsage); this.patterns = new Pattern[patterns]; } + public void setExternalPatternInputSink(final PatternProviderExternalPatternInputSink externalPatternInputSink) { + this.externalPatternInputSink = externalPatternInputSink; + } + public void setPattern(final int index, @Nullable final Pattern pattern) { final Pattern oldPattern = patterns[index]; if (oldPattern != null) { - parents.forEach(parent -> parent.remove(oldPattern)); + parents.forEach(parent -> parent.remove(this, oldPattern)); } patterns[index] = pattern; if (pattern != null) { - parents.forEach(parent -> parent.add(pattern, priority)); + parents.forEach(parent -> parent.add(this, pattern, priority)); + } + } + + @Override + public void setNetwork(@Nullable final Network network) { + if (this.network != null) { + for (final Task task : tasks) { + this.network.getComponent(StorageNetworkComponent.class).removeListener(task); + } + } + super.setNetwork(network); + if (network != null) { + final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (final Task task : tasks) { + storage.addListener(task); + } } } @@ -36,14 +76,14 @@ protected void onActiveChanged(final boolean newActive) { if (!newActive) { for (final Pattern pattern : patterns) { if (pattern != null) { - parents.forEach(parent -> parent.remove(pattern)); + parents.forEach(parent -> parent.remove(this, pattern)); } } return; } for (final Pattern pattern : patterns) { if (pattern != null) { - parents.forEach(parent -> parent.add(pattern, priority)); + parents.forEach(parent -> parent.add(this, pattern, priority)); } } } @@ -53,7 +93,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 +103,46 @@ public void onRemovedFromContainer(final ParentContainer parentContainer) { parents.remove(parentContainer); for (final Pattern pattern : patterns) { if (pattern != null) { - parentContainer.remove(pattern); + parentContainer.remove(this, pattern); } } } + @Override + public void addTask(final Task task) { + tasks.add(task); + if (network != null) { + network.getComponent(StorageNetworkComponent.class).addListener(task); + } + } + + @Override + public boolean accept(final Collection resources, final Action action) { + if (externalPatternInputSink == null) { + return false; + } + return externalPatternInputSink.accept(resources, action); + } + + public List getTasks() { + return tasks; + } + + @Override + public void doWork() { + super.doWork(); + if (network == null || !isActive()) { + return; + } + final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + final ExternalPatternInputSink outerExternalPatternInputSink = + network.getComponent(AutocraftingNetworkComponent.class); + tasks.removeIf(task -> { + task.step(storage, outerExternalPatternInputSink); + return task.getState() == TaskState.COMPLETED; + }); + } + public int getPriority() { return priority; } diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java index 9424bb9c5..53f8d1548 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java @@ -1,5 +1,7 @@ package com.refinedmods.refinedstorage.api.network.impl.node.relay; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.core.Action; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider; @@ -13,11 +15,13 @@ import com.refinedmods.refinedstorage.api.network.security.SecurityNetworkComponent; import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; import com.refinedmods.refinedstorage.api.network.storage.StorageProvider; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.resource.filter.FilterMode; import com.refinedmods.refinedstorage.api.storage.AccessMode; import com.refinedmods.refinedstorage.api.storage.Storage; +import java.util.Collection; import java.util.Set; import java.util.function.UnaryOperator; import javax.annotation.Nullable; @@ -129,6 +133,11 @@ public boolean contains(final AutocraftingNetworkComponent component) { return patternProvider.contains(component); } + @Override + public void addTask(final Task task) { + // TODO(feat): relay support + } + @Override public SecurityDecision isAllowed(final Permission permission, final SecurityActor actor) { if (securityDelegate == null || securityDelegate.contains(securityDelegate)) { @@ -156,4 +165,9 @@ public void onAddedIntoContainer(final ParentContainer parentContainer) { public void onRemovedFromContainer(final ParentContainer parentContainer) { patternProvider.onRemovedFromContainer(parentContainer); } + + @Override + public boolean accept(final Collection resources, final Action action) { + return false; // TODO(feat): relay support + } } diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java index 9736b785c..803c19e13 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java @@ -1,6 +1,8 @@ package com.refinedmods.refinedstorage.api.network.impl.node.relay; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.core.Action; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternListener; @@ -10,6 +12,7 @@ import com.refinedmods.refinedstorage.api.resource.filter.Filter; import com.refinedmods.refinedstorage.api.resource.filter.FilterMode; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -44,12 +47,12 @@ private void reset(final Runnable action) { void setDelegate(@Nullable final AutocraftingNetworkComponent delegate) { if (this.delegate != null) { - parents.forEach(parent -> getPatterns().forEach(parent::remove)); + parents.forEach(parent -> getPatterns().forEach(pattern -> parent.remove(this, pattern))); this.delegate.removeListener(this); } this.delegate = delegate; if (delegate != null) { - parents.forEach(parent -> getPatterns().forEach(pattern -> parent.add(pattern, 0))); + parents.forEach(parent -> getPatterns().forEach(pattern -> parent.add(this, pattern, 0))); delegate.addListener(this); } } @@ -74,7 +77,7 @@ public void onAdded(final Pattern pattern) { if (delegate == null || !isPatternAllowed(pattern) || delegate.contains(delegate)) { return; } - parents.forEach(parent -> parent.add(pattern, 0)); + parents.forEach(parent -> parent.add(this, pattern, 0)); } @Override @@ -82,7 +85,7 @@ public void onRemoved(final Pattern pattern) { if (delegate == null || !isPatternAllowed(pattern) || delegate.contains(delegate)) { return; } - parents.forEach(parent -> parent.remove(pattern)); + parents.forEach(parent -> parent.remove(this, pattern)); } @Override @@ -90,10 +93,15 @@ public boolean contains(final AutocraftingNetworkComponent component) { return component == delegate || (delegate != null && delegate.contains(component)); } + @Override + public void addTask(final Task task) { + // TODO(feat): relay support + } + @Override public void onAddedIntoContainer(final ParentContainer parentContainer) { if (delegate != null) { - delegate.getPatterns().forEach(pattern -> parentContainer.add(pattern, 0)); + delegate.getPatterns().forEach(pattern -> parentContainer.add(this, pattern, 0)); } parents.add(parentContainer); } @@ -101,8 +109,13 @@ public void onAddedIntoContainer(final ParentContainer parentContainer) { @Override public void onRemovedFromContainer(final ParentContainer parentContainer) { if (delegate != null) { - delegate.getPatterns().forEach(parentContainer::remove); + delegate.getPatterns().forEach(pattern -> parentContainer.remove(this, pattern)); } parents.remove(parentContainer); } + + @Override + public boolean accept(final Collection resources, final Action action) { + return false; // TODO(feat): relay support + } } diff --git a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java index acd74effb..b27938ea3 100644 --- a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java +++ b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java @@ -1,26 +1,19 @@ package com.refinedmods.refinedstorage.api.network.impl.autocrafting; -import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.preview.Preview; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewItem; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; -import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; -import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.core.Action; -import com.refinedmods.refinedstorage.api.network.autocrafting.PatternListener; import com.refinedmods.refinedstorage.api.network.impl.node.patternprovider.PatternProviderNetworkNode; import com.refinedmods.refinedstorage.api.network.node.container.NetworkNodeContainer; import com.refinedmods.refinedstorage.api.storage.Actor; import com.refinedmods.refinedstorage.api.storage.StorageImpl; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; import com.refinedmods.refinedstorage.api.storage.root.RootStorageImpl; -import com.refinedmods.refinedstorage.network.test.fixtures.FakeTaskStatusProvider; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; @@ -40,49 +33,7 @@ class AutocraftingNetworkComponentImplTest { @BeforeEach void setUp() { rootStorage = new RootStorageImpl(); - sut = new AutocraftingNetworkComponentImpl( - () -> rootStorage, - new FakeTaskStatusProvider(), - Executors.newSingleThreadExecutor() - ); - } - - @Test - void temporaryCoverage() { - final PatternListener listener = new PatternListener() { - @Override - public void onAdded(final Pattern pattern) { - // no op - } - - @Override - public void onRemoved(final Pattern pattern) { - // no op - } - }; - sut.addListener(listener); - sut.removeListener(listener); - final TaskStatusListener listener2 = new TaskStatusListener() { - @Override - public void taskStatusChanged(final TaskStatus status) { - // no op - } - - @Override - public void taskRemoved(final TaskId id) { - // no op - } - - @Override - public void taskAdded(final TaskStatus status) { - // no op - } - }; - sut.addListener(listener2); - sut.removeListener(listener2); - sut.getStatuses(); - sut.cancel(new TaskId(UUID.randomUUID())); - sut.cancelAll(); + sut = new AutocraftingNetworkComponentImpl(() -> rootStorage, Executors.newSingleThreadExecutor()); } @Test @@ -115,11 +66,6 @@ void shouldRemovePatternsFromPatternProvider() { assertThat(sut.getOutputs()).usingRecursiveFieldByFieldElementComparator().isEmpty(); } - @Test - void shouldStartTask() { - sut.startTask(A, 10, Actor.EMPTY, true); - } - @Test void shouldGetPreview() throws ExecutionException, InterruptedException { // Arrange @@ -158,4 +104,39 @@ void shouldGetMaxAmount() throws ExecutionException, InterruptedException { // Assert assertThat(maxAmount).isEqualTo(16); } + + @Test + void shouldStartTask() throws ExecutionException, InterruptedException { + // Arrange + rootStorage.addSource(new StorageImpl()); + rootStorage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + final PatternProviderNetworkNode provider = new PatternProviderNetworkNode(0, 5); + provider.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + final NetworkNodeContainer container = () -> provider; + sut.onContainerAdded(container); + + // Act + final boolean success = sut.startTask(B, 1, Actor.EMPTY, false).get(); + + // Assert + assertThat(success).isTrue(); + assertThat(provider.getTasks()).hasSize(1); + } + + @Test + void shouldNotStartTaskWhenThereAreMissingIngredients() throws ExecutionException, InterruptedException { + // Arrange + final PatternProviderNetworkNode provider = new PatternProviderNetworkNode(0, 5); + provider.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + final NetworkNodeContainer container = () -> provider; + sut.onContainerAdded(container); + + // Act + final boolean success = sut.startTask(B, 2, Actor.EMPTY, false).get(); + + // Assert + assertThat(success).isFalse(); + assertThat(provider.getTasks()).isEmpty(); + } } diff --git a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNodeTest.java b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNodeTest.java index 6ba04dee7..99a3036e2 100644 --- a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNodeTest.java +++ b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNodeTest.java @@ -1,13 +1,25 @@ package com.refinedmods.refinedstorage.api.network.impl.node.patternprovider; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.PatternType; +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.network.Network; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; +import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.storage.Actor; +import com.refinedmods.refinedstorage.api.storage.Storage; +import com.refinedmods.refinedstorage.api.storage.StorageImpl; import com.refinedmods.refinedstorage.network.test.AddNetworkNode; +import com.refinedmods.refinedstorage.network.test.InjectNetwork; import com.refinedmods.refinedstorage.network.test.InjectNetworkAutocraftingComponent; +import com.refinedmods.refinedstorage.network.test.InjectNetworkStorageComponent; import com.refinedmods.refinedstorage.network.test.NetworkTest; import com.refinedmods.refinedstorage.network.test.SetupNetwork; import com.refinedmods.refinedstorage.network.test.nodefactory.PatternProviderNetworkNodeFactory; +import java.util.concurrent.ExecutionException; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -104,6 +116,374 @@ void shouldAddPatternsFromNetworkWhenActive( assertThat(autocrafting.getOutputs()).containsExactly(A); } + @Test + void shouldNotStepTasksWithoutNetwork( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act + sut.setNetwork(null); + sut.doWork(); + + // Assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + } + + @Test + void shouldNotStepTasksWhenInactive( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act + sut.setActive(false); + sut.doWork(); + + // Assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + } + + @Test + void shouldStepTasks( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern().ingredient(A, 3).output(B, 1).build()); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act & assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 1) + ); + assertThat(sut.getTasks()).isEmpty(); + } + + @Test + void shouldUseProviderAsSinkForExternalPatternInputsWhenSinkIsAttached( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + final Storage sinkContents = new StorageImpl(); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 1).build()); + sut.setExternalPatternInputSink((resources, action) -> { + if (action == Action.EXECUTE) { + resources.forEach(resource -> + sinkContents.insert(resource.resource(), resource.amount(), Action.EXECUTE, Actor.EMPTY)); + } + return true; + }); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act & assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sinkContents.getAll()).isEmpty(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sinkContents.getAll()).isEmpty(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sinkContents.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + } + + @Test + void shouldUseProviderAsSinkForExternalPatternInputsWhenSinkIsAttachedAndDoesNotAcceptResources( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 1).build()); + sut.setExternalPatternInputSink((resources, action) -> false); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act & assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + } + + @Test + void shouldNotUseProviderAsSinkForExternalPatternInputsWhenThereIsNoSinkAttached( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 1).build()); + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + // Act & assert + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 10) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + + sut.doWork(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + } + + @Test + void shouldInterceptNetworkInsertionsWhenWaitingForExternalPatternOutputs( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) throws ExecutionException, InterruptedException { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 5).build()); + // swallow resources + sut.setExternalPatternInputSink((resources, action) -> true); + + // Act & assert + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).get()).isTrue(); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + storage.insert(B, 3, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 3)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + storage.insert(B, 4, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 5)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 2) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 5)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 2) + ); + + sut.doWork(); + assertThat(sut.getTasks()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 7) + ); + } + + @Nested + @SetupNetwork(id = "other") + class NetworkChangeTest { + @Test + void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting, + @InjectNetwork("other") final Network otherNetwork, + @InjectNetworkStorageComponent(networkId = "other") final StorageNetworkComponent otherStorage + ) { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(A, 10, Action.EXECUTE, Actor.EMPTY); + + otherStorage.addSource(new StorageImpl()); + + sut.setPattern(1, pattern(PatternType.EXTERNAL).ingredient(A, 3).output(B, 5).build()); + // swallow resources + sut.setExternalPatternInputSink((resources, action) -> true); + + // Act & assert + assertThat(autocrafting.startTask(B, 1, Actor.EMPTY, false).join()).isTrue(); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + new ResourceAmount(A, 3) + ); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7) + ); + + sut.setNetwork(otherNetwork); + storage.insert(B, 3, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 3) + ); + assertThat(otherStorage.getAll()).isEmpty(); + + otherStorage.insert(B, 4, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 4)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 3) + ); + assertThat(otherStorage.getAll()).isEmpty(); + + otherStorage.insert(B, 2, Action.EXECUTE, Actor.EMPTY); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(B, 5)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 3) + ); + assertThat(otherStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(B, 1) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + + sut.doWork(); + assertThat(sut.getTasks()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(A, 7), + new ResourceAmount(B, 3) + ); + assertThat(otherStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(B, 6) + ); + } + } + @Nested class PriorityTest { @AddNetworkNode(properties = { @@ -111,6 +491,53 @@ class PriorityTest { }) private PatternProviderNetworkNode other; + @Test + void shouldNotUseProviderAsSinkForExternalChildPatternWhenProviderIsRemovedAndRootProviderIsStillPresent( + @InjectNetworkStorageComponent final StorageNetworkComponent storage, + @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting + ) { + // Arrange + storage.addSource(new StorageImpl()); + storage.insert(C, 10, Action.EXECUTE, Actor.EMPTY); + + final Pattern patternForA = pattern().output(A, 1).ingredient(B, 1).build(); + sut.setPattern(0, patternForA); + + final Pattern patternForB = pattern(PatternType.EXTERNAL) + .output(B, 1) + .ingredient(C, 1) + .build(); + other.setPattern(0, patternForB); + + // Act & assert + assertThat(autocrafting.startTask(A, 1, Actor.EMPTY, false).join()).isTrue(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 10) + ); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(C, 1)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 9) + ); + + autocrafting.onContainerRemoved(() -> other); + + sut.doWork(); + assertThat(sut.getTasks()).hasSize(1); + assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder(new ResourceAmount(C, 1)); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(C, 9) + ); + } + @Test void shouldSetPatternsRespectingPriority( @InjectNetworkAutocraftingComponent final AutocraftingNetworkComponent autocrafting