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 34f417e6e..4ac1940ea 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 @@ -39,6 +39,8 @@ abstract PatternStepResult step(MutableResourceList internalStorage, abstract RootStorageListener.InterceptResult interceptInsertion(ResourceKey resource, long amount); + abstract TaskSnapshot.PatternSnapshot createSnapshot(); + abstract void appendStatus(TaskStatusBuilder builder); abstract long getWeight(); diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkKey.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkKey.java index d9eaa12e0..18cb25af6 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkKey.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkKey.java @@ -2,8 +2,6 @@ import org.apiguardian.api.API; -@FunctionalInterface @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") public interface ExternalPatternInputSinkKey { - String getName(); } 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 ee513069a..d25ad6346 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 @@ -16,10 +16,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.util.Objects.requireNonNull; + class ExternalTaskPattern extends AbstractTaskPattern { private static final Logger LOGGER = LoggerFactory.getLogger(ExternalTaskPattern.class); - private final MutableResourceList expectedOutputs = MutableResourceListImpl.create(); + private final MutableResourceList expectedOutputs; private final ResourceList simulatedIterationInputs; private final long originalIterationsToSendToSink; private long iterationsToSendToSink; @@ -33,6 +35,7 @@ class ExternalTaskPattern extends AbstractTaskPattern { ExternalTaskPattern(final Pattern pattern, final TaskPlan.PatternPlan plan) { super(pattern, plan); this.originalIterationsToSendToSink = plan.iterations(); + this.expectedOutputs = MutableResourceListImpl.create(); pattern.outputs().forEach( output -> expectedOutputs.add(output.resource(), output.amount() * plan.iterations()) ); @@ -40,6 +43,22 @@ class ExternalTaskPattern extends AbstractTaskPattern { this.simulatedIterationInputs = calculateIterationInputs(Action.SIMULATE); } + ExternalTaskPattern(final TaskSnapshot.PatternSnapshot snapshot) { + super(snapshot.pattern(), new TaskPlan.PatternPlan( + snapshot.root(), + requireNonNull(snapshot.externalPattern()).originalIterationsToSendToSink(), + snapshot.ingredients() + )); + this.expectedOutputs = snapshot.externalPattern().copyExpectedOutputs(); + this.simulatedIterationInputs = snapshot.externalPattern().simulatedIterationInputs(); + this.originalIterationsToSendToSink = snapshot.externalPattern().originalIterationsToSendToSink(); + this.iterationsToSendToSink = snapshot.externalPattern().iterationsToSendToSink(); + this.iterationsReceived = snapshot.externalPattern().iterationsReceived(); + this.interceptedAnythingSinceLastStep = snapshot.externalPattern().interceptedAnythingSinceLastStep(); + this.lastSinkResult = snapshot.externalPattern().lastSinkResult(); + this.lastSinkResultKey = snapshot.externalPattern().lastSinkResultKey(); + } + @Override PatternStepResult step(final MutableResourceList internalStorage, final RootStorage rootStorage, @@ -168,4 +187,24 @@ private boolean acceptsIterationInputs(final MutableResourceList internalStorage } return true; } + + @Override + TaskSnapshot.PatternSnapshot createSnapshot() { + return new TaskSnapshot.PatternSnapshot( + root, + pattern, + ingredients, + null, + new TaskSnapshot.ExternalPatternSnapshot( + expectedOutputs.copy(), + simulatedIterationInputs, + originalIterationsToSendToSink, + iterationsToSendToSink, + iterationsReceived, + interceptedAnythingSinceLastStep, + lastSinkResult, + lastSinkResultKey + ) + ); + } } 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 ccb62c4de..951ecbea3 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 @@ -14,6 +14,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.util.Objects.requireNonNull; + class InternalTaskPattern extends AbstractTaskPattern { private static final Logger LOGGER = LoggerFactory.getLogger(InternalTaskPattern.class); @@ -26,6 +28,16 @@ class InternalTaskPattern extends AbstractTaskPattern { this.iterationsRemaining = plan.iterations(); } + InternalTaskPattern(final TaskSnapshot.PatternSnapshot snapshot) { + super(snapshot.pattern(), new TaskPlan.PatternPlan( + snapshot.root(), + requireNonNull(snapshot.internalPattern()).originalIterationsRemaining(), + snapshot.ingredients() + )); + this.originalIterationsRemaining = snapshot.internalPattern().originalIterationsRemaining(); + this.iterationsRemaining = snapshot.internalPattern().iterationsRemaining(); + } + @Override PatternStepResult step(final MutableResourceList internalStorage, final RootStorage rootStorage, @@ -89,4 +101,15 @@ protected PatternStepResult useIteration() { LOGGER.debug("Stepped {} with {} iterations remaining", pattern, iterationsRemaining); return iterationsRemaining == 0 ? PatternStepResult.COMPLETED : PatternStepResult.RUNNING; } + + @Override + TaskSnapshot.PatternSnapshot createSnapshot() { + return new TaskSnapshot.PatternSnapshot( + root, + pattern, + ingredients, + new TaskSnapshot.InternalPatternSnapshot(originalIterationsRemaining, iterationsRemaining), + null + ); + } } 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 e520f5f98..b54a57609 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,14 +1,11 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; -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 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") @@ -25,8 +22,6 @@ public interface Task extends RootStorageListener { TaskState getState(); - Collection copyInternalStorageState(); - boolean step(RootStorage rootStorage, ExternalPatternInputSink externalPatternInputSink, StepBehavior stepBehavior); void cancel(); 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 4c2f397d9..be64ebe8a 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 @@ -4,7 +4,6 @@ import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusBuilder; import com.refinedmods.refinedstorage.api.core.Action; -import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl; @@ -12,7 +11,6 @@ import com.refinedmods.refinedstorage.api.storage.root.RootStorage; import java.util.ArrayList; -import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -26,35 +24,58 @@ public class TaskImpl implements Task { private static final Logger LOGGER = LoggerFactory.getLogger(TaskImpl.class); - private final TaskId id = TaskId.create(); + private final TaskId id; private final ResourceKey resource; private final long amount; private final Actor actor; private final boolean notify; - private final long startTime = System.currentTimeMillis(); + private final long startTime; private final Map patterns; private final List completedPatterns = new ArrayList<>(); - private final MutableResourceList initialRequirements = MutableResourceListImpl.create(); + private final MutableResourceList initialRequirements; private final MutableResourceList internalStorage; private TaskState state = TaskState.READY; private boolean cancelled; + public TaskImpl(final TaskSnapshot snapshot) { + this.id = snapshot.id(); + this.resource = snapshot.resource(); + this.amount = snapshot.amount(); + this.actor = snapshot.actor(); + this.notify = snapshot.notifyActor(); + this.startTime = snapshot.startTime(); + this.patterns = snapshot.patterns().entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + e -> e.getValue().toTaskPattern(), + (a, b) -> a, + LinkedHashMap::new + )); + snapshot.completedPatterns().forEach(patternSnapshot -> completedPatterns.add(patternSnapshot.toTaskPattern())); + this.initialRequirements = snapshot.copyInitialRequirements(); + this.internalStorage = snapshot.copyInternalStorage(); + this.state = snapshot.state(); + this.cancelled = snapshot.cancelled(); + } + public TaskImpl(final TaskPlan plan, final Actor actor, final boolean notify) { this(plan, MutableResourceListImpl.create(), actor, notify); } TaskImpl(final TaskPlan plan, final MutableResourceList internalStorage, final Actor actor, final boolean notify) { + this.id = TaskId.create(); this.internalStorage = internalStorage; this.resource = plan.resource(); this.amount = plan.amount(); this.actor = actor; this.notify = notify; + this.startTime = System.currentTimeMillis(); this.patterns = plan.patterns().entrySet().stream().collect(Collectors.toMap( Map.Entry::getKey, e -> createTaskPattern(e.getKey(), e.getValue()), (a, b) -> a, LinkedHashMap::new )); + this.initialRequirements = MutableResourceListImpl.create(); plan.initialRequirements().forEach(initialRequirements::add); } @@ -140,6 +161,32 @@ public TaskStatus getStatus() { return builder.build(totalWeight == 0 ? 0 : totalWeightedCompleted / totalWeight); } + public TaskSnapshot createSnapshot() { + return new TaskSnapshot( + id, + resource, + amount, + actor, + notify, + startTime, + patterns.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + e -> e.getValue().createSnapshot(), + (a, b) -> a, + LinkedHashMap::new + )), + completedPatterns.stream() + .filter(InternalTaskPattern.class::isInstance) + .map(InternalTaskPattern.class::cast) + .map(InternalTaskPattern::createSnapshot) + .toList(), + initialRequirements.copy(), + internalStorage.copy(), + state, + cancelled + ); + } + private boolean startTask(final RootStorage rootStorage) { updateState(TaskState.EXTRACTING_INITIAL_RESOURCES); return extractInitialResourcesAndTryStartRunningTask(rootStorage); @@ -243,11 +290,6 @@ private boolean returnInternalStorageAndTryCompleteTask(final RootStorage rootSt return returnedAny; } - @Override - public Collection copyInternalStorageState() { - return internalStorage.copyState(); - } - @Override public InterceptResult beforeInsert(final ResourceKey insertedResource, final long insertedAmount, diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskSnapshot.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskSnapshot.java new file mode 100644 index 000000000..18bf112c2 --- /dev/null +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskSnapshot.java @@ -0,0 +1,73 @@ +package com.refinedmods.refinedstorage.api.autocrafting.task; + +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.resource.ResourceKey; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl; +import com.refinedmods.refinedstorage.api.resource.list.ResourceList; +import com.refinedmods.refinedstorage.api.storage.Actor; + +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +public record TaskSnapshot( + TaskId id, + ResourceKey resource, + long amount, + Actor actor, + boolean notifyActor, + long startTime, + Map patterns, + List completedPatterns, + ResourceList initialRequirements, + ResourceList internalStorage, + TaskState state, + boolean cancelled +) { + MutableResourceList copyInitialRequirements() { + final MutableResourceList copy = MutableResourceListImpl.create(); + initialRequirements.getAll().forEach(key -> copy.add(key, initialRequirements.get(key))); + return copy; + } + + public MutableResourceList copyInternalStorage() { + final MutableResourceList copy = MutableResourceListImpl.create(); + internalStorage.getAll().forEach(key -> copy.add(key, internalStorage.get(key))); + return copy; + } + + public record PatternSnapshot( + boolean root, + Pattern pattern, + Map> ingredients, + @Nullable InternalPatternSnapshot internalPattern, + @Nullable ExternalPatternSnapshot externalPattern + ) { + AbstractTaskPattern toTaskPattern() { + return internalPattern != null ? new InternalTaskPattern(this) : new ExternalTaskPattern(this); + } + } + + public record InternalPatternSnapshot(long originalIterationsRemaining, long iterationsRemaining) { + } + + public record ExternalPatternSnapshot( + ResourceList expectedOutputs, + ResourceList simulatedIterationInputs, + long originalIterationsToSendToSink, + long iterationsToSendToSink, + long iterationsReceived, + boolean interceptedAnythingSinceLastStep, + @Nullable + ExternalPatternInputSink.Result lastSinkResult, + @Nullable + ExternalPatternInputSinkKey lastSinkResultKey + ) { + MutableResourceList copyExpectedOutputs() { + final MutableResourceList copy = MutableResourceListImpl.create(); + expectedOutputs.getAll().forEach(key -> copy.add(key, expectedOutputs.get(key))); + return copy; + } + } +} 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 45b50101e..f18542a59 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 @@ -47,10 +47,6 @@ public ExternalPatternInputSinkKey getKey(final Pattern pattern) { } record SinkKey(Pattern pattern) implements ExternalPatternInputSinkKey { - @Override - public String getName() { - return pattern.id().toString(); - } } static class Sink { 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 754e0e885..1f3fb742c 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 @@ -112,7 +112,7 @@ void shouldCancelTask() { // Act & assert assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(SPRUCE_LOG, 1), @@ -129,7 +129,7 @@ void shouldCancelTask() { assertThat(task.getActor()).isEqualTo(Actor.EMPTY); assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(SPRUCE_LOG, 1), @@ -140,7 +140,7 @@ void shouldCancelTask() { assertThat(task.step(storage, EMPTY_SINK, StepBehavior.DEFAULT)).isTrue(); assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(SPRUCE_LOG, 1), new ResourceAmount(OAK_LOG, 1), @@ -166,7 +166,7 @@ void shouldExtractAllResources() { // Assert assertThat(changed).isTrue(); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_LOG, 1), @@ -249,7 +249,7 @@ void shouldCompleteTaskWithInternalPatterns() { assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(SIGN, 10) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(SPRUCE_PLANKS, 4), @@ -262,7 +262,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(SIGN, 10), new ResourceAmount(CRAFTING_TABLE, 1) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(SPRUCE_PLANKS, 3), @@ -275,7 +275,7 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(SIGN, 10), new ResourceAmount(CRAFTING_TABLE, 2) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(SPRUCE_PLANKS, 2), @@ -288,11 +288,11 @@ void shouldCompleteTaskWithInternalPatterns() { new ResourceAmount(SIGN, 10), new ResourceAmount(CRAFTING_TABLE, 3) ); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); task.step(storage, EMPTY_SINK, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(SIGN, 10), new ResourceAmount(CRAFTING_TABLE, 3) @@ -321,7 +321,7 @@ void shouldPartiallyReturnOutputsWithInternalPatterns() { task.step(returnStorage, EMPTY_SINK, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(returnStorage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(SPRUCE_PLANKS, 4), @@ -333,7 +333,7 @@ void shouldPartiallyReturnOutputsWithInternalPatterns() { assertThat(returnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(CRAFTING_TABLE, 2) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_PLANKS, 2), @@ -345,7 +345,7 @@ void shouldPartiallyReturnOutputsWithInternalPatterns() { assertThat(returnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(CRAFTING_TABLE, 3) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactly( new ResourceAmount(CRAFTING_TABLE, 1) @@ -356,7 +356,7 @@ void shouldPartiallyReturnOutputsWithInternalPatterns() { assertThat(returnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(CRAFTING_TABLE, 3) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactly( new ResourceAmount(CRAFTING_TABLE, 1) @@ -373,7 +373,7 @@ void shouldPartiallyReturnOutputsWithInternalPatterns() { assertThat(finalReturnStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(CRAFTING_TABLE, 1) ); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); } @Test @@ -387,10 +387,10 @@ void shouldCompleteTaskWithExternalPatternAsChildPatternOfInternalPattern() { final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); final ExternalPatternInputSinkBuilder.Sink ironOreSink = sinkBuilder.storageSink(IRON_INGOT_PATTERN); final ExternalPatternInputSink sink = sinkBuilder.build(); - final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); + Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); assertThat(storage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 3), @@ -400,7 +400,7 @@ void shouldCompleteTaskWithExternalPatternAsChildPatternOfInternalPattern() { // Act & assert task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 2), @@ -412,7 +412,7 @@ void shouldCompleteTaskWithExternalPatternAsChildPatternOfInternalPattern() { task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 1), @@ -424,7 +424,7 @@ void shouldCompleteTaskWithExternalPatternAsChildPatternOfInternalPattern() { task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(STICKS, 2) @@ -435,7 +435,7 @@ void shouldCompleteTaskWithExternalPatternAsChildPatternOfInternalPattern() { task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(STICKS, 2) @@ -444,10 +444,14 @@ void shouldCompleteTaskWithExternalPatternAsChildPatternOfInternalPattern() { new ResourceAmount(IRON_ORE, 3) ); + storage.removeListener(task); + task = new TaskImpl(((TaskImpl) task).createSnapshot()); + storage.addListener(task); + storage.insert(IRON_INGOT, 1, Action.EXECUTE, Actor.EMPTY); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(storage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_INGOT, 1), @@ -457,7 +461,7 @@ void shouldCompleteTaskWithExternalPatternAsChildPatternOfInternalPattern() { task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(storage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_INGOT, 1), @@ -471,7 +475,7 @@ void shouldCompleteTaskWithExternalPatternAsChildPatternOfInternalPattern() { new ResourceAmount(IRON_INGOT, 3), new ResourceAmount(STONE, 2) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_INGOT, 3), @@ -485,7 +489,7 @@ void shouldCompleteTaskWithExternalPatternAsChildPatternOfInternalPattern() { new ResourceAmount(IRON_INGOT, 3), new ResourceAmount(STONE, 2) ); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); } @Test @@ -504,7 +508,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(COBBLESTONE, 60) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactly( new ResourceAmount(COBBLESTONE, 4) @@ -513,7 +517,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { // Act & assert assertThat(task.step(storage, sink, StepBehavior.DEFAULT)).isTrue(); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactly( new ResourceAmount(COBBLESTONE, 3) @@ -525,7 +529,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { assertThat(task.step(storage, sink, StepBehavior.DEFAULT)).isTrue(); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactly( new ResourceAmount(COBBLESTONE, 2) @@ -537,7 +541,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { storage.insert(STONE, 2, Action.EXECUTE, Actor.EMPTY); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(COBBLESTONE, 2), @@ -550,7 +554,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { assertThat(task.step(storage, sink, StepBehavior.DEFAULT)).isTrue(); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(COBBLESTONE, 1), @@ -565,7 +569,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { assertThat(task.step(storage, sink, StepBehavior.DEFAULT)).isTrue(); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(COBBLESTONE, 4) ); @@ -575,7 +579,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { assertThat(task.step(storage, sink, StepBehavior.DEFAULT)).isFalse(); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(COBBLESTONE, 4) ); @@ -585,7 +589,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { storage.insert(STONE, 2, Action.EXECUTE, Actor.EMPTY); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactly( new ResourceAmount(STONE, 2) @@ -599,7 +603,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { assertThat(task.step(storage, sink, StepBehavior.DEFAULT)).isTrue(); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()).usingRecursiveFieldByFieldElementComparator().containsExactly( + assertThat(copyInternalStorage(task)).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(STONE, 1) ); assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( @@ -611,7 +615,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { assertThat(task.step(storage, sink, StepBehavior.DEFAULT)).isTrue(); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(COBBLESTONE, 4) ); @@ -621,7 +625,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { storage.insert(SMOOTH_STONE, 4, Action.EXECUTE, Actor.EMPTY); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(COBBLESTONE, 4) ); @@ -635,7 +639,7 @@ void shouldCompleteTaskWithExternalPatternsThatAreDependentOnEachOther() { assertThat(task.step(storage, sink, StepBehavior.DEFAULT)).isTrue(); assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(COBBLESTONE, 4) ); @@ -674,7 +678,7 @@ void shouldCompleteTaskWithExternalPatternsThatShareTheSameOutputResources() { final Task task = getRunningTask(storage, patterns, sink, STONE_BRICKS, 1); assertThat(storage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(COBBLESTONE, 1), @@ -685,7 +689,7 @@ void shouldCompleteTaskWithExternalPatternsThatShareTheSameOutputResources() { task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(storage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(COBBLESTONE, 1) ); @@ -696,7 +700,7 @@ void shouldCompleteTaskWithExternalPatternsThatShareTheSameOutputResources() { storage.insert(STONE, 2, Action.EXECUTE, Actor.EMPTY); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(storage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()).usingRecursiveFieldByFieldElementComparator().containsExactly( + assertThat(copyInternalStorage(task)).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(STONE, 2) ); assertThat(cobblestoneSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( @@ -722,7 +726,7 @@ void shouldNotCompleteTaskWithExternalPatternIfSinkDoesNotAcceptResources() { final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 2); assertThat(storage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 3 * 2), @@ -733,7 +737,7 @@ void shouldNotCompleteTaskWithExternalPatternIfSinkDoesNotAcceptResources() { for (int i = 0; i < 6; ++i) { task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 3 * 2), @@ -744,7 +748,7 @@ void shouldNotCompleteTaskWithExternalPatternIfSinkDoesNotAcceptResources() { ironOreSink.setEnabled(true); task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, (3 * 2) - 1), @@ -782,7 +786,7 @@ public ExternalPatternInputSinkKey getKey(final Pattern pattern) { final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); assertThat(storage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 3), @@ -792,7 +796,7 @@ public ExternalPatternInputSinkKey getKey(final Pattern pattern) { // Act & assert task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 2), // we have voided 1 iron ore @@ -801,7 +805,7 @@ public ExternalPatternInputSinkKey getKey(final Pattern pattern) { task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 1), // we have voided 1 iron ore @@ -810,13 +814,13 @@ public ExternalPatternInputSinkKey getKey(final Pattern pattern) { task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactly(new ResourceAmount(STICKS, 2)); task.step(storage, sink, StepBehavior.DEFAULT); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactly(new ResourceAmount(STICKS, 2)); } @@ -839,7 +843,7 @@ public int getSteps(final Pattern pattern) { }); assertThat(task.getState()).isEqualTo(TaskState.RUNNING); assertThat(storage.getAll()).isEmpty(); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_LOG, 10 - 3), @@ -856,7 +860,7 @@ public int getSteps(final Pattern pattern) { assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(CRAFTING_TABLE, 3) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_LOG, 10 - 3 - 4), @@ -878,7 +882,7 @@ public int getSteps(final Pattern pattern) { assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(CRAFTING_TABLE, 3) ); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_PLANKS, 4 * 7) @@ -894,7 +898,7 @@ public int getSteps(final Pattern pattern) { assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(CRAFTING_TABLE, 10) ); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); } @Test @@ -917,7 +921,7 @@ void shouldReportStatusCorrectlyForInternalPatterns() { .stored(OAK_PLANKS, 4) .stored(OAK_LOG, 1) .build(0.25)); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_LOG, 1), @@ -955,7 +959,7 @@ void shouldReportStatusCorrectlyForInternalAndExternalPatterns() { .stored(OAK_PLANKS, 4) .stored(IRON_ORE, 2) .build(0.2)); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 2), @@ -976,7 +980,7 @@ void shouldReportStatusCorrectlyForInternalAndExternalPatterns() { .stored(IRON_ORE, 1) .stored(STICKS, 4) .build(0.5)); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(IRON_ORE, 1), @@ -996,7 +1000,7 @@ void shouldReportStatusCorrectlyForInternalAndExternalPatterns() { .stored(OAK_PLANKS, 2) .stored(STICKS, 4) .build(0.6666666666666666)); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_PLANKS, 2), @@ -1016,7 +1020,7 @@ void shouldReportStatusCorrectlyForInternalAndExternalPatterns() { .stored(STICKS, 4) .stored(IRON_INGOT, 2) .build(0.6666666666666666)); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_PLANKS, 2), @@ -1036,7 +1040,7 @@ void shouldReportStatusCorrectlyForInternalAndExternalPatterns() { .stored(STICKS, 4) .stored(IRON_INGOT, 3) .build(0.6666666666666666)); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_PLANKS, 2), @@ -1058,7 +1062,7 @@ void shouldReportStatusCorrectlyForInternalAndExternalPatterns() { .stored(OAK_PLANKS, 2) .stored(STICKS, 2) .build(1.0)); - assertThat(task.copyInternalStorageState()) + assertThat(copyInternalStorage(task)) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder( new ResourceAmount(OAK_PLANKS, 2), @@ -1078,7 +1082,7 @@ void shouldReportStatusCorrectlyForInternalAndExternalPatterns() { assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0).build(1.0) ); - assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(task)).isEmpty(); assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( new ResourceAmount(IRON_ORE, 3) ); @@ -1211,4 +1215,8 @@ private static Task getRunningTask(final RootStorage storage, assertThat(task.getState()).isEqualTo(TaskState.RUNNING); return task; } + + private static Collection copyInternalStorage(final Task task) { + return ((TaskImpl) task).createSnapshot().copyInternalStorage().copyState(); + } } 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 bdb7f3afb..797104d50 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 @@ -3,6 +3,9 @@ import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.StepBehavior; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskImpl; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskSnapshot; import com.refinedmods.refinedstorage.api.network.Network; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider; @@ -32,6 +35,7 @@ import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.codec.StreamEncoder; @@ -44,7 +48,11 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import static com.refinedmods.refinedstorage.common.autocrafting.autocrafter.TaskSnapshotPersistence.decodeSnapshot; +import static com.refinedmods.refinedstorage.common.autocrafting.autocrafter.TaskSnapshotPersistence.encodeSnapshot; import static com.refinedmods.refinedstorage.common.support.AbstractDirectionalBlock.tryExtractDirection; public class AutocrafterBlockEntity extends AbstractBaseNetworkNodeContainerBlockEntity @@ -52,11 +60,14 @@ public class AutocrafterBlockEntity extends AbstractBaseNetworkNodeContainerBloc ExternalPatternInputSinkKeyProvider { static final int PATTERNS = 9; + private static final Logger LOGGER = LoggerFactory.getLogger(AutocrafterBlockEntity.class); + private static final int MAX_CHAINED_AUTOCRAFTERS = 8; private static final String TAG_UPGRADES = "upgr"; private static final String TAG_PATTERNS = "patterns"; private static final String TAG_LOCK_MODE = "lm"; private static final String TAG_PRIORITY = "pri"; + private static final String TAG_TASKS = "tasks"; private static final String TAG_VISIBLE_TO_THE_AUTOCRAFTER_MANAGER = "vaum"; private final PatternInventory patternContainer = new PatternInventory(PATTERNS, this::getLevel); @@ -95,6 +106,7 @@ public AutocrafterBlockEntity(final BlockPos pos, final BlockState state) { this.patternContainer.setListener(this); this.mainNetworkNode.setStepBehavior(this); this.mainNetworkNode.setSinkKeyProvider(this); + this.mainNetworkNode.onAddedIntoContainer(new AutocrafterParentContainer(this)); } @Override @@ -215,6 +227,17 @@ public void saveAdditional(final CompoundTag tag, final HolderLookup.Provider pr super.saveAdditional(tag, provider); tag.put(TAG_PATTERNS, ContainerUtil.write(patternContainer, provider)); tag.put(TAG_UPGRADES, ContainerUtil.write(upgradeContainer, provider)); + final ListTag tasks = new ListTag(); + for (final Task task : mainNetworkNode.getTasks()) { + if (task instanceof TaskImpl taskImpl) { + try { + tasks.add(encodeSnapshot(taskImpl.createSnapshot())); + } catch (final Exception e) { + LOGGER.error("Error while saving task {} {}", task.getResource(), task.getAmount(), e); + } + } + } + tag.put(TAG_TASKS, tasks); } @Override @@ -233,6 +256,18 @@ public void loadAdditional(final CompoundTag tag, final HolderLookup.Provider pr if (tag.contains(TAG_UPGRADES)) { ContainerUtil.read(tag.getCompound(TAG_UPGRADES), upgradeContainer, provider); } + if (tag.contains(TAG_TASKS)) { + final ListTag tasks = tag.getList(TAG_TASKS, CompoundTag.TAG_COMPOUND); + for (int i = 0; i < tasks.size(); ++i) { + final CompoundTag taskTag = tasks.getCompound(i); + try { + final TaskSnapshot snapshot = decodeSnapshot(taskTag); + mainNetworkNode.addTask(new TaskImpl(snapshot)); + } catch (final Exception e) { + LOGGER.error("Error while loading task, skipping", e); + } + } + } super.loadAdditional(tag, provider); } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterParentContainer.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterParentContainer.java new file mode 100644 index 000000000..577626462 --- /dev/null +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterParentContainer.java @@ -0,0 +1,44 @@ +package com.refinedmods.refinedstorage.common.autocrafting.autocrafter; + +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.task.Task; +import com.refinedmods.refinedstorage.api.network.autocrafting.ParentContainer; +import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider; + +class AutocrafterParentContainer implements ParentContainer { + private final AutocrafterBlockEntity blockEntity; + + AutocrafterParentContainer(final AutocrafterBlockEntity blockEntity) { + this.blockEntity = blockEntity; + } + + @Override + public void add(final PatternProvider provider, final Pattern pattern, final int priority) { + // no op + } + + @Override + public void remove(final PatternProvider provider, final Pattern pattern) { + // no op + } + + @Override + public void update(final Pattern pattern, final int priority) { + // no op + } + + @Override + public void taskAdded(final Task task) { + blockEntity.setChanged(); + } + + @Override + public void taskRemoved(final Task task) { + blockEntity.setChanged(); + } + + @Override + public void taskChanged(final Task task) { + blockEntity.setChanged(); + } +} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/InWorldExternalPatternInputSinkKey.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/InWorldExternalPatternInputSinkKey.java index 76eae18c6..15fb5401e 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/InWorldExternalPatternInputSinkKey.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/InWorldExternalPatternInputSinkKey.java @@ -5,8 +5,4 @@ import net.minecraft.world.item.ItemStack; public record InWorldExternalPatternInputSinkKey(String name, ItemStack stack) implements ExternalPatternInputSinkKey { - @Override - public String getName() { - return name; - } } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/TaskSnapshotPersistence.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/TaskSnapshotPersistence.java new file mode 100644 index 000000000..ffa0c11e1 --- /dev/null +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/TaskSnapshotPersistence.java @@ -0,0 +1,339 @@ +package com.refinedmods.refinedstorage.common.autocrafting.autocrafter; + +import com.refinedmods.refinedstorage.api.autocrafting.Ingredient; +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.PatternType; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSink; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskSnapshot; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskState; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.resource.ResourceKey; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; +import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl; +import com.refinedmods.refinedstorage.api.resource.list.ResourceList; +import com.refinedmods.refinedstorage.api.storage.Actor; +import com.refinedmods.refinedstorage.common.api.storage.PlayerActor; +import com.refinedmods.refinedstorage.common.api.support.resource.PlatformResourceKey; +import com.refinedmods.refinedstorage.common.support.resource.ResourceCodecs; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.ItemStack; + +final class TaskSnapshotPersistence { + private TaskSnapshotPersistence() { + } + + static CompoundTag encodeSnapshot(final TaskSnapshot snapshot) { + final CompoundTag tag = new CompoundTag(); + tag.putUUID("id", snapshot.id().id()); + final CompoundTag resourceTag = encodeResource(snapshot.resource()); + tag.put("resource", resourceTag); + tag.putLong("amount", snapshot.amount()); + if (snapshot.actor() instanceof PlayerActor(String name)) { + tag.putString("actor", name); + } + tag.putBoolean("notifyActor", snapshot.notifyActor()); + tag.putLong("startTime", snapshot.startTime()); + tag.put("initialRequirements", encodeResourceList(snapshot.initialRequirements())); + tag.put("internalStorage", encodeResourceList(snapshot.internalStorage())); + tag.putBoolean("cancelled", snapshot.cancelled()); + tag.putString("state", snapshot.state().name()); + final ListTag completedPatterns = new ListTag(); + snapshot.completedPatterns().forEach(pattern -> completedPatterns.add(encodePatternSnapshot(pattern))); + tag.put("completedPatterns", completedPatterns); + tag.put("patterns", encodePatternMap(snapshot.patterns())); + return tag; + } + + private static ListTag encodeResourceList(final ResourceList list) { + final ListTag listTag = new ListTag(); + list.getAll().forEach(resource -> { + final CompoundTag entryTag = encodeResource(resource); + entryTag.putLong("amount", list.get(resource)); + listTag.add(entryTag); + }); + return listTag; + } + + private static CompoundTag encodePattern(final Pattern pattern) { + final CompoundTag tag = new CompoundTag(); + tag.putUUID("id", pattern.id()); + final ListTag ingredients = new ListTag(); + for (final Ingredient ingredient : pattern.ingredients()) { + ingredients.add(encodeIngredient(ingredient)); + } + tag.put("ingredients", ingredients); + final ListTag outputs = new ListTag(); + for (final ResourceAmount output : pattern.outputs()) { + outputs.add(ResourceCodecs.AMOUNT_CODEC.encode(output, NbtOps.INSTANCE, new CompoundTag()).getOrThrow()); + } + tag.put("outputs", outputs); + tag.putString("type", pattern.type().name()); + return tag; + } + + private static CompoundTag encodePatternSnapshot(final TaskSnapshot.PatternSnapshot snapshot) { + final CompoundTag tag = new CompoundTag(); + tag.putBoolean("root", snapshot.root()); + tag.put("pattern", encodePattern(snapshot.pattern())); + tag.put("ingredients", encodeIngredientMap(snapshot.ingredients())); + final boolean internal = snapshot.internalPattern() != null; + tag.putBoolean("internal", internal); + if (snapshot.internalPattern() != null) { + tag.put("internalPattern", encodeInternalPattern(snapshot.internalPattern())); + } else if (snapshot.externalPattern() != null) { + tag.put("externalPattern", encodeExternalPattern(snapshot.externalPattern())); + } + return tag; + } + + private static ListTag encodePatternMap(final Map patterns) { + final ListTag patternMap = new ListTag(); + for (final var pattern : patterns.entrySet()) { + final CompoundTag patternTag = new CompoundTag(); + patternTag.put("k", encodePattern(pattern.getKey())); + patternTag.put("v", encodePatternSnapshot(pattern.getValue())); + patternMap.add(patternTag); + } + return patternMap; + } + + private static CompoundTag encodeInternalPattern(final TaskSnapshot.InternalPatternSnapshot internalPattern) { + final CompoundTag tag = new CompoundTag(); + tag.putLong("originalIterationsRemaining", internalPattern.originalIterationsRemaining()); + tag.putLong("iterationsRemaining", internalPattern.iterationsRemaining()); + return tag; + } + + private static CompoundTag encodeExternalPattern(final TaskSnapshot.ExternalPatternSnapshot externalPattern) { + final CompoundTag tag = new CompoundTag(); + tag.put("expectedOutputs", encodeResourceList(externalPattern.expectedOutputs())); + tag.put("simulatedIterationInputs", encodeResourceList(externalPattern.simulatedIterationInputs())); + tag.putLong("originalIterationsToSendToSink", externalPattern.originalIterationsToSendToSink()); + tag.putLong("iterationsToSendToSink", externalPattern.iterationsToSendToSink()); + tag.putLong("iterationsReceived", externalPattern.iterationsReceived()); + tag.putBoolean("interceptedAnythingSinceLastStep", externalPattern.interceptedAnythingSinceLastStep()); + if (externalPattern.lastSinkResult() != null) { + tag.putString("lastSinkResult", externalPattern.lastSinkResult().name()); + } + final ExternalPatternInputSinkKey lastSinkResultKey = externalPattern.lastSinkResultKey(); + if (lastSinkResultKey instanceof InWorldExternalPatternInputSinkKey(String name, ItemStack stack)) { + tag.putString("lastSinkResultKeyName", name); + tag.put("lastSinkResultKeyStack", ItemStack.CODEC + .encode(stack, NbtOps.INSTANCE, new CompoundTag()).getOrThrow()); + } + return tag; + } + + private static ListTag encodeIngredientMap(final Map> ingredients) { + final ListTag ingredientMap = new ListTag(); + for (final var ingredient : ingredients.entrySet()) { + final CompoundTag ingredientTag = new CompoundTag(); + ingredientTag.putInt("i", ingredient.getKey()); + ingredientTag.put("v", encodeIngredientResources(ingredient)); + ingredientMap.add(ingredientTag); + } + return ingredientMap; + } + + private static ListTag encodeIngredientResources(final Map.Entry> ingredient) { + final ListTag ingredientResources = new ListTag(); + for (final var resourceAndAmount : ingredient.getValue().entrySet()) { + final CompoundTag tag = encodeResource(resourceAndAmount.getKey()); + tag.putLong("amount", resourceAndAmount.getValue()); + ingredientResources.add(tag); + } + return ingredientResources; + } + + private static CompoundTag encodeResource(final ResourceKey resource) { + return (CompoundTag) ResourceCodecs.CODEC.encode((PlatformResourceKey) resource, NbtOps.INSTANCE, + new CompoundTag()).getOrThrow(); + } + + private static CompoundTag encodeIngredient(final Ingredient ingredient) { + final CompoundTag ingredientTag = new CompoundTag(); + ingredientTag.putLong("amount", ingredient.amount()); + final ListTag inputsTag = new ListTag(); + for (final ResourceKey input : ingredient.inputs()) { + inputsTag.add(encodeResource(input)); + } + ingredientTag.put("inputs", inputsTag); + return ingredientTag; + } + + static TaskSnapshot decodeSnapshot(final CompoundTag tag) { + final UUID id = tag.getUUID("id"); + final ResourceKey resource = decodeResource(tag.getCompound("resource")); + final long amount = tag.getLong("amount"); + final Actor actor = tag.contains("actor", Tag.TAG_STRING) + ? new PlayerActor(tag.getString("actor")) + : Actor.EMPTY; + final boolean notifyActor = tag.getBoolean("notifyActor"); + final long startTime = tag.getLong("startTime"); + final ResourceList initialRequirements = decodeResourceList( + tag.getList("initialRequirements", Tag.TAG_COMPOUND) + ); + final ResourceList internalStorage = decodeResourceList( + tag.getList("internalStorage", Tag.TAG_COMPOUND) + ); + final boolean cancelled = tag.getBoolean("cancelled"); + final TaskState state = TaskState.valueOf(tag.getString("state")); + final List completedPatterns = new ArrayList<>(); + for (final Tag completedTag : tag.getList("completedPatterns", Tag.TAG_COMPOUND)) { + completedPatterns.add(decodePatternSnapshot((CompoundTag) completedTag)); + } + final var patterns = decodePatternMap(tag.getList("patterns", Tag.TAG_COMPOUND)); + return new TaskSnapshot( + new TaskId(id), + resource, + amount, + actor, + notifyActor, + startTime, + patterns, + completedPatterns, + initialRequirements, + internalStorage, + state, + cancelled + ); + } + + private static ResourceList decodeResourceList(final ListTag listTag) { + final MutableResourceList resourceList = MutableResourceListImpl.create(); + for (final Tag tag : listTag) { + final CompoundTag entryTag = (CompoundTag) tag; + final ResourceKey resource = decodeResource(entryTag); + final long amount = entryTag.getLong("amount"); + resourceList.add(resource, amount); + } + return resourceList; + } + + private static ResourceKey decodeResource(final CompoundTag resourceTag) { + return ResourceCodecs.CODEC.parse(NbtOps.INSTANCE, resourceTag).result().orElseThrow(); + } + + private static TaskSnapshot.PatternSnapshot decodePatternSnapshot(final CompoundTag tag) { + final boolean root = tag.getBoolean("root"); + final Pattern pattern = decodePattern(tag.getCompound("pattern")); + final var ingredients = decodeIngredientMap(tag.getList("ingredients", Tag.TAG_COMPOUND)); + if (tag.getBoolean("internal")) { + final TaskSnapshot.InternalPatternSnapshot internalPattern = decodeInternalPattern( + tag.getCompound("internalPattern") + ); + return new TaskSnapshot.PatternSnapshot(root, pattern, ingredients, internalPattern, null); + } + final TaskSnapshot.ExternalPatternSnapshot externalPattern = decodeExternalPattern( + tag.getCompound("externalPattern") + ); + return new TaskSnapshot.PatternSnapshot(root, pattern, ingredients, null, externalPattern); + } + + private static Pattern decodePattern(final CompoundTag tag) { + final UUID id = tag.getUUID("id"); + final List ingredients = new ArrayList<>(); + for (final Tag ingredientTag : tag.getList("ingredients", Tag.TAG_COMPOUND)) { + ingredients.add(decodeIngredient((CompoundTag) ingredientTag)); + } + final List outputs = new ArrayList<>(); + for (final Tag outputTag : tag.getList("outputs", Tag.TAG_COMPOUND)) { + outputs.add(ResourceCodecs.AMOUNT_CODEC.parse(NbtOps.INSTANCE, outputTag).result().orElseThrow()); + } + final PatternType type = PatternType.valueOf(tag.getString("type")); + return new Pattern(id, ingredients, outputs, type); + } + + private static Ingredient decodeIngredient(final CompoundTag tag) { + final long amount = tag.getLong("amount"); + final List inputs = new ArrayList<>(); + for (final Tag inputTag : tag.getList("inputs", Tag.TAG_COMPOUND)) { + inputs.add(decodeResource((CompoundTag) inputTag)); + } + return new Ingredient(amount, inputs); + } + + private static TaskSnapshot.InternalPatternSnapshot decodeInternalPattern(final CompoundTag tag) { + final long originalIterationsRemaining = tag.getLong("originalIterationsRemaining"); + final long iterationsRemaining = tag.getLong("iterationsRemaining"); + return new TaskSnapshot.InternalPatternSnapshot(originalIterationsRemaining, iterationsRemaining); + } + + private static TaskSnapshot.ExternalPatternSnapshot decodeExternalPattern(final CompoundTag tag) { + final ResourceList expectedOutputs = decodeResourceList(tag.getList("expectedOutputs", Tag.TAG_COMPOUND)); + final ResourceList simulatedIterationInputs = + decodeResourceList(tag.getList("simulatedIterationInputs", Tag.TAG_COMPOUND)); + final long originalIterationsToSendToSink = tag.getLong("originalIterationsToSendToSink"); + final long iterationsToSendToSink = tag.getLong("iterationsToSendToSink"); + final long iterationsReceived = tag.getLong("iterationsReceived"); + final boolean interceptedAnythingSinceLastStep = tag.getBoolean("interceptedAnythingSinceLastStep"); + final ExternalPatternInputSink.Result lastSinkResult = tag.contains("lastSinkResult", Tag.TAG_STRING) + ? ExternalPatternInputSink.Result.valueOf(tag.getString("lastSinkResult")) + : null; + final ExternalPatternInputSinkKey lastSinkResultKey = tag.contains("lastSinkResultKeyName", Tag.TAG_STRING) + ? decodeSinkResultKey(tag) + : null; + return new TaskSnapshot.ExternalPatternSnapshot( + expectedOutputs, + simulatedIterationInputs, + originalIterationsToSendToSink, + iterationsToSendToSink, + iterationsReceived, + interceptedAnythingSinceLastStep, + lastSinkResult, + lastSinkResultKey + ); + } + + private static InWorldExternalPatternInputSinkKey decodeSinkResultKey(final CompoundTag tag) { + return new InWorldExternalPatternInputSinkKey( + tag.getString("lastSinkResultKeyName"), + ItemStack.CODEC.parse(NbtOps.INSTANCE, tag.getCompound("lastSinkResultKeyStack")).result().orElseThrow() + ); + } + + private static Map decodePatternMap(final ListTag patternMapTag) { + final Map patternMap = new LinkedHashMap<>(); + for (final Tag tag : patternMapTag) { + final CompoundTag entry = (CompoundTag) tag; + final Pattern key = decodePattern(entry.getCompound("k")); + final TaskSnapshot.PatternSnapshot value = decodePatternSnapshot(entry.getCompound("v")); + patternMap.put(key, value); + } + return patternMap; + } + + private static Map> decodeIngredientMap(final ListTag ingredientMapTag) { + final Map> ingredients = new LinkedHashMap<>(); + for (final Tag tag : ingredientMapTag) { + final CompoundTag entry = (CompoundTag) tag; + final int index = entry.getInt("i"); + final Map resources = decodeIngredientResources(entry.getList("v", Tag.TAG_COMPOUND)); + ingredients.put(index, resources); + } + return ingredients; + } + + private static Map decodeIngredientResources(final ListTag ingredientResources) { + final Map resources = new LinkedHashMap<>(); + for (final Tag rawTag : ingredientResources) { + final CompoundTag tag = (CompoundTag) rawTag; + final ResourceKey resource = decodeResource(tag); + final long amount = tag.getLong("amount"); + resources.put(resource, amount); + } + return resources; + } +} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorItemTooltip.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorItemTooltip.java index 6aeaa71ef..850a6f9cf 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorItemTooltip.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorItemTooltip.java @@ -83,8 +83,8 @@ public int getHeight() { public int getWidth(final Font font) { final int resourceWidth = font.width(rendering.getDisplayName(item.resource())); final int errorWidth = item.type() != TaskStatus.ItemType.NORMAL ? font.width(getErrorTooltip(item.type())) : 0; - final int sinkWidth = item.sinkKey() != null - ? (18 + SPACING + font.width(item.sinkKey().getName())) + final int sinkWidth = item.sinkKey() instanceof InWorldExternalPatternInputSinkKey sinkKey + ? (18 + SPACING + font.width(sinkKey.name())) : 0; return Math.max(resourceWidth, Math.max(errorWidth, sinkWidth)); } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/PlatformUtil.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/PlatformUtil.java index b3c138a20..e43c0fbf3 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/PlatformUtil.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/PlatformUtil.java @@ -2,6 +2,7 @@ import javax.annotation.Nullable; +import com.mojang.serialization.Codec; import io.netty.buffer.ByteBuf; import net.minecraft.core.BlockPos; import net.minecraft.network.codec.ByteBufCodecs; @@ -17,6 +18,10 @@ public static > StreamCodec enumStreamCodec(final return ByteBufCodecs.idMapper(id -> id < 0 || id >= values.length ? values[0] : values[id], Enum::ordinal); } + public static > Codec enumCodec(final T[] values) { + return Codec.INT.xmap(i -> values[i], Enum::ordinal); + } + public static void sendBlockUpdateToClient(@Nullable final Level level, final BlockPos pos) { if (level == null) { return; diff --git a/refinedstorage-common/src/main/resources/assets/refinedstorage/lang/en_us.json b/refinedstorage-common/src/main/resources/assets/refinedstorage/lang/en_us.json index 8a5863438..946f20843 100644 --- a/refinedstorage-common/src/main/resources/assets/refinedstorage/lang/en_us.json +++ b/refinedstorage-common/src/main/resources/assets/refinedstorage/lang/en_us.json @@ -579,7 +579,7 @@ "text.autoconfig.refinedstorage.option.upgrade.silkTouchUpgradeEnergyUsage": "Silk Touch Upgrade energy usage", "text.autoconfig.refinedstorage.option.upgrade.silkTouchUpgradeEnergyUsage.tooltip": "The additional energy used by the Silk Touch Upgrade.", "text.autoconfig.refinedstorage.option.upgrade.regulatorUpgradeEnergyUsage": "Regulator Upgrade energy usage", - "text.autoconfig.refinedstorage.option.upgrade.regulatorUpgradeEnergyUsage.tooltip": "The additional energy used by the Regulator Upgrade.", + "text.autoconfig.refinedstorage.option.upgrade.regulatorUpgradeEnergyUsage.tooltip": "The additional energy used per Regulator Upgrade.", "text.autoconfig.refinedstorage.option.upgrade.rangeUpgradeEnergyUsage": "Range Upgrade energy usage", "text.autoconfig.refinedstorage.option.upgrade.rangeUpgradeEnergyUsage.tooltip": "The additional energy used per Range Upgrade.", "text.autoconfig.refinedstorage.option.upgrade.creativeRangeUpgradeEnergyUsage": "Creative Range Upgrade energy usage", 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 641d8fd3d..aad7c468d 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 @@ -25,9 +25,13 @@ import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.Nullable; -// TODO(feat): persistence of tasks +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + // TODO(feat): autocrafter locking support public class PatternProviderNetworkNode extends SimpleNetworkNode implements PatternProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(PatternProviderNetworkNode.class); + private final Pattern[] patterns; private final Set parents = new HashSet<>(); private final List tasks = new CopyOnWriteArrayList<>(); @@ -176,17 +180,29 @@ public void doWork() { final ExternalPatternInputSink outerExternalPatternInputSink = network.getComponent( AutocraftingNetworkComponent.class ); - tasks.removeIf(task -> { - final boolean changed = task.step(storage, outerExternalPatternInputSink, stepBehavior); - final boolean completed = task.getState() == TaskState.COMPLETED; - if (completed) { - cleanupTask(task, storage); - parents.forEach(parent -> parent.taskRemoved(task)); - } else if (changed) { - parents.forEach(parent -> parent.taskChanged(task)); - } - return completed; - }); + tasks.removeIf(task -> stepPattern(task, storage, outerExternalPatternInputSink)); + } + + private boolean stepPattern(final Task task, + final StorageNetworkComponent storage, + final ExternalPatternInputSink outerExternalPatternInputSink) { + boolean changed; + boolean completed; + try { + changed = task.step(storage, outerExternalPatternInputSink, stepBehavior); + completed = task.getState() == TaskState.COMPLETED; + } catch (final Exception e) { + LOGGER.error("Exception while stepping task {} {}, removing task", task.getResource(), task.getAmount(), e); + changed = false; + completed = true; + } + if (completed) { + cleanupTask(task, storage); + parents.forEach(parent -> parent.taskRemoved(task)); + } else if (changed) { + parents.forEach(parent -> parent.taskChanged(task)); + } + return completed; } public int getPriority() { 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 db222325c..da78413ad 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 @@ -6,7 +6,9 @@ import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSink; import com.refinedmods.refinedstorage.api.autocrafting.task.StepBehavior; +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.core.Action; import com.refinedmods.refinedstorage.api.network.Network; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; @@ -23,6 +25,7 @@ import com.refinedmods.refinedstorage.network.test.SetupNetwork; import com.refinedmods.refinedstorage.network.test.nodefactory.PatternProviderNetworkNodeFactory; +import java.util.Collection; import java.util.Optional; import org.junit.jupiter.api.Nested; @@ -275,7 +278,7 @@ void shouldUseProviderAsSinkForExternalPatternInputsWhenSinkIsAttached( ); assertThat(sinkContents.getAll()).isEmpty(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); sut.doWork(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -283,7 +286,7 @@ void shouldUseProviderAsSinkForExternalPatternInputsWhenSinkIsAttached( ); assertThat(sinkContents.getAll()).isEmpty(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactlyInAnyOrder( new ResourceAmount(A, 3) ); @@ -295,7 +298,7 @@ void shouldUseProviderAsSinkForExternalPatternInputsWhenSinkIsAttached( new ResourceAmount(A, 3) ); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); } @Test @@ -316,14 +319,14 @@ void shouldUseProviderAsSinkForExternalPatternInputsWhenSinkIsAttachedAndDoesNot new ResourceAmount(A, 10) ); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); sut.doWork(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 7) ); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactlyInAnyOrder( new ResourceAmount(A, 3) ); @@ -332,7 +335,7 @@ void shouldUseProviderAsSinkForExternalPatternInputsWhenSinkIsAttachedAndDoesNot new ResourceAmount(A, 7) ); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactlyInAnyOrder( new ResourceAmount(A, 3) ); } @@ -354,14 +357,14 @@ void shouldNotUseProviderAsSinkForExternalPatternInputsWhenThereIsNoSinkAttached new ResourceAmount(A, 10) ); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); sut.doWork(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 7) ); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactlyInAnyOrder( new ResourceAmount(A, 3) ); @@ -370,7 +373,7 @@ void shouldNotUseProviderAsSinkForExternalPatternInputsWhenThereIsNoSinkAttached new ResourceAmount(A, 7) ); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactlyInAnyOrder( new ResourceAmount(A, 3) ); } @@ -393,7 +396,7 @@ void shouldInterceptNetworkInsertionsWhenWaitingForExternalPatternOutputs( sut.doWork(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactlyInAnyOrder( new ResourceAmount(A, 3) ); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -402,14 +405,14 @@ void shouldInterceptNetworkInsertionsWhenWaitingForExternalPatternOutputs( sut.doWork(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).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()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 7), new ResourceAmount(B, 3) @@ -417,7 +420,7 @@ void shouldInterceptNetworkInsertionsWhenWaitingForExternalPatternOutputs( storage.insert(B, 4, Action.EXECUTE, Actor.EMPTY); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 7), new ResourceAmount(B, 7) @@ -526,7 +529,7 @@ void shouldNotifyStatusListenerWhenTaskHasChanged( sut.doWork(); taskShouldBeMarkedAsChangedOnce(listener); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactlyInAnyOrder( new ResourceAmount(A, 2) ); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -536,7 +539,7 @@ void shouldNotifyStatusListenerWhenTaskHasChanged( sut.doWork(); taskShouldBeMarkedAsChangedOnce(listener); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactlyInAnyOrder( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactlyInAnyOrder( new ResourceAmount(A, 1) ); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -546,7 +549,7 @@ void shouldNotifyStatusListenerWhenTaskHasChanged( sut.doWork(); taskShouldBeMarkedAsChangedOnce(listener); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 8) ); @@ -554,7 +557,7 @@ void shouldNotifyStatusListenerWhenTaskHasChanged( sut.doWork(); taskShouldNotBeMarkedAsChanged(listener); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 8) ); @@ -562,7 +565,7 @@ void shouldNotifyStatusListenerWhenTaskHasChanged( storage.insert(B, 1, Action.EXECUTE, Actor.EMPTY); taskShouldNotBeMarkedAsChanged(listener); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 8), new ResourceAmount(B, 1) @@ -571,7 +574,7 @@ void shouldNotifyStatusListenerWhenTaskHasChanged( sut.doWork(); taskShouldBeMarkedAsChangedOnce(listener); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 8), new ResourceAmount(B, 1) @@ -580,7 +583,7 @@ void shouldNotifyStatusListenerWhenTaskHasChanged( sut.doWork(); taskShouldNotBeMarkedAsChanged(listener); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 8), new ResourceAmount(B, 1) @@ -589,7 +592,7 @@ void shouldNotifyStatusListenerWhenTaskHasChanged( storage.insert(B, 1, Action.EXECUTE, Actor.EMPTY); taskShouldNotBeMarkedAsChanged(listener); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 8), new ResourceAmount(B, 2) @@ -610,6 +613,10 @@ private static void taskShouldNotBeMarkedAsChanged(final TaskStatusListener list verify(listener, never()).taskStatusChanged(any()); } + private static Collection copyInternalStorage(final Task task) { + return ((TaskImpl) task).createSnapshot().copyInternalStorage().copyState(); + } + @Nested @SetupNetwork(id = "other") class NetworkChangeTest { @@ -642,7 +649,7 @@ void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( sut.doWork(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactly( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactly( new ResourceAmount(C, 3) ); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( @@ -651,7 +658,7 @@ void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( sut.doWork(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactly( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactly( new ResourceAmount(C, 2) ); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -660,7 +667,7 @@ void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( sut.doWork(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).containsExactly( + assertThat(copyInternalStorage(sut.getTasks().getFirst())).containsExactly( new ResourceAmount(C, 1) ); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -669,7 +676,7 @@ void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( sut.doWork(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(C, 7) ); @@ -679,7 +686,7 @@ void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( storage.insert(B, 3, Action.EXECUTE, Actor.EMPTY); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(C, 7), new ResourceAmount(B, 3) @@ -688,7 +695,7 @@ void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( otherStorage.insert(B, 3, Action.EXECUTE, Actor.EMPTY); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + assertThat(copyInternalStorage(sut.getTasks().getFirst())) .usingRecursiveFieldByFieldElementComparator() .containsExactly(new ResourceAmount(B, 3)); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -698,21 +705,21 @@ void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( assertThat(otherStorage.getAll()).isEmpty(); sut.doWork(); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + assertThat(copyInternalStorage(sut.getTasks().getFirst())) .usingRecursiveFieldByFieldElementComparator() .containsExactly(new ResourceAmount(B, 2)); sut.doWork(); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + assertThat(copyInternalStorage(sut.getTasks().getFirst())) .usingRecursiveFieldByFieldElementComparator() .containsExactly(new ResourceAmount(B, 1)); sut.doWork(); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); storage.insert(A, 3, Action.EXECUTE, Actor.EMPTY); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 3), new ResourceAmount(C, 7), @@ -722,7 +729,7 @@ void shouldInterceptInsertionsOnNewNetworkWhenNetworkChanges( otherStorage.insert(A, 3, Action.EXECUTE, Actor.EMPTY); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(A, 3), new ResourceAmount(C, 7), @@ -814,14 +821,14 @@ void shouldNotUseProviderAsSinkForExternalChildPatternWhenProviderIsRemovedAndRo // Act & assert assertThat(autocrafting.startTask(A, 1, Actor.EMPTY, false).join()).isPresent(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()).isEmpty(); + assertThat(copyInternalStorage(sut.getTasks().getFirst())).isEmpty(); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( new ResourceAmount(C, 10) ); sut.doWork(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + assertThat(copyInternalStorage(sut.getTasks().getFirst())) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder(new ResourceAmount(C, 1)); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( @@ -832,7 +839,7 @@ void shouldNotUseProviderAsSinkForExternalChildPatternWhenProviderIsRemovedAndRo sut.doWork(); assertThat(sut.getTasks()).hasSize(1); - assertThat(sut.getTasks().getFirst().copyInternalStorageState()) + assertThat(copyInternalStorage(sut.getTasks().getFirst())) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrder(new ResourceAmount(C, 1)); assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(