diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatus.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatus.java index 25d96e8d9..4a22851c7 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatus.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatus.java @@ -8,21 +8,17 @@ import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.10") -public record TaskStatus(TaskInfo info, float percentageCompleted, List items) { +public record TaskStatus(TaskInfo info, double percentageCompleted, List items) { public record TaskInfo(TaskId id, ResourceKey resource, long amount, long startTime) { } public record Item( - ItemType type, ResourceKey resource, - // what is stored internally? + ItemType type, long stored, - // what is currently processing? - long processing, // (originalIterationsToSendToSink - iterationsToSendToSink)*input - // what are we still going to process? - long scheduled, // iterationsToSendToSink*input - // what do we need to still craft? - long crafting // iterationsRemaining*output + long processing, + long scheduled, + long crafting ) { } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatusBuilder.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatusBuilder.java new file mode 100644 index 000000000..50d5b4cd2 --- /dev/null +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatusBuilder.java @@ -0,0 +1,68 @@ +package com.refinedmods.refinedstorage.api.autocrafting.status; + +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; +import com.refinedmods.refinedstorage.api.core.CoreValidations; +import com.refinedmods.refinedstorage.api.resource.ResourceKey; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class TaskStatusBuilder { + private final TaskStatus.TaskInfo info; + private final Map items = new LinkedHashMap<>(); + + public TaskStatusBuilder(final TaskId id, final ResourceKey resource, final long amount, final long startTime) { + this.info = new TaskStatus.TaskInfo(id, resource, amount, startTime); + } + + public TaskStatusBuilder stored(final ResourceKey resource, final long stored) { + CoreValidations.validateLargerThanZero(stored, "Stored"); + get(resource).stored += stored; + return this; + } + + public TaskStatusBuilder processing(final ResourceKey resource, final long processing) { + CoreValidations.validateLargerThanZero(processing, "Processing"); + get(resource).processing += processing; + return this; + } + + public TaskStatusBuilder scheduled(final ResourceKey resource, final long scheduled) { + CoreValidations.validateLargerThanZero(scheduled, "Crafting"); + get(resource).scheduled += scheduled; + return this; + } + + public TaskStatusBuilder crafting(final ResourceKey resource, final long crafting) { + CoreValidations.validateLargerThanZero(crafting, "Crafting"); + get(resource).crafting += crafting; + return this; + } + + private MutableItem get(final ResourceKey resource) { + return items.computeIfAbsent(resource, key -> new MutableItem(TaskStatus.ItemType.NORMAL)); + } + + public TaskStatus build(final double percentageCompleted) { + return new TaskStatus(info, percentageCompleted, items.entrySet().stream().map(entry -> new TaskStatus.Item( + entry.getKey(), + entry.getValue().type, + entry.getValue().stored, + entry.getValue().processing, + entry.getValue().scheduled, + entry.getValue().crafting + )).toList()); + } + + private static class MutableItem { + private final TaskStatus.ItemType type; + private long stored; + private long processing; + private long scheduled; + private long crafting; + + private MutableItem(final TaskStatus.ItemType type) { + this.type = type; + } + } +} 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 c2893f9bc..eb7afafc5 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 @@ -1,6 +1,7 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusBuilder; import com.refinedmods.refinedstorage.api.core.Action; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; @@ -38,6 +39,12 @@ abstract boolean step(MutableResourceList internalStorage, abstract RootStorageListener.InterceptResult interceptInsertion(ResourceKey resource, long amount); + abstract void appendStatus(TaskStatusBuilder builder); + + abstract long getWeight(); + + abstract double getPercentageCompleted(); + protected final boolean extractAll(final ResourceList inputs, final MutableResourceList internalStorage, final 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 e64068eb8..ad5c7c6fd 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 @@ -1,7 +1,9 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +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; @@ -16,14 +18,19 @@ class ExternalTaskPattern extends AbstractTaskPattern { private static final Logger LOGGER = LoggerFactory.getLogger(ExternalTaskPattern.class); private final MutableResourceList expectedOutputs = MutableResourceListImpl.create(); + private final ResourceList simulatedIterationInputs; + private final long originalIterationsToSendToSink; private long iterationsToSendToSink; + private long iterationsReceived; ExternalTaskPattern(final Pattern pattern, final TaskPlan.PatternPlan plan) { super(pattern, plan); - this.iterationsToSendToSink = plan.iterations(); + this.originalIterationsToSendToSink = plan.iterations(); pattern.outputs().forEach( output -> expectedOutputs.add(output.resource(), output.amount() * plan.iterations()) ); + this.iterationsToSendToSink = plan.iterations(); + this.simulatedIterationInputs = calculateIterationInputs(Action.SIMULATE); } @Override @@ -52,10 +59,51 @@ RootStorageListener.InterceptResult interceptInsertion(final ResourceKey resourc } final long reserved = Math.min(needed, amount); expectedOutputs.remove(resource, reserved); + updateIterationsReceived(); final long intercepted = root ? 0 : reserved; return new RootStorageListener.InterceptResult(reserved, intercepted); } + private void updateIterationsReceived() { + long result = originalIterationsToSendToSink; + for (final ResourceAmount output : pattern.outputs()) { + final long expected = output.amount() * originalIterationsToSendToSink; + final long stillNeeded = expectedOutputs.get(output.resource()); + final long receivedOutputs = expected - stillNeeded; + final long receivedOutputIterations = receivedOutputs / output.amount(); + if (result > receivedOutputIterations) { + result = receivedOutputIterations; + } + } + this.iterationsReceived = result; + } + + @Override + void appendStatus(final TaskStatusBuilder builder) { + if (iterationsToSendToSink > 0) { + for (final ResourceAmount output : pattern.outputs()) { + builder.scheduled(output.resource(), output.amount() * iterationsToSendToSink); + } + } + final long iterationsSentToSink = originalIterationsToSendToSink - iterationsToSendToSink; + final long iterationsProcessing = iterationsSentToSink - iterationsReceived; + if (iterationsProcessing > 0) { + for (final ResourceKey input : simulatedIterationInputs.getAll()) { + builder.processing(input, simulatedIterationInputs.get(input) * iterationsProcessing); + } + } + } + + @Override + long getWeight() { + return iterationsToSendToSink; + } + + @Override + double getPercentageCompleted() { + return iterationsReceived / (double) originalIterationsToSendToSink; + } + private boolean acceptsIterationInputs(final MutableResourceList internalStorage, final ExternalPatternInputSink externalPatternInputSink) { final ResourceList iterationInputsSimulated = calculateIterationInputs(Action.SIMULATE); 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 3b7fe0bce..5c9b7f09c 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 @@ -1,6 +1,7 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +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; @@ -16,10 +17,12 @@ class InternalTaskPattern extends AbstractTaskPattern { private static final Logger LOGGER = LoggerFactory.getLogger(InternalTaskPattern.class); + private final long originalIterationsRemaining; private long iterationsRemaining; InternalTaskPattern(final Pattern pattern, final TaskPlan.PatternPlan plan) { super(pattern, plan); + this.originalIterationsRemaining = plan.iterations(); this.iterationsRemaining = plan.iterations(); } @@ -60,6 +63,27 @@ RootStorageListener.InterceptResult interceptInsertion(final ResourceKey resourc return RootStorageListener.InterceptResult.EMPTY; } + @Override + void appendStatus(final TaskStatusBuilder builder) { + if (iterationsRemaining == 0) { + return; + } + for (final ResourceAmount output : pattern.outputs()) { + builder.crafting(output.resource(), output.amount() * iterationsRemaining); + } + } + + @Override + long getWeight() { + return originalIterationsRemaining; + } + + @Override + double getPercentageCompleted() { + final double iterationsCompleted = originalIterationsRemaining - iterationsRemaining; + return iterationsCompleted / originalIterationsRemaining; + } + protected boolean useIteration() { iterationsRemaining--; LOGGER.debug("Stepped {} with {} iterations remaining", pattern, iterationsRemaining); 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 dd2554a69..e7fd26bc4 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,5 +1,6 @@ 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.storage.root.RootStorage; import com.refinedmods.refinedstorage.api.storage.root.RootStorageListener; @@ -13,10 +14,12 @@ public interface Task extends RootStorageListener { TaskId getId(); TaskState getState(); - + Collection copyInternalStorageState(); void step(RootStorage rootStorage, ExternalPatternInputSink externalPatternInputSink, StepBehavior stepBehavior); void cancel(); + + TaskStatus getStatus(); } 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 62585c9ff..ca86a680b 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 @@ -1,6 +1,8 @@ package com.refinedmods.refinedstorage.api.autocrafting.task; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +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; @@ -9,9 +11,11 @@ import com.refinedmods.refinedstorage.api.storage.Actor; 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; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -27,11 +31,13 @@ public class TaskImpl implements Task { private final long amount; private final long startTime = System.currentTimeMillis(); private final Map patterns; + private final List completedPatterns = new ArrayList<>(); private final MutableResourceList initialRequirements = MutableResourceListImpl.create(); - private final MutableResourceList internalStorage = MutableResourceListImpl.create(); + private final MutableResourceList internalStorage; private TaskState state = TaskState.READY; - private TaskImpl(final TaskPlan plan) { + TaskImpl(final TaskPlan plan, final MutableResourceList internalStorage) { + this.internalStorage = internalStorage; this.resource = plan.resource(); this.amount = plan.amount(); this.patterns = plan.patterns().entrySet().stream().collect(Collectors.toMap( @@ -44,7 +50,7 @@ private TaskImpl(final TaskPlan plan) { } public static Task fromPlan(final TaskPlan plan) { - return new TaskImpl(plan); + return new TaskImpl(plan, MutableResourceListImpl.create()); } private static AbstractTaskPattern createTaskPattern(final Pattern pattern, @@ -87,6 +93,25 @@ public void cancel() { state = TaskState.RETURNING_INTERNAL_STORAGE; } + @Override + public TaskStatus getStatus() { + final TaskStatusBuilder builder = new TaskStatusBuilder(id, resource, amount, startTime); + double totalWeightedCompleted = 0; + double totalWeight = 0; + for (final AbstractTaskPattern pattern : patterns.values()) { + pattern.appendStatus(builder); + totalWeightedCompleted += pattern.getPercentageCompleted() * pattern.getWeight(); + totalWeight += pattern.getWeight(); + } + for (final AbstractTaskPattern pattern : completedPatterns) { + totalWeightedCompleted += pattern.getWeight(); + totalWeight += pattern.getWeight(); + } + internalStorage.getAll().forEach(internalResource -> + builder.stored(internalResource, internalStorage.get(internalResource))); + return builder.build(totalWeightedCompleted / totalWeight); + } + private void startTask(final RootStorage rootStorage) { updateState(TaskState.EXTRACTING_INITIAL_RESOURCES); extractInitialResourcesAndTryStartRunningTask(rootStorage); @@ -125,6 +150,7 @@ private boolean stepPattern(final RootStorage rootStorage, final boolean completed = pattern.getValue().step(internalStorage, rootStorage, externalPatternInputSink); if (completed) { LOGGER.debug("{} completed", pattern.getKey()); + completedPatterns.add(pattern.getValue()); return true; } } @@ -144,17 +170,17 @@ public Collection copyInternalStorageState() { private boolean extractInitialResources(final RootStorage rootStorage) { boolean extractedAll = true; - final Set resources = new HashSet<>(initialRequirements.getAll()); - for (final ResourceKey resource : resources) { - final long needed = initialRequirements.get(resource); - final long extracted = rootStorage.extract(resource, needed, Action.EXECUTE, Actor.EMPTY); - LOGGER.debug("Extracted {}x {} from storage", extracted, resource); + final Set initialRequirementResources = new HashSet<>(initialRequirements.getAll()); + for (final ResourceKey initialRequirementResource : initialRequirementResources) { + final long needed = initialRequirements.get(initialRequirementResource); + final long extracted = rootStorage.extract(initialRequirementResource, needed, Action.EXECUTE, Actor.EMPTY); + LOGGER.debug("Extracted {}x {} from storage", extracted, initialRequirementResource); if (extracted != needed) { extractedAll = false; } if (extracted > 0) { - initialRequirements.remove(resource, extracted); - internalStorage.add(resource, extracted); + initialRequirements.remove(initialRequirementResource, extracted); + internalStorage.add(initialRequirementResource, extracted); } } return extractedAll; @@ -162,34 +188,36 @@ private boolean extractInitialResources(final RootStorage rootStorage) { private boolean returnInternalStorage(final RootStorage rootStorage) { boolean returnedAll = true; - final Set resources = new HashSet<>(internalStorage.getAll()); - for (final ResourceKey resource : resources) { - final long amount = internalStorage.get(resource); - final long inserted = rootStorage.insert(resource, amount, Action.EXECUTE, Actor.EMPTY); - LOGGER.debug("Returned {}x {} into storage", inserted, resource); - if (inserted != amount) { + final Set internalResources = new HashSet<>(internalStorage.getAll()); + for (final ResourceKey internalResource : internalResources) { + final long internalAmount = internalStorage.get(internalResource); + final long inserted = rootStorage.insert(internalResource, internalAmount, Action.EXECUTE, Actor.EMPTY); + LOGGER.debug("Returned {}x {} into storage", inserted, internalResource); + if (inserted != internalAmount) { returnedAll = false; } if (inserted > 0) { - internalStorage.remove(resource, inserted); + internalStorage.remove(internalResource, inserted); } } return returnedAll; } @Override - public InterceptResult beforeInsert(final ResourceKey resource, final long amount, final Actor actor) { + public InterceptResult beforeInsert(final ResourceKey insertedResource, + final long insertedAmount, + final Actor actor) { long reserved = 0; long intercepted = 0; for (final AbstractTaskPattern pattern : patterns.values()) { - final long remainder = amount - reserved; - final InterceptResult result = pattern.interceptInsertion(resource, remainder); + final long remainder = insertedAmount - reserved; + final InterceptResult result = pattern.interceptInsertion(insertedResource, remainder); if (result.intercepted() > 0) { - internalStorage.add(resource, result.intercepted()); + internalStorage.add(insertedResource, result.intercepted()); } reserved += result.reserved(); intercepted += result.intercepted(); - if (reserved == amount) { + if (reserved == insertedAmount) { return new InterceptResult(reserved, intercepted); } } 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 448e7bcb3..4efd3bd67 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 @@ -5,15 +5,18 @@ 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.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.MutableResourceListImpl; import com.refinedmods.refinedstorage.api.storage.Actor; import com.refinedmods.refinedstorage.api.storage.StorageImpl; import com.refinedmods.refinedstorage.api.storage.limited.LimitedStorageImpl; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; import com.refinedmods.refinedstorage.api.storage.root.RootStorageImpl; +import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration; import org.junit.jupiter.api.Test; import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingUtil.patterns; @@ -26,6 +29,7 @@ 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.STICKS_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; @@ -47,6 +51,9 @@ import static org.assertj.core.api.Assertions.assertThat; class TaskImplTest { + private static final RecursiveComparisonConfiguration STATUS_CONFIG = RecursiveComparisonConfiguration.builder() + .withIgnoredFields("info.startTime") + .build(); private static final ExternalPatternInputSink EMPTY_SINK = (pattern, resources, action) -> false; @Test @@ -773,12 +780,181 @@ public int getSteps(final Pattern pattern) { assertThat(task.copyInternalStorageState()).isEmpty(); } + @Test + void shouldReportStatusCorrectly() { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(OAK_LOG, 10), + new ResourceAmount(IRON_ORE, 10) + ); + final PatternRepository patterns = patterns( + OAK_PLANKS_PATTERN, + STICKS_PATTERN, + IRON_INGOT_PATTERN, + IRON_PICKAXE_PATTERN + ); + final ExternalPatternInputSinkBuilder sinkBuilder = externalPatternInputSink(); + final ExternalPatternInputSinkBuilder.Sink ironOreSink = sinkBuilder.storageSink(IRON_INGOT_PATTERN); + final ExternalPatternInputSink sink = sinkBuilder.build(); + final Task task = getRunningTask(storage, patterns, EMPTY_SINK, IRON_PICKAXE, 1); + + // Act & assert + task.step(storage, sink, StepBehavior.DEFAULT); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( + new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) + .crafting(IRON_PICKAXE, 1) + .scheduled(IRON_INGOT, 2) + .processing(IRON_ORE, 1) + .crafting(STICKS, 4) + .stored(OAK_PLANKS, 4) + .stored(IRON_ORE, 2) + .build(0.2)); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 2), + new ResourceAmount(OAK_PLANKS, 4) + ); + assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(IRON_ORE, 1) + ); + + task.step(storage, sink, StepBehavior.DEFAULT); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( + new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) + .crafting(IRON_PICKAXE, 1) + .scheduled(IRON_INGOT, 1) + .processing(IRON_ORE, 2) + .stored(OAK_PLANKS, 2) + .stored(IRON_ORE, 1) + .stored(STICKS, 4) + .build(0.5)); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(IRON_ORE, 1), + new ResourceAmount(OAK_PLANKS, 2), + new ResourceAmount(STICKS, 4) + ); + assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(IRON_ORE, 2) + ); + + task.step(storage, sink, StepBehavior.DEFAULT); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( + new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) + .crafting(IRON_PICKAXE, 1) + .processing(IRON_ORE, 3) + .stored(OAK_PLANKS, 2) + .stored(STICKS, 4) + .build(0.6666666666666666)); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(OAK_PLANKS, 2), + new ResourceAmount(STICKS, 4) + ); + assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(IRON_ORE, 3) + ); + + storage.insert(IRON_INGOT, 2, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( + new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) + .crafting(IRON_PICKAXE, 1) + .processing(IRON_ORE, 1) + .stored(OAK_PLANKS, 2) + .stored(STICKS, 4) + .stored(IRON_INGOT, 2) + .build(0.6666666666666666)); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(OAK_PLANKS, 2), + new ResourceAmount(STICKS, 4), + new ResourceAmount(IRON_INGOT, 2) + ); + assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(IRON_ORE, 3) + ); + + storage.insert(IRON_INGOT, 1, Action.EXECUTE, Actor.EMPTY); + assertThat(task.getState()).isEqualTo(TaskState.RUNNING); + assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( + new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) + .crafting(IRON_PICKAXE, 1) + .stored(OAK_PLANKS, 2) + .stored(STICKS, 4) + .stored(IRON_INGOT, 3) + .build(0.6666666666666666)); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(OAK_PLANKS, 2), + new ResourceAmount(STICKS, 4), + new ResourceAmount(IRON_INGOT, 3) + ); + assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(IRON_ORE, 3) + ); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(OAK_LOG, 9), + new ResourceAmount(IRON_ORE, 7) + ); + + task.step(storage, sink, StepBehavior.DEFAULT); + assertThat(task.getState()).isEqualTo(TaskState.RETURNING_INTERNAL_STORAGE); + assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( + new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) + .stored(OAK_PLANKS, 2) + .stored(STICKS, 2) + .build(1.0)); + assertThat(task.copyInternalStorageState()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + new ResourceAmount(OAK_PLANKS, 2), + new ResourceAmount(STICKS, 2) + ); + assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(IRON_ORE, 3) + ); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(OAK_LOG, 9), + new ResourceAmount(IRON_ORE, 7), + new ResourceAmount(IRON_PICKAXE, 1) + ); + + task.step(storage, sink, StepBehavior.DEFAULT); + assertThat(task.getState()).isEqualTo(TaskState.COMPLETED); + assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( + new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0).build(1.0) + ); + assertThat(task.copyInternalStorageState()).isEmpty(); + assertThat(ironOreSink.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly( + new ResourceAmount(IRON_ORE, 3) + ); + assertThat(storage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder( + new ResourceAmount(OAK_LOG, 9), + new ResourceAmount(IRON_ORE, 7), + new ResourceAmount(IRON_PICKAXE, 1), + new ResourceAmount(OAK_PLANKS, 2), + new ResourceAmount(STICKS, 2) + ); + } + private static Task getTask(final RootStorage storage, final PatternRepository patterns, final ResourceKey resource, final long amount) { final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); - final Task task = calculatePlan(sut, resource, amount).map(TaskImpl::fromPlan).orElseThrow(); + final Task task = calculatePlan(sut, resource, amount).map(plan -> new TaskImpl( + plan, + MutableResourceListImpl.orderPreserving() + )).orElseThrow(); storage.addListener(task); return task; } 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 59af2e604..334deb681 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 @@ -90,7 +90,7 @@ List getTasksView() { return tasksView; } - float getPercentageCompleted(final TaskId taskId) { + double getPercentageCompleted(final TaskId taskId) { final TaskStatus status = statusByTaskId.get(taskId); return status == null ? 0 : status.percentageCompleted(); } 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 2117eb5d9..4d067adac 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 @@ -32,7 +32,7 @@ public final class AutocraftingMonitorStreamCodecs { public static final StreamCodec STATUS_STREAM_CODEC = StreamCodec.composite( INFO_STREAM_CODEC, TaskStatus::info, - ByteBufCodecs.FLOAT, TaskStatus::percentageCompleted, + ByteBufCodecs.DOUBLE, TaskStatus::percentageCompleted, ByteBufCodecs.collection(ArrayList::new, STATUS_ITEM_STREAM_CODEC), TaskStatus::items, TaskStatus::new ); @@ -48,8 +48,8 @@ private static class StatusItemStreamCodec implements StreamCodec