From 6bce70848c5b4a3d8c159f66aa9e87f3bede1644 Mon Sep 17 00:00:00 2001 From: raoulvdberge Date: Fri, 27 Dec 2024 17:51:04 +0100 Subject: [PATCH 1/6] fix: amount in amount screen resetting when resizing --- CHANGELOG.md | 4 ++++ .../preview/AutocraftingPreviewScreen.java | 11 ++++++++++- .../common/support/amount/AbstractAmountScreen.java | 8 ++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fa590d4d..3aeb146a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Autocrafting now handles multiple patterns with the same output correctly by trying to use the pattern with the highest priority first. If there are missing resources, lower priority patterns are checked. +### Fixed + +- Fixed amount in amount screens resetting when resizing the screen. + ## [2.0.0-milestone.4.11] - 2024-12-08 ### Added diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java index 9e83b6bad..4423770be 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java @@ -114,6 +114,7 @@ public AutocraftingPreviewScreen(final AutocraftingPreviewContainerMenu menu, @Override protected void init() { + final boolean wasAlreadyInitialized = amountField != null; super.init(); previewItemsScrollbar = new ScrollbarWidget( leftPos + 235, @@ -128,7 +129,9 @@ protected void init() { if (confirmButton != null) { setStartDisabled(); } - getMenu().loadCurrentRequest(); + if (!wasAlreadyInitialized) { + getMenu().loadCurrentRequest(); + } getExclusionZones().add(new Rect2i( leftPos - REQUESTS_WIDTH + 4, topPos, @@ -424,6 +427,9 @@ protected void onAmountFieldChanged() { } private void setPending() { + if (confirmButton == null) { + return; + } confirmButton.active = false; confirmButton.setError(false); confirmButton.setTooltip(null); @@ -431,6 +437,9 @@ private void setPending() { } private void setStartDisabled() { + if (confirmButton == null) { + return; + } confirmButton.active = false; confirmButton.setError(false); confirmButton.setTooltip(null); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/amount/AbstractAmountScreen.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/amount/AbstractAmountScreen.java index 66b5c5e07..4a8baab44 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/amount/AbstractAmountScreen.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/amount/AbstractAmountScreen.java @@ -117,6 +117,7 @@ private Button addCancelButton(final int x, final int y) { private void addAmountField() { final Vector3f pos = configuration.getAmountFieldPosition(); + final String originalValue = amountField != null ? amountField.getValue() : null; amountField = new EditBox( font, leftPos + (int) pos.x(), @@ -126,14 +127,17 @@ private void addAmountField() { Component.empty() ); amountField.setBordered(false); - if (configuration.getInitialAmount() != null) { + amountField.setTextColor(0xFFFFFF); + if (originalValue != null) { + amountField.setValue(originalValue); + onAmountFieldChanged(); + } else if (configuration.getInitialAmount() != null) { updateAmount(configuration.getInitialAmount()); } amountField.setVisible(true); amountField.setCanLoseFocus(this instanceof AlternativesScreen); amountField.setFocused(true); amountField.setResponder(value -> onAmountFieldChanged()); - amountField.setTextColor(0xFFFFFF); setFocused(amountField); addRenderableWidget(amountField); From 7d0e09bb7131155b9457b83ec364723fcbef24af Mon Sep 17 00:00:00 2001 From: raoulvdberge Date: Fri, 27 Dec 2024 18:45:25 +0100 Subject: [PATCH 2/6] fix: detect cycles in patterns --- .../calculation/CraftingTree.java | 21 ++++- .../PatternCycleDetectedException.java | 16 ++++ .../api/autocrafting/preview/Preview.java | 4 +- .../autocrafting/preview/PreviewBuilder.java | 13 ++- .../PreviewCraftingCalculatorListener.java | 15 ++- .../api/autocrafting/preview/PreviewType.java | 3 +- .../api/autocrafting/PatternImpl.java | 5 + .../preview/PreviewBuilderTest.java | 22 ++++- .../api/autocrafting/preview/PreviewTest.java | 67 ++++++++----- .../preview/AutocraftingPreviewScreen.java | 93 ++++++++++++++++++- .../AutocraftingPreviewResponsePacket.java | 2 + .../assets/refinedstorage/lang/en_us.json | 3 + .../AutocraftingNetworkComponentImpl.java | 4 +- .../AutocraftingNetworkComponentImplTest.java | 3 +- 14 files changed, 227 insertions(+), 44 deletions(-) create mode 100644 refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/PatternCycleDetectedException.java diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingTree.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingTree.java index 2479adf2f..fed0b0d20 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingTree.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingTree.java @@ -5,7 +5,9 @@ import com.refinedmods.refinedstorage.api.autocrafting.PatternRepository; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.annotation.Nullable; import static java.util.Objects.requireNonNull; @@ -15,18 +17,21 @@ class CraftingTree { private final Amount amount; private final PatternRepository patternRepository; private final CraftingCalculatorListener listener; + private final Set activePatterns; private CraftingState craftingState; private CraftingTree(final Pattern pattern, final CraftingState craftingState, final Amount amount, final PatternRepository patternRepository, - final CraftingCalculatorListener listener) { + final CraftingCalculatorListener listener, + final Set activePatterns) { this.pattern = pattern; this.craftingState = craftingState; this.amount = amount; this.patternRepository = patternRepository; this.listener = listener; + this.activePatterns = activePatterns; } static CraftingTree root(final Pattern pattern, @@ -35,19 +40,23 @@ static CraftingTree root(final Pattern pattern, final PatternRepository patternRepository, final CraftingCalculatorListener listener) { final CraftingState craftingState = CraftingState.of(rootStorage); - return new CraftingTree<>(pattern, craftingState, amount, patternRepository, listener); + return new CraftingTree<>(pattern, craftingState, amount, patternRepository, listener, new HashSet<>()); } static CraftingTree child(final Pattern pattern, final CraftingState parentState, final Amount amount, final PatternRepository patternRepository, - final CraftingCalculatorListener listener) { + final CraftingCalculatorListener listener, + final Set activePatterns) { return new CraftingTree<>(pattern, parentState.copy(), amount, patternRepository, - listener.childCalculationStarted()); + listener.childCalculationStarted(), activePatterns); } CalculationResult calculate() { + if (!activePatterns.add(pattern)) { + throw new PatternCycleDetectedException(pattern); + } CalculationResult result = CalculationResult.SUCCESS; for (final Ingredient ingredient : pattern.getIngredients()) { if (ingredient.isEmpty()) { @@ -60,6 +69,7 @@ CalculationResult calculate() { } } craftingState.addOutputsToInternalStorage(pattern, amount); + activePatterns.remove(pattern); return result; } @@ -147,7 +157,8 @@ private ChildCalculationResult calculateChild(final long remaining, craftingState, childAmount, patternRepository, - listener + listener, + activePatterns ); final CalculationResult childResult = childTree.calculate(); if (childResult == CalculationResult.MISSING_RESOURCES) { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/PatternCycleDetectedException.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/PatternCycleDetectedException.java new file mode 100644 index 000000000..a2ed6376b --- /dev/null +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/PatternCycleDetectedException.java @@ -0,0 +1,16 @@ +package com.refinedmods.refinedstorage.api.autocrafting.calculation; + +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; + +public class PatternCycleDetectedException extends RuntimeException { + private final Pattern pattern; + + PatternCycleDetectedException(final Pattern pattern) { + super("Pattern loop detected in pattern " + pattern); + this.pattern = pattern; + } + + public Pattern getPattern() { + return this.pattern; + } +} diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/Preview.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/Preview.java index 1ca73b92b..ad493b6d8 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/Preview.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/Preview.java @@ -1,9 +1,11 @@ package com.refinedmods.refinedstorage.api.autocrafting.preview; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; + import java.util.List; import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.9") -public record Preview(PreviewType type, List items) { +public record Preview(PreviewType type, List items, List outputsOfPatternWithCycle) { } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewBuilder.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewBuilder.java index 1c07fde3c..ce894747f 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewBuilder.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewBuilder.java @@ -1,15 +1,21 @@ package com.refinedmods.refinedstorage.api.autocrafting.preview; +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.core.CoreValidations; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.api.resource.ResourceKey; +import java.util.Collections; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; public class PreviewBuilder { private final PreviewType type; private final Map items = new LinkedHashMap<>(); + private List outputsOfPatternWithCycle = Collections.emptyList(); + private PreviewBuilder(final PreviewType type) { this.type = type; } @@ -22,6 +28,11 @@ private MutablePreviewItem get(final ResourceKey resource) { return items.computeIfAbsent(resource, key -> new MutablePreviewItem()); } + public PreviewBuilder withPatternWithCycle(final Pattern pattern) { + this.outputsOfPatternWithCycle = pattern.getOutputs(); + return this; + } + public PreviewBuilder addAvailable(final ResourceKey resource, final long amount) { CoreValidations.validateLargerThanZero(amount, "Available amount must be larger than 0"); get(resource).available += amount; @@ -44,7 +55,7 @@ public Preview build() { return new Preview(type, items.entrySet() .stream() .map(entry -> entry.getValue().toPreviewItem(entry.getKey())) - .toList()); + .toList(), outputsOfPatternWithCycle); } private static class MutablePreviewItem { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java index f57997532..1722f79e9 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java @@ -1,10 +1,13 @@ package com.refinedmods.refinedstorage.api.autocrafting.preview; +import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculator; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculatorListener; +import com.refinedmods.refinedstorage.api.autocrafting.calculation.PatternCycleDetectedException; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; import com.refinedmods.refinedstorage.api.resource.list.MutableResourceListImpl; +import java.util.Collections; import java.util.UUID; import org.slf4j.Logger; @@ -22,8 +25,16 @@ private PreviewCraftingCalculatorListener(final PreviewState previewState) { this.previewState = previewState; } - public static PreviewCraftingCalculatorListener ofRoot() { - return new PreviewCraftingCalculatorListener(new PreviewState()); + public static Preview calculatePreview(final CraftingCalculator calculator, + final ResourceKey resource, + final long amount) { + final PreviewCraftingCalculatorListener listener = new PreviewCraftingCalculatorListener(new PreviewState()); + try { + calculator.calculate(resource, amount, listener); + } catch (final PatternCycleDetectedException e) { + return new Preview(PreviewType.CYCLE_DETECTED, Collections.emptyList(), e.getPattern().getOutputs()); + } + return listener.buildPreview(); } @Override diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewType.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewType.java index 26e8d2b07..8e4654cea 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewType.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewType.java @@ -5,5 +5,6 @@ @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.9") public enum PreviewType { SUCCESS, - MISSING_RESOURCES + MISSING_RESOURCES, + CYCLE_DETECTED } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternImpl.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternImpl.java index 7f5a7066d..2bd80427f 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternImpl.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/PatternImpl.java @@ -41,4 +41,9 @@ public List getIngredients() { public List getOutputs() { return outputs; } + + @Override + public String toString() { + return ingredients + " -> " + outputs; + } } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewBuilderTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewBuilderTest.java index af5fc6379..462f75c18 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewBuilderTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewBuilderTest.java @@ -1,5 +1,8 @@ package com.refinedmods.refinedstorage.api.autocrafting.preview; +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.PatternImpl; + import java.util.Collections; import java.util.List; @@ -27,7 +30,22 @@ void testDefaultState() { // Assert assertThat(preview).usingRecursiveComparison() - .isEqualTo(new Preview(PreviewType.SUCCESS, Collections.emptyList())); + .isEqualTo(new Preview(PreviewType.SUCCESS, Collections.emptyList(), Collections.emptyList())); + } + + @Test + void testWithPatternWithCycle() { + // Arrange + final Pattern pattern = new PatternImpl(OAK_PLANKS); + + // Act + final Preview preview = PreviewBuilder.ofType(PreviewType.CYCLE_DETECTED) + .withPatternWithCycle(pattern) + .build(); + + // Assert + assertThat(preview).usingRecursiveComparison() + .isEqualTo(new Preview(PreviewType.CYCLE_DETECTED, Collections.emptyList(), pattern.getOutputs())); } @Test @@ -49,7 +67,7 @@ void testPreview() { new PreviewItem(OAK_PLANKS, 0, 0, 5), new PreviewItem(OAK_LOG, 3, 0, 0), new PreviewItem(SPRUCE_LOG, 0, 3, 0) - ))); + ), Collections.emptyList())); } @ParameterizedTest diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java index e389ea4b5..e29ba1e55 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java @@ -7,7 +7,6 @@ import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculatorImpl; 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.storage.EmptyActor; import com.refinedmods.refinedstorage.api.storage.StorageImpl; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; @@ -31,6 +30,8 @@ import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_LOG; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.SPRUCE_PLANKS; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.STICKS; +import static com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewCraftingCalculatorListener.calculatePreview; +import static com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType.CYCLE_DETECTED; import static com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType.MISSING_RESOURCES; import static com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType.SUCCESS; import static org.assertj.core.api.Assertions.assertThat; @@ -49,7 +50,7 @@ void shouldNotCalculateForPatternThatIsNotFound() { final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Executable action = () -> calculateAndGetPreview(sut, CRAFTING_TABLE, 1); + final Executable action = () -> calculatePreview(sut, CRAFTING_TABLE, 1); // Assert final IllegalStateException e = assertThrows(IllegalStateException.class, action); @@ -65,7 +66,7 @@ void shouldNotCalculateWithInvalidRequestedAmount(final long requestedAmount) { final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Executable action = () -> calculateAndGetPreview(sut, CRAFTING_TABLE, requestedAmount); + final Executable action = () -> calculatePreview(sut, CRAFTING_TABLE, requestedAmount); // Assert final IllegalArgumentException e = assertThrows(IllegalArgumentException.class, action); @@ -88,7 +89,7 @@ void shouldCalculateForSingleRootPatternSingleIngredientAndAllResourcesAreAvaila final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, requestedAmount); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, requestedAmount); // Assert assertThat(preview).usingRecursiveComparison().isEqualTo(PreviewBuilder.ofType(SUCCESS) @@ -120,7 +121,7 @@ void shouldCalculateForSingleRootPatternSingleIngredientSpreadOutOverMultipleIng final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, requestedAmount); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, requestedAmount); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(PreviewBuilder.ofType(MISSING_RESOURCES) @@ -143,7 +144,7 @@ void shouldNotCalculateForSingleRootPatternSingleIngredientAndAlmostAllResources final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, 3); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 3); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG) @@ -177,7 +178,7 @@ void shouldCalculateWithSingleRootPatternWithMultipleIngredientAndMultipleAreCra final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, 1); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 1); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(PreviewBuilder.ofType(SUCCESS) @@ -203,7 +204,7 @@ void shouldPrioritizeResourcesThatWeHaveMostOfInStorageForSingleRootPatternAndMu final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, 11); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 11); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(PreviewBuilder.ofType(SUCCESS) @@ -229,7 +230,7 @@ void shouldExhaustAllPossibleIngredientsWhenRunningOutInSingleRootPatternAndMult final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, 16); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 16); // Assert assertThat(preview) @@ -263,7 +264,7 @@ void shouldExhaustAllPossibleIngredientsWhenRunningOutInSingleRootPatternAndMult final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, 1); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 1); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(PreviewBuilder.ofType(MISSING_RESOURCES) @@ -292,7 +293,7 @@ void shouldCalculateForMultipleRootPatternsAndSingleIngredientAndAllResourcesAre final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, 2); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 2); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(PreviewBuilder.ofType(SUCCESS) @@ -320,7 +321,7 @@ void shouldNotCalculateForMultipleRootPatternsAndSingleIngredientAndAlmostAllRes final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, 3); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 3); // Assert assertThat(preview) @@ -352,7 +353,7 @@ void shouldCalculateForSingleRootPatternAndSingleChildPatternWithSingleIngredien final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, 3); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 3); // Assert assertThat(preview) @@ -388,7 +389,7 @@ void shouldNotCalculateForSingleRootPatternSingleChildPatternWSingleIngredientAn final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, CRAFTING_TABLE, 3); + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 3); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(PreviewBuilder.ofType(MISSING_RESOURCES) @@ -426,7 +427,7 @@ void shouldCraftMoreIfNecessaryIfResourcesFromInternalStorageAreUsedUp() { final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, SIGN, 1); + final Preview preview = calculatePreview(sut, SIGN, 1); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(PreviewBuilder.ofType(SUCCESS) @@ -466,7 +467,7 @@ void shouldCraftMoreIfNecessaryIfResourcesFromStorageAreUsedUp() { final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, SIGN, 1); + final Preview preview = calculatePreview(sut, SIGN, 1); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(PreviewBuilder.ofType(SUCCESS) @@ -538,7 +539,7 @@ void shouldKeepCalculatingEvenIfResourcesAreMissing( final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, SIGN, requestedAmount); + final Preview preview = calculatePreview(sut, SIGN, requestedAmount); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(expectedPreview); @@ -575,7 +576,7 @@ void shouldCraftCorrectAmountWhenNotRequestingAMultipleOfThePatternOutputAmount( final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); // Act - final Preview preview = calculateAndGetPreview(sut, OAK_PLANKS, requestedAmount); + final Preview preview = calculatePreview(sut, OAK_PLANKS, requestedAmount); // Assert assertThat(preview).usingRecursiveComparison(PREVIEW_CONFIG).isEqualTo(PreviewBuilder.ofType(SUCCESS) @@ -584,12 +585,30 @@ void shouldCraftCorrectAmountWhenNotRequestingAMultipleOfThePatternOutputAmount( .build()); } - private static Preview calculateAndGetPreview(final CraftingCalculator calculator, - final ResourceKey resource, - final long amount) { - final PreviewCraftingCalculatorListener listener = PreviewCraftingCalculatorListener.ofRoot(); - calculator.calculate(resource, amount, listener); - return listener.buildPreview(); + @Test + void shouldDetectPatternCycles() { + // Arrange + final RootStorage storage = storage(); + final Pattern cycledPattern = pattern() + .ingredient(OAK_LOG, 1) + .output(OAK_PLANKS, 4) + .build(); + final PatternRepository patterns = patterns( + cycledPattern, + pattern() + .ingredient(OAK_PLANKS, 4) + .output(OAK_LOG, 1) + .build() + ); + final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); + + // Act + final Preview preview = calculatePreview(sut, OAK_PLANKS, 1); + + // Assert + assertThat(preview).usingRecursiveComparison().isEqualTo(PreviewBuilder.ofType(CYCLE_DETECTED) + .withPatternWithCycle(cycledPattern) + .build()); } private static RootStorage storage(final ResourceAmount... resourceAmounts) { diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java index 4423770be..df401966b 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java @@ -3,6 +3,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.preview.Preview; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewItem; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import com.refinedmods.refinedstorage.common.api.RefinedStorageClientApi; import com.refinedmods.refinedstorage.common.api.support.resource.ResourceRendering; import com.refinedmods.refinedstorage.common.support.amount.AbstractAmountScreen; @@ -22,6 +23,7 @@ import net.minecraft.client.renderer.Rect2i; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.Style; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Inventory; import org.joml.Vector3f; @@ -42,6 +44,18 @@ public class AutocraftingPreviewScreen extends AbstractAmountScreen items = preview.items(); final int rows = Math.ceilDiv(items.size(), COLUMNS); for (int i = 0; i < rows; ++i) { - final int scrollOffset = previewItemsScrollbar.isSmoothScrolling() - ? (int) previewItemsScrollbar.getOffset() - : (int) previewItemsScrollbar.getOffset() * ROW_HEIGHT; + final int scrollOffset = getScrollOffset(); final int yy = y + (i * ROW_HEIGHT) - scrollOffset; renderRow(graphics, x, yy, i, items, mouseX, mouseY); } - graphics.disableScissor(); + } + + private int getScrollOffset() { + if (previewItemsScrollbar == null) { + return 0; + } + return (previewItemsScrollbar.isSmoothScrolling() + ? (int) previewItemsScrollbar.getOffset() + : (int) previewItemsScrollbar.getOffset() * ROW_HEIGHT); } private void renderRow(final GuiGraphics graphics, diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingPreviewResponsePacket.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingPreviewResponsePacket.java index d46a880d1..297420f7f 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingPreviewResponsePacket.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingPreviewResponsePacket.java @@ -35,6 +35,8 @@ public record AutocraftingPreviewResponsePacket(UUID id, Preview preview) implem StreamCodec.composite( enumStreamCodec(PreviewType.values()), Preview::type, ByteBufCodecs.collection(ArrayList::new, PREVIEW_ITEM_STREAM_CODEC), Preview::items, + ByteBufCodecs.collection(ArrayList::new, ResourceCodecs.AMOUNT_STREAM_CODEC), + Preview::outputsOfPatternWithCycle, Preview::new ); public static final StreamCodec STREAM_CODEC = 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 5ec7d5adc..5fcbe48e8 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 @@ -227,6 +227,9 @@ "gui.refinedstorage.autocrafting_preview.start.missing_resources": "There are missing resources.", "gui.refinedstorage.autocrafting_preview.available": "Available: %s", "gui.refinedstorage.autocrafting_preview.to_craft": "To craft: %s", + "gui.refinedstorage.autocrafting_preview.cycle_detected": "A pattern cycle was detected!", + "gui.refinedstorage.autocrafting_preview.cycle_detected.outputs": "The pattern had following outputs:", + "gui.refinedstorage.autocrafting_preview.cycle_detected.break_the_cycle_and_try_again": "Break the cycle and try again.", "gui.refinedstorage.autocrafting_preview.missing": "Missing: %s", "gui.refinedstorage.autocrafter_manager.search_mode": "Search mode", "gui.refinedstorage.autocrafter_manager.search_mode.all": "All", diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java index 2aba9c211..d2f1f6c79 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java @@ -79,9 +79,7 @@ public boolean contains(final AutocraftingNetworkComponent component) { public Optional getPreview(final ResourceKey resource, final long amount) { final RootStorage rootStorage = rootStorageProvider.get(); final CraftingCalculator craftingCalculator = new CraftingCalculatorImpl(patternRepository, rootStorage); - final PreviewCraftingCalculatorListener listener = PreviewCraftingCalculatorListener.ofRoot(); - craftingCalculator.calculate(resource, amount, listener); - return Optional.of(listener.buildPreview()); + return Optional.of(PreviewCraftingCalculatorListener.calculatePreview(craftingCalculator, resource, amount)); } @Override diff --git a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java index 002603538..c4edd7920 100644 --- a/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java +++ b/refinedstorage-network/src/test/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImplTest.java @@ -19,6 +19,7 @@ import com.refinedmods.refinedstorage.api.storage.root.RootStorageImpl; import com.refinedmods.refinedstorage.network.test.fixtures.FakeTaskStatusProvider; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -166,6 +167,6 @@ void shouldGetPreview() { .isEqualTo(new Preview(PreviewType.SUCCESS, List.of( new PreviewItem(B, 0, 0, 2), new PreviewItem(A, 6, 0, 0) - ))); + ), Collections.emptyList())); } } From 7320e07d2411fccc71928f2790b206344b0762db Mon Sep 17 00:00:00 2001 From: raoulvdberge Date: Fri, 27 Dec 2024 20:08:23 +0100 Subject: [PATCH 3/6] fix: detect requests that cause number overflow --- .../calculation/CraftingCalculatorImpl.java | 3 ++ .../calculation/CraftingTree.java | 3 ++ ...berOverflowDuringCalculationException.java | 7 +++ .../PreviewCraftingCalculatorListener.java | 3 ++ .../api/autocrafting/preview/PreviewType.java | 3 +- .../api/autocrafting/preview/PreviewTest.java | 44 +++++++++++++++++++ .../preview/AutocraftingPreviewScreen.java | 31 +++++++++++++ .../assets/refinedstorage/lang/en_us.json | 2 + 8 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/NumberOverflowDuringCalculationException.java diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImpl.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImpl.java index 4661bb873..3c04210f1 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImpl.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImpl.java @@ -29,6 +29,9 @@ public void calculate(final ResourceKey resource, Amount lastPatternAmount = null; for (final Pattern pattern : patterns) { final Amount patternAmount = Amount.of(pattern, resource, amount); + if (patternAmount.getTotal() < 0) { + throw new NumberOverflowDuringCalculationException(); + } final CraftingCalculatorListener childListener = listener.childCalculationStarted(); final CraftingTree tree = root(pattern, rootStorage, patternAmount, patternRepository, childListener); final CraftingTree.CalculationResult calculationResult = tree.calculate(); diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingTree.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingTree.java index fed0b0d20..755bacac9 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingTree.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingTree.java @@ -76,6 +76,9 @@ CalculationResult calculate() { private CalculationResult calculateIngredient(final IngredientState ingredientState) { CraftingState.ResourceState resourceState = craftingState.getResource(ingredientState.get()); long remaining = ingredientState.amount() * amount.iterations(); + if (remaining < 0) { + throw new NumberOverflowDuringCalculationException(); + } while (remaining > 0) { if (resourceState.isInInternalStorage()) { final long toTake = Math.min(remaining, resourceState.inInternalStorage()); diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/NumberOverflowDuringCalculationException.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/NumberOverflowDuringCalculationException.java new file mode 100644 index 000000000..7e2634708 --- /dev/null +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/NumberOverflowDuringCalculationException.java @@ -0,0 +1,7 @@ +package com.refinedmods.refinedstorage.api.autocrafting.calculation; + +public class NumberOverflowDuringCalculationException extends RuntimeException { + NumberOverflowDuringCalculationException() { + super("Invalid amount"); + } +} diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java index 1722f79e9..31b1a6791 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewCraftingCalculatorListener.java @@ -2,6 +2,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculator; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculatorListener; +import com.refinedmods.refinedstorage.api.autocrafting.calculation.NumberOverflowDuringCalculationException; import com.refinedmods.refinedstorage.api.autocrafting.calculation.PatternCycleDetectedException; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import com.refinedmods.refinedstorage.api.resource.list.MutableResourceList; @@ -33,6 +34,8 @@ public static Preview calculatePreview(final CraftingCalculator calculator, calculator.calculate(resource, amount, listener); } catch (final PatternCycleDetectedException e) { return new Preview(PreviewType.CYCLE_DETECTED, Collections.emptyList(), e.getPattern().getOutputs()); + } catch (final NumberOverflowDuringCalculationException e) { + return new Preview(PreviewType.OVERFLOW, Collections.emptyList(), Collections.emptyList()); } return listener.buildPreview(); } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewType.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewType.java index 8e4654cea..80f75d36e 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewType.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewType.java @@ -6,5 +6,6 @@ public enum PreviewType { SUCCESS, MISSING_RESOURCES, - CYCLE_DETECTED + CYCLE_DETECTED, + OVERFLOW } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java index e29ba1e55..605d8258c 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java @@ -33,6 +33,7 @@ import static com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewCraftingCalculatorListener.calculatePreview; import static com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType.CYCLE_DETECTED; import static com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType.MISSING_RESOURCES; +import static com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType.OVERFLOW; import static com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType.SUCCESS; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -611,6 +612,49 @@ void shouldDetectPatternCycles() { .build()); } + @Test + void shouldDetectNumberOverflowInIngredient() { + // Arrange + final RootStorage storage = storage(); + final PatternRepository patterns = patterns( + pattern() + .ingredient(OAK_LOG, Long.MAX_VALUE) + .output(OAK_PLANKS, 1) + .build() + ); + final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); + + // Act + final Preview preview = calculatePreview(sut, OAK_PLANKS, 2); + + // Assert + assertThat(preview).usingRecursiveComparison().isEqualTo(PreviewBuilder.ofType(OVERFLOW).build()); + } + + @Test + void shouldDetectNumberOverflowInChildPattern() { + // Arrange + final RootStorage storage = storage(); + final PatternRepository patterns = patterns( + pattern() + .ingredient(OAK_LOG, 1) + .output(OAK_PLANKS, 4) + .build(), + pattern() + .ingredient(OAK_PLANKS, 4) + .output(CRAFTING_TABLE, 1) + .build() + ); + final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); + + // Act + final Preview preview = calculatePreview(sut, OAK_PLANKS, Long.MAX_VALUE); + + // Assert + assertThat(preview).usingRecursiveComparison().isEqualTo(PreviewBuilder.ofType(OVERFLOW).build()); + } + + private static RootStorage storage(final ResourceAmount... resourceAmounts) { final RootStorage storage = new RootStorageImpl(); storage.addSource(new StorageImpl()); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java index df401966b..a6c4e13e4 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java @@ -56,6 +56,14 @@ public class AutocraftingPreviewScreen extends AbstractAmountScreen Date: Fri, 27 Dec 2024 20:46:54 +0100 Subject: [PATCH 4/6] feat: ability to retrieve the maximum amount craftable --- CHANGELOG.md | 1 + .../calculation/CalculationException.java | 7 ++ .../calculation/CraftingCalculator.java | 3 +- .../calculation/CraftingCalculatorImpl.java | 48 ++++++++++ ...ngResourcesCraftingCalculatorListener.java | 40 ++++++++ ...berOverflowDuringCalculationException.java | 2 +- .../PatternCycleDetectedException.java | 2 +- .../autocrafting/preview/PreviewProvider.java | 2 + .../api/autocrafting/AutocraftingHelpers.java | 30 ++++++ .../CraftingCalculatorImplTest.java | 95 +++++++++++++++++++ .../calculation/package-info.java | 7 ++ .../api/autocrafting/preview/PreviewTest.java | 25 +---- .../AutocraftingPreviewContainerMenu.java | 17 ++++ .../preview/AutocraftingPreviewListener.java | 2 + .../preview/AutocraftingPreviewScreen.java | 45 +++++++++ .../common/grid/AbstractGridBlockEntity.java | 8 ++ .../grid/AbstractGridContainerMenu.java | 5 + .../common/grid/ClientCraftingGrid.java | 5 + .../common/grid/WirelessGrid.java | 5 + .../storage/portablegrid/PortableGrid.java | 5 + ...tocraftingStorageMonitorContainerMenu.java | 5 + .../StorageMonitorBlockEntity.java | 8 ++ ...craftingPreviewMaxAmountRequestPacket.java | 38 ++++++++ .../common/support/packet/c2s/C2SPackets.java | 4 + ...raftingPreviewMaxAmountResponsePacket.java | 31 ++++++ .../common/support/packet/s2c/S2CPackets.java | 4 + .../common/util/ClientPlatformUtil.java | 6 ++ .../assets/refinedstorage/lang/en_us.json | 4 +- .../fabric/ClientModInitializerImpl.java | 5 + .../fabric/ModInitializerImpl.java | 14 +++ .../neoforge/ModInitializer.java | 12 +++ .../AutocraftingNetworkComponentImpl.java | 7 ++ 32 files changed, 465 insertions(+), 27 deletions(-) create mode 100644 refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CalculationException.java create mode 100644 refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java create mode 100644 refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingHelpers.java create mode 100644 refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImplTest.java create mode 100644 refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/package-info.java create mode 100644 refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/AutocraftingPreviewMaxAmountRequestPacket.java create mode 100644 refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingPreviewMaxAmountResponsePacket.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aeb146a3..05fc93c56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added - Autocrafting engine. +- The crafting preview now has the ability to fill out the maximum amount of a resource you can currently craft. ### Changed diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CalculationException.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CalculationException.java new file mode 100644 index 000000000..1783b1b1d --- /dev/null +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CalculationException.java @@ -0,0 +1,7 @@ +package com.refinedmods.refinedstorage.api.autocrafting.calculation; + +class CalculationException extends RuntimeException { + protected CalculationException(final String message) { + super(message); + } +} diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculator.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculator.java index ec3f665ef..a961ef919 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculator.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculator.java @@ -4,8 +4,9 @@ import org.apiguardian.api.API; -@FunctionalInterface @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") public interface CraftingCalculator { void calculate(ResourceKey resource, long amount, CraftingCalculatorListener listener); + + long getMaxAmount(ResourceKey resource); } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImpl.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImpl.java index 3c04210f1..f40065405 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImpl.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImpl.java @@ -8,9 +8,14 @@ import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import static com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingTree.root; public class CraftingCalculatorImpl implements CraftingCalculator { + private static final Logger LOGGER = LoggerFactory.getLogger(CraftingCalculatorImpl.class); + private final PatternRepository patternRepository; private final RootStorage rootStorage; @@ -48,4 +53,47 @@ public void calculate(final ResourceKey resource, } listener.childCalculationCompleted(resource, lastPatternAmount.getTotal(), lastChildListener); } + + private boolean isCraftable(final ResourceKey resource, final long amount) { + final MissingResourcesCraftingCalculatorListener listener = new MissingResourcesCraftingCalculatorListener(); + calculate(resource, amount, listener); + return !listener.isMissingResources(); + } + + @Override + public long getMaxAmount(final ResourceKey resource) { + try { + LOGGER.debug("Finding max amount for {} starting from 1", resource); + long low = 1; + long high = 1; + int calculationCount = 1; + while (isCraftable(resource, high)) { + low = high; + high = high * 2; + LOGGER.debug("Finding low and high for the craftable amount, currently between {} and {}", low, high); + calculationCount++; + } + if (low == high) { + return 0; + } + LOGGER.debug("Our craftable amount is between {} and {}", low, high); + while (low < high) { + final long amount = low + (high - low + 1) / 2; + LOGGER.debug("Trying {} (between {} and {})", amount, low, high); + calculationCount++; + if (isCraftable(resource, amount)) { + LOGGER.debug("{} was craftable, increasing our low amount", amount); + low = amount; + } else { + LOGGER.debug("{} is not craftable, decreasing our high amount", amount); + high = amount - 1; + } + } + LOGGER.debug("Found the maximum amount of {} in {} tries", low, calculationCount); + return low; + } catch (final CalculationException e) { + LOGGER.debug("Failed to calculate the maximum amount", e); + return 0; + } + } } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java new file mode 100644 index 000000000..1b448dcad --- /dev/null +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/MissingResourcesCraftingCalculatorListener.java @@ -0,0 +1,40 @@ +package com.refinedmods.refinedstorage.api.autocrafting.calculation; + +import com.refinedmods.refinedstorage.api.resource.ResourceKey; + +class MissingResourcesCraftingCalculatorListener implements CraftingCalculatorListener { + private boolean missingResources; + + MissingResourcesCraftingCalculatorListener() { + } + + MissingResourcesCraftingCalculatorListener(final boolean missingResources) { + this.missingResources = missingResources; + } + + boolean isMissingResources() { + return missingResources; + } + + @Override + public CraftingCalculatorListener childCalculationStarted() { + return new MissingResourcesCraftingCalculatorListener(missingResources); + } + + @Override + public void childCalculationCompleted(final ResourceKey resource, + final long amount, + final CraftingCalculatorListener childListener) { + missingResources = ((MissingResourcesCraftingCalculatorListener) childListener).missingResources; + } + + @Override + public void ingredientsExhausted(final ResourceKey resource, final long amount) { + missingResources = true; + } + + @Override + public void ingredientExtractedFromStorage(final ResourceKey resource, final long amount) { + // no op + } +} diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/NumberOverflowDuringCalculationException.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/NumberOverflowDuringCalculationException.java index 7e2634708..1ef3047a5 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/NumberOverflowDuringCalculationException.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/NumberOverflowDuringCalculationException.java @@ -1,6 +1,6 @@ package com.refinedmods.refinedstorage.api.autocrafting.calculation; -public class NumberOverflowDuringCalculationException extends RuntimeException { +public class NumberOverflowDuringCalculationException extends CalculationException { NumberOverflowDuringCalculationException() { super("Invalid amount"); } diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/PatternCycleDetectedException.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/PatternCycleDetectedException.java index a2ed6376b..c8ebf4269 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/PatternCycleDetectedException.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/PatternCycleDetectedException.java @@ -2,7 +2,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.Pattern; -public class PatternCycleDetectedException extends RuntimeException { +public class PatternCycleDetectedException extends CalculationException { private final Pattern pattern; PatternCycleDetectedException(final Pattern pattern) { diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java index fd69071c9..c274f4a76 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewProvider.java @@ -10,5 +10,7 @@ public interface PreviewProvider { Optional getPreview(ResourceKey resource, long amount); + long getMaxAmount(ResourceKey resource); + boolean startTask(ResourceKey resource, long amount); } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingHelpers.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingHelpers.java new file mode 100644 index 000000000..05f423edf --- /dev/null +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/AutocraftingHelpers.java @@ -0,0 +1,30 @@ +package com.refinedmods.refinedstorage.api.autocrafting; + +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.storage.EmptyActor; +import com.refinedmods.refinedstorage.api.storage.StorageImpl; +import com.refinedmods.refinedstorage.api.storage.root.RootStorage; +import com.refinedmods.refinedstorage.api.storage.root.RootStorageImpl; + +public final class AutocraftingHelpers { + private AutocraftingHelpers() { + } + + public static RootStorage storage(final ResourceAmount... resourceAmounts) { + final RootStorage storage = new RootStorageImpl(); + storage.addSource(new StorageImpl()); + for (final ResourceAmount resourceAmount : resourceAmounts) { + storage.insert(resourceAmount.resource(), resourceAmount.amount(), Action.EXECUTE, EmptyActor.INSTANCE); + } + return storage; + } + + public static PatternRepository patterns(final Pattern... patterns) { + final PatternRepository patternRepository = new PatternRepositoryImpl(); + for (final Pattern pattern : patterns) { + patternRepository.add(pattern); + } + return patternRepository; + } +} diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImplTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImplTest.java new file mode 100644 index 000000000..e0b84def0 --- /dev/null +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingCalculatorImplTest.java @@ -0,0 +1,95 @@ +package com.refinedmods.refinedstorage.api.autocrafting.calculation; + +import com.refinedmods.refinedstorage.api.autocrafting.PatternRepository; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.storage.root.RootStorage; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.patterns; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.storage; +import static com.refinedmods.refinedstorage.api.autocrafting.PatternBuilder.pattern; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; +import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_PLANKS; +import static org.assertj.core.api.Assertions.assertThat; + +class CraftingCalculatorImplTest { + @Test + void shouldNotFindMaxAmountIfThereAreAlwaysMissingResources() { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(OAK_PLANKS, 1) + ); + final PatternRepository patterns = patterns( + pattern() + .ingredient(OAK_LOG, 1) + .output(OAK_PLANKS, 4) + .build(), + pattern() + .ingredient(OAK_PLANKS, 4) + .output(CRAFTING_TABLE, 1) + .build() + ); + final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); + + // Act + final long maxAmount = sut.getMaxAmount(CRAFTING_TABLE); + + // Assert + assertThat(maxAmount).isZero(); + } + + @ParameterizedTest + @ValueSource(longs = {1L, 2L, 3L, 4L, 5L, 6L, 7L, 64L, 128L}) + void shouldFindMaxAmount(final long amountPossible) { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(OAK_LOG, amountPossible) + ); + final PatternRepository patterns = patterns( + pattern() + .ingredient(OAK_LOG, 1) + .output(OAK_PLANKS, 4) + .build(), + pattern() + .ingredient(OAK_PLANKS, 4) + .output(CRAFTING_TABLE, 1) + .build() + ); + final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); + + // Act + final long maxAmount = sut.getMaxAmount(CRAFTING_TABLE); + + // Assert + assertThat(maxAmount).isEqualTo(amountPossible); + } + + @Test + void shouldNotFindMaxAmountIfThereIsANumberOverflow() { + // Arrange + final RootStorage storage = storage( + new ResourceAmount(OAK_PLANKS, Long.MAX_VALUE) + ); + final PatternRepository patterns = patterns( + pattern() + .ingredient(OAK_LOG, 1) + .output(OAK_PLANKS, 4) + .build(), + pattern() + .ingredient(OAK_PLANKS, 4) + .output(CRAFTING_TABLE, 1) + .build() + ); + final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); + + // Act + final long maxAmount = sut.getMaxAmount(CRAFTING_TABLE); + + // Assert + assertThat(maxAmount).isZero(); + } +} diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/package-info.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/package-info.java new file mode 100644 index 000000000..06a860405 --- /dev/null +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@FieldsAndMethodsAreNonnullByDefault +package com.refinedmods.refinedstorage.api.autocrafting.calculation; + +import com.refinedmods.refinedstorage.api.core.FieldsAndMethodsAreNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java index 605d8258c..c36703359 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java @@ -2,15 +2,10 @@ import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.PatternRepository; -import com.refinedmods.refinedstorage.api.autocrafting.PatternRepositoryImpl; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculator; import com.refinedmods.refinedstorage.api.autocrafting.calculation.CraftingCalculatorImpl; -import com.refinedmods.refinedstorage.api.core.Action; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; -import com.refinedmods.refinedstorage.api.storage.EmptyActor; -import com.refinedmods.refinedstorage.api.storage.StorageImpl; import com.refinedmods.refinedstorage.api.storage.root.RootStorage; -import com.refinedmods.refinedstorage.api.storage.root.RootStorageImpl; import java.util.stream.Stream; @@ -22,6 +17,8 @@ import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.patterns; +import static com.refinedmods.refinedstorage.api.autocrafting.AutocraftingHelpers.storage; import static com.refinedmods.refinedstorage.api.autocrafting.PatternBuilder.pattern; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.CRAFTING_TABLE; import static com.refinedmods.refinedstorage.api.autocrafting.ResourceFixtures.OAK_LOG; @@ -653,22 +650,4 @@ void shouldDetectNumberOverflowInChildPattern() { // Assert assertThat(preview).usingRecursiveComparison().isEqualTo(PreviewBuilder.ofType(OVERFLOW).build()); } - - - private static RootStorage storage(final ResourceAmount... resourceAmounts) { - final RootStorage storage = new RootStorageImpl(); - storage.addSource(new StorageImpl()); - for (final ResourceAmount resourceAmount : resourceAmounts) { - storage.insert(resourceAmount.resource(), resourceAmount.amount(), Action.EXECUTE, EmptyActor.INSTANCE); - } - return storage; - } - - private static PatternRepository patterns(final Pattern... patterns) { - final PatternRepository patternRepository = new PatternRepositoryImpl(); - for (final Pattern pattern : patterns) { - patternRepository.add(pattern); - } - return patternRepository; - } } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewContainerMenu.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewContainerMenu.java index 5bc763e4b..bee76a9ad 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewContainerMenu.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewContainerMenu.java @@ -2,10 +2,12 @@ import com.refinedmods.refinedstorage.api.autocrafting.preview.Preview; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.common.api.support.resource.PlatformResourceKey; import com.refinedmods.refinedstorage.common.api.support.resource.ResourceContainer; import com.refinedmods.refinedstorage.common.support.containermenu.AbstractResourceContainerMenu; import com.refinedmods.refinedstorage.common.support.containermenu.DisabledResourceSlot; import com.refinedmods.refinedstorage.common.support.containermenu.ResourceSlotType; +import com.refinedmods.refinedstorage.common.support.packet.c2s.C2SPackets; import com.refinedmods.refinedstorage.common.support.resource.ResourceContainerImpl; import java.util.ArrayList; @@ -104,4 +106,19 @@ public void responseReceived(final UUID id, final boolean started) { setCurrentRequest(requests.getFirst()); } } + + public void maxAmountResponseReceived(final long maxAmount) { + if (listener == null) { + return; + } + if (currentRequest.getResource() instanceof PlatformResourceKey resource) { + listener.maxAmountReceived(resource.getResourceType().getDisplayAmount(maxAmount)); + } + } + + void requestMaxAmount() { + if (currentRequest.getResource() instanceof PlatformResourceKey resource) { + C2SPackets.sendAutocraftingPreviewMaxAmountRequest(resource); + } + } } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewListener.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewListener.java index f2c528fbb..dc18fc16c 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewListener.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewListener.java @@ -10,4 +10,6 @@ interface AutocraftingPreviewListener { void previewChanged(@Nullable Preview preview); void requestRemoved(AutocraftingRequest request, boolean last); + + void maxAmountReceived(double maxAmount); } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java index a6c4e13e4..6f390824a 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java @@ -4,11 +4,13 @@ import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewItem; import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType; import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.common.Platform; import com.refinedmods.refinedstorage.common.api.RefinedStorageClientApi; import com.refinedmods.refinedstorage.common.api.support.resource.ResourceRendering; import com.refinedmods.refinedstorage.common.support.amount.AbstractAmountScreen; import com.refinedmods.refinedstorage.common.support.amount.AmountScreenConfiguration; import com.refinedmods.refinedstorage.common.support.amount.DoubleAmountOperations; +import com.refinedmods.refinedstorage.common.support.tooltip.HelpClientTooltipComponent; import com.refinedmods.refinedstorage.common.support.tooltip.SmallText; import com.refinedmods.refinedstorage.common.support.widget.ScrollbarWidget; @@ -18,6 +20,7 @@ import com.google.common.util.concurrent.RateLimiter; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.Rect2i; @@ -40,6 +43,8 @@ public class AutocraftingPreviewScreen extends AbstractAmountScreen requestButtons = new ArrayList<>(); private final boolean requestsButtonsVisible; private final RateLimiter requestRateLimiter = RateLimiter.create(1); + private final RateLimiter maxAmountRequestRateLimiter = RateLimiter.create(1 / 5D); @Nullable private Double changedAmount; + private boolean mayEnableMaxAmountRequestButtonAgain; public AutocraftingPreviewScreen(final Screen parent, final Inventory playerInventory, @@ -154,6 +163,13 @@ protected void init() { if (!wasAlreadyInitialized) { getMenu().loadCurrentRequest(); } + + maxButton = Button.builder(MAX, this::requestMaxAmount) + .size(22, 15) + .pos(leftPos + 185 - 1, topPos + 49 - 1) + .build(); + addRenderableWidget(maxButton); + getExclusionZones().add(new Rect2i( leftPos - REQUESTS_WIDTH + 4, topPos, @@ -162,6 +178,11 @@ protected void init() { )); } + private void requestMaxAmount(final Button button) { + button.active = false; + getMenu().requestMaxAmount(); + } + private void initRequestButtons() { requestButtons.clear(); requestButtonsScrollbar = new ScrollbarWidget( @@ -459,6 +480,19 @@ private void renderCellText(final GuiGraphics graphics, ); } + @Override + protected void renderTooltip(final GuiGraphics graphics, final int x, final int y) { + super.renderTooltip(graphics, x, y); + if (maxButton != null && maxButton.isHovered()) { + Platform.INSTANCE.renderTooltip( + graphics, + List.of(HelpClientTooltipComponent.createAlwaysDisplayed(MAX_HELP)), + x, + y + ); + } + } + @Override public boolean mouseClicked(final double mouseX, final double mouseY, final int clickedButton) { if (previewItemsScrollbar != null && previewItemsScrollbar.mouseClicked(mouseX, mouseY, clickedButton)) { @@ -569,6 +603,10 @@ protected void containerTick() { getMenu().amountChanged(changedAmount); changedAmount = null; } + if (mayEnableMaxAmountRequestButtonAgain && maxButton != null && maxAmountRequestRateLimiter.tryAcquire()) { + maxButton.active = true; + mayEnableMaxAmountRequestButtonAgain = false; + } } @Override @@ -612,6 +650,13 @@ public void requestRemoved(final AutocraftingRequest request, final boolean last } } + @Override + public void maxAmountReceived(final double maxAmount) { + updateAmount(maxAmount); + maxAmountRequestRateLimiter.tryAcquire(); + mayEnableMaxAmountRequestButtonAgain = true; + } + private void updateRequestsScrollbar() { if (requestButtonsScrollbar == null) { return; diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridBlockEntity.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridBlockEntity.java index bb1180725..17997f8e0 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridBlockEntity.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridBlockEntity.java @@ -107,6 +107,14 @@ public Optional getPreview(final ResourceKey resource, final long amoun .flatMap(component -> component.getPreview(resource, amount)); } + @Override + public long getMaxAmount(final ResourceKey resource) { + return Optional.ofNullable(mainNetworkNode.getNetwork()) + .map(network -> network.getComponent(AutocraftingNetworkComponent.class)) + .map(component -> component.getMaxAmount(resource)) + .orElse(0L); + } + @Override public boolean startTask(final ResourceKey resource, final long amount) { final Network network = mainNetworkNode.getNetwork(); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridContainerMenu.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridContainerMenu.java index 64ca79739..ebde4fd05 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridContainerMenu.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/AbstractGridContainerMenu.java @@ -463,6 +463,11 @@ public Optional getPreview(final ResourceKey resource, final long amoun return requireNonNull(grid).getPreview(resource, amount); } + @Override + public long getMaxAmount(final ResourceKey resource) { + return requireNonNull(grid).getMaxAmount(resource); + } + @Override public boolean startTask(final ResourceKey resource, final long amount) { return requireNonNull(grid).startTask(resource, amount); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/ClientCraftingGrid.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/ClientCraftingGrid.java index d4510fbe0..c4de47773 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/ClientCraftingGrid.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/ClientCraftingGrid.java @@ -109,6 +109,11 @@ public Optional getPreview(final ResourceKey resource, final long amoun throw new UnsupportedOperationException(); } + @Override + public long getMaxAmount(final ResourceKey resource) { + return 0; + } + @Override public boolean startTask(final ResourceKey resource, final long amount) { throw new UnsupportedOperationException(); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/WirelessGrid.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/WirelessGrid.java index d48a18765..88c5d046e 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/WirelessGrid.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/grid/WirelessGrid.java @@ -120,6 +120,11 @@ public Optional getPreview(final ResourceKey resource, final long amoun return getAutocrafting().flatMap(component -> component.getPreview(resource, amount)); } + @Override + public long getMaxAmount(final ResourceKey resource) { + return getAutocrafting().map(component -> component.getMaxAmount(resource)).orElse(0L); + } + @Override public boolean startTask(final ResourceKey resource, final long amount) { return getAutocrafting().map(autocrafting -> autocrafting.startTask(resource, amount)).orElse(false); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storage/portablegrid/PortableGrid.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storage/portablegrid/PortableGrid.java index 87df2e7ae..6468b9fdb 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storage/portablegrid/PortableGrid.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storage/portablegrid/PortableGrid.java @@ -135,6 +135,11 @@ public Optional getPreview(final ResourceKey resource, final long amoun return Optional.empty(); } + @Override + public long getMaxAmount(final ResourceKey resource) { + return 0; + } + @Override public boolean startTask(final ResourceKey resource, final long amount) { return false; diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/AutocraftingStorageMonitorContainerMenu.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/AutocraftingStorageMonitorContainerMenu.java index edee1c301..d7d548d63 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/AutocraftingStorageMonitorContainerMenu.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/AutocraftingStorageMonitorContainerMenu.java @@ -43,6 +43,11 @@ public Optional getPreview(final ResourceKey resource, final long amoun return requireNonNull(storageMonitor).getPreview(resource, amount); } + @Override + public long getMaxAmount(final ResourceKey resource) { + return requireNonNull(storageMonitor).getMaxAmount(resource); + } + @Override public boolean startTask(final ResourceKey resource, final long amount) { return requireNonNull(storageMonitor).startTask(resource, amount); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/StorageMonitorBlockEntity.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/StorageMonitorBlockEntity.java index d3b078ab7..b11782669 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/StorageMonitorBlockEntity.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/storagemonitor/StorageMonitorBlockEntity.java @@ -373,6 +373,14 @@ public Optional getPreview(final ResourceKey resource, final long amoun .flatMap(component -> component.getPreview(resource, amount)); } + @Override + public long getMaxAmount(final ResourceKey resource) { + return Optional.ofNullable(mainNetworkNode.getNetwork()) + .map(network -> network.getComponent(AutocraftingNetworkComponent.class)) + .map(component -> component.getMaxAmount(resource)) + .orElse(0L); + } + @Override public boolean startTask(final ResourceKey resource, final long amount) { final Network network = mainNetworkNode.getNetwork(); diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/AutocraftingPreviewMaxAmountRequestPacket.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/AutocraftingPreviewMaxAmountRequestPacket.java new file mode 100644 index 000000000..9856a36a9 --- /dev/null +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/AutocraftingPreviewMaxAmountRequestPacket.java @@ -0,0 +1,38 @@ +package com.refinedmods.refinedstorage.common.support.packet.c2s; + +import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewProvider; +import com.refinedmods.refinedstorage.common.api.support.resource.PlatformResourceKey; +import com.refinedmods.refinedstorage.common.support.packet.PacketContext; +import com.refinedmods.refinedstorage.common.support.packet.s2c.S2CPackets; +import com.refinedmods.refinedstorage.common.support.resource.ResourceCodecs; + +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; + +import static com.refinedmods.refinedstorage.common.util.IdentifierUtil.createIdentifier; + +public record AutocraftingPreviewMaxAmountRequestPacket(PlatformResourceKey resource) implements CustomPacketPayload { + public static final Type PACKET_TYPE = new Type<>(createIdentifier( + "autocrafting_preview_max_amount_request" + )); + public static final StreamCodec STREAM_CODEC = + StreamCodec.composite( + ResourceCodecs.STREAM_CODEC, AutocraftingPreviewMaxAmountRequestPacket::resource, + AutocraftingPreviewMaxAmountRequestPacket::new + ); + + public static void handle(final AutocraftingPreviewMaxAmountRequestPacket packet, final PacketContext ctx) { + if (ctx.getPlayer().containerMenu instanceof PreviewProvider provider) { + final ServerPlayer player = (ServerPlayer) ctx.getPlayer(); + final long maxAmount = provider.getMaxAmount(packet.resource); + S2CPackets.sendAutocraftingPreviewMaxAmountResponse(player, maxAmount); + } + } + + @Override + public Type type() { + return PACKET_TYPE; + } +} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/C2SPackets.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/C2SPackets.java index 96bb861b9..1c4fd5f6f 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/C2SPackets.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/c2s/C2SPackets.java @@ -145,6 +145,10 @@ public static void sendAutocraftingRequest(final UUID id, Platform.INSTANCE.sendPacketToServer(new AutocraftingRequestPacket(id, resource, amount)); } + public static void sendAutocraftingPreviewMaxAmountRequest(final PlatformResourceKey resource) { + Platform.INSTANCE.sendPacketToServer(new AutocraftingPreviewMaxAmountRequestPacket(resource)); + } + public static void sendAutocraftingMonitorCancel(final TaskId taskId) { Platform.INSTANCE.sendPacketToServer(new AutocraftingMonitorCancelPacket(taskId)); } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingPreviewMaxAmountResponsePacket.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingPreviewMaxAmountResponsePacket.java new file mode 100644 index 000000000..093f822e8 --- /dev/null +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/AutocraftingPreviewMaxAmountResponsePacket.java @@ -0,0 +1,31 @@ +package com.refinedmods.refinedstorage.common.support.packet.s2c; + +import com.refinedmods.refinedstorage.common.util.ClientPlatformUtil; + +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; + +import static com.refinedmods.refinedstorage.common.util.IdentifierUtil.createIdentifier; + +public record AutocraftingPreviewMaxAmountResponsePacket(long maxAmount) implements CustomPacketPayload { + public static final Type PACKET_TYPE = new Type<>( + createIdentifier("autocrafting_preview_max_amount_response") + ); + public static final StreamCodec STREAM_CODEC = + StreamCodec.composite( + ByteBufCodecs.VAR_LONG, AutocraftingPreviewMaxAmountResponsePacket::maxAmount, + AutocraftingPreviewMaxAmountResponsePacket::new + ); + + public static void handle(final AutocraftingPreviewMaxAmountResponsePacket packet) { + ClientPlatformUtil.autocraftingPreviewMaxAmountResponseReceived(packet.maxAmount); + } + + @Override + public Type type() { + return PACKET_TYPE; + } +} + diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/S2CPackets.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/S2CPackets.java index d114b3a9c..d36b65168 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/S2CPackets.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/support/packet/s2c/S2CPackets.java @@ -99,6 +99,10 @@ public static void sendAutocraftingPreviewResponse(final ServerPlayer player, Platform.INSTANCE.sendPacketToClient(player, new AutocraftingPreviewResponsePacket(id, preview)); } + public static void sendAutocraftingPreviewMaxAmountResponse(final ServerPlayer player, final long maxAmount) { + Platform.INSTANCE.sendPacketToClient(player, new AutocraftingPreviewMaxAmountResponsePacket(maxAmount)); + } + public static void sendAutocraftingResponse(final ServerPlayer player, final UUID id, final boolean started) { diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java index 12b37c339..b47b15fe0 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/util/ClientPlatformUtil.java @@ -52,6 +52,12 @@ public static void autocraftingResponseReceived(final UUID id, final boolean sta } } + public static void autocraftingPreviewMaxAmountResponseReceived(final long maxAmount) { + if (Minecraft.getInstance().screen instanceof AutocraftingPreviewScreen screen) { + screen.getMenu().maxAmountResponseReceived(maxAmount); + } + } + public static void openCraftingPreview(final List requests, @Nullable final Object parentScreen) { final Minecraft minecraft = Minecraft.getInstance(); if ((!(parentScreen instanceof Screen) && minecraft.screen == null) || minecraft.player == null) { 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 0b787f6a2..38d0785eb 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 @@ -227,12 +227,14 @@ "gui.refinedstorage.autocrafting_preview.start.missing_resources": "There are missing resources.", "gui.refinedstorage.autocrafting_preview.available": "Available: %s", "gui.refinedstorage.autocrafting_preview.to_craft": "To craft: %s", + "gui.refinedstorage.autocrafting_preview.missing": "Missing: %s", + "gui.refinedstorage.autocrafting_preview.max": "Max", + "gui.refinedstorage.autocrafting_preview.max.help": "Fills in the maximum number of this resource you can craft right now without starting the crafting process yet.", "gui.refinedstorage.autocrafting_preview.cycle_detected": "A pattern cycle was detected!", "gui.refinedstorage.autocrafting_preview.cycle_detected.outputs": "The pattern had following outputs:", "gui.refinedstorage.autocrafting_preview.cycle_detected.break_the_cycle_and_try_again": "Break the cycle and try again.", "gui.refinedstorage.autocrafting_preview.request_too_large_to_handle": "The request is too large to handle!", "gui.refinedstorage.autocrafting_preview.request_too_large_to_handle.try_smaller_amount": "Try a smaller amount.", - "gui.refinedstorage.autocrafting_preview.missing": "Missing: %s", "gui.refinedstorage.autocrafter_manager.search_mode": "Search mode", "gui.refinedstorage.autocrafter_manager.search_mode.all": "All", "gui.refinedstorage.autocrafter_manager.search_mode.all.help": "Search in pattern inputs, pattern outputs and autocrafter names.", diff --git a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ClientModInitializerImpl.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ClientModInitializerImpl.java index 27d301f9a..5c2fc9147 100644 --- a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ClientModInitializerImpl.java +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ClientModInitializerImpl.java @@ -25,6 +25,7 @@ import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingMonitorTaskAddedPacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingMonitorTaskRemovedPacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingMonitorTaskStatusChangedPacket; +import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingPreviewMaxAmountResponsePacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingPreviewResponsePacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingResponsePacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.EnergyInfoPacket; @@ -311,6 +312,10 @@ private void registerPacketHandlers() { AutocraftingPreviewResponsePacket.PACKET_TYPE, wrapHandler((packet, ctx) -> AutocraftingPreviewResponsePacket.handle(packet)) ); + ClientPlayNetworking.registerGlobalReceiver( + AutocraftingPreviewMaxAmountResponsePacket.PACKET_TYPE, + wrapHandler((packet, ctx) -> AutocraftingPreviewMaxAmountResponsePacket.handle(packet)) + ); ClientPlayNetworking.registerGlobalReceiver( AutocraftingResponsePacket.PACKET_TYPE, wrapHandler((packet, ctx) -> AutocraftingResponsePacket.handle(packet)) diff --git a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ModInitializerImpl.java b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ModInitializerImpl.java index 731711db6..df1bb4ebf 100644 --- a/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ModInitializerImpl.java +++ b/refinedstorage-fabric/src/main/java/com/refinedmods/refinedstorage/fabric/ModInitializerImpl.java @@ -33,6 +33,7 @@ import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocrafterNameChangePacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingMonitorCancelAllPacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingMonitorCancelPacket; +import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingPreviewMaxAmountRequestPacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingPreviewRequestPacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingRequestPacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.CraftingGridClearPacket; @@ -64,6 +65,7 @@ import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingMonitorTaskAddedPacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingMonitorTaskRemovedPacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingMonitorTaskStatusChangedPacket; +import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingPreviewMaxAmountResponsePacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingPreviewResponsePacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingResponsePacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.EnergyInfoPacket; @@ -491,6 +493,10 @@ private void registerServerToClientPackets() { AutocraftingPreviewResponsePacket.PACKET_TYPE, AutocraftingPreviewResponsePacket.STREAM_CODEC ); + PayloadTypeRegistry.playS2C().register( + AutocraftingPreviewMaxAmountResponsePacket.PACKET_TYPE, + AutocraftingPreviewMaxAmountResponsePacket.STREAM_CODEC + ); PayloadTypeRegistry.playS2C().register( AutocraftingResponsePacket.PACKET_TYPE, AutocraftingResponsePacket.STREAM_CODEC @@ -602,6 +608,10 @@ private void registerClientToServerPackets() { AutocraftingPreviewRequestPacket.PACKET_TYPE, AutocraftingPreviewRequestPacket.STREAM_CODEC ); + PayloadTypeRegistry.playC2S().register( + AutocraftingPreviewMaxAmountRequestPacket.PACKET_TYPE, + AutocraftingPreviewMaxAmountRequestPacket.STREAM_CODEC + ); PayloadTypeRegistry.playC2S().register( AutocraftingRequestPacket.PACKET_TYPE, AutocraftingRequestPacket.STREAM_CODEC @@ -717,6 +727,10 @@ private void registerPacketHandlers() { AutocraftingPreviewRequestPacket.PACKET_TYPE, wrapHandler(AutocraftingPreviewRequestPacket::handle) ); + ServerPlayNetworking.registerGlobalReceiver( + AutocraftingPreviewMaxAmountRequestPacket.PACKET_TYPE, + wrapHandler(AutocraftingPreviewMaxAmountRequestPacket::handle) + ); ServerPlayNetworking.registerGlobalReceiver( AutocraftingRequestPacket.PACKET_TYPE, wrapHandler(AutocraftingRequestPacket::handle) diff --git a/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/ModInitializer.java b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/ModInitializer.java index 8a823eed4..3d2ee4ca3 100644 --- a/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/ModInitializer.java +++ b/refinedstorage-neoforge/src/main/java/com/refinedmods/refinedstorage/neoforge/ModInitializer.java @@ -34,6 +34,7 @@ import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocrafterNameChangePacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingMonitorCancelAllPacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingMonitorCancelPacket; +import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingPreviewMaxAmountRequestPacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingPreviewRequestPacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.AutocraftingRequestPacket; import com.refinedmods.refinedstorage.common.support.packet.c2s.CraftingGridClearPacket; @@ -65,6 +66,7 @@ import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingMonitorTaskAddedPacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingMonitorTaskRemovedPacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingMonitorTaskStatusChangedPacket; +import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingPreviewMaxAmountResponsePacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingPreviewResponsePacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.AutocraftingResponsePacket; import com.refinedmods.refinedstorage.common.support.packet.s2c.EnergyInfoPacket; @@ -688,6 +690,11 @@ private static void registerServerToClientPackets(final PayloadRegistrar registr AutocraftingPreviewResponsePacket.STREAM_CODEC, wrapHandler((packet, ctx) -> AutocraftingPreviewResponsePacket.handle(packet)) ); + registrar.playToClient( + AutocraftingPreviewMaxAmountResponsePacket.PACKET_TYPE, + AutocraftingPreviewMaxAmountResponsePacket.STREAM_CODEC, + wrapHandler((packet, ctx) -> AutocraftingPreviewMaxAmountResponsePacket.handle(packet)) + ); registrar.playToClient( AutocraftingResponsePacket.PACKET_TYPE, AutocraftingResponsePacket.STREAM_CODEC, @@ -841,6 +848,11 @@ private static void registerClientToServerPackets(final PayloadRegistrar registr AutocraftingPreviewRequestPacket.STREAM_CODEC, wrapHandler(AutocraftingPreviewRequestPacket::handle) ); + registrar.playToServer( + AutocraftingPreviewMaxAmountRequestPacket.PACKET_TYPE, + AutocraftingPreviewMaxAmountRequestPacket.STREAM_CODEC, + wrapHandler(AutocraftingPreviewMaxAmountRequestPacket::handle) + ); registrar.playToServer( AutocraftingRequestPacket.PACKET_TYPE, AutocraftingRequestPacket.STREAM_CODEC, diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java index d2f1f6c79..2a7865e2d 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/autocrafting/AutocraftingNetworkComponentImpl.java @@ -82,6 +82,13 @@ public Optional getPreview(final ResourceKey resource, final long amoun return Optional.of(PreviewCraftingCalculatorListener.calculatePreview(craftingCalculator, resource, amount)); } + @Override + public long getMaxAmount(final ResourceKey resource) { + final RootStorage rootStorage = rootStorageProvider.get(); + final CraftingCalculator craftingCalculator = new CraftingCalculatorImpl(patternRepository, rootStorage); + return craftingCalculator.getMaxAmount(resource); + } + @Override public boolean startTask(final ResourceKey resource, final long amount) { return true; From 19889b607315c9e4889877e5c95d9aa7d076ded2 Mon Sep 17 00:00:00 2001 From: raoulvdberge Date: Fri, 27 Dec 2024 22:12:46 +0100 Subject: [PATCH 5/6] fix: missing case for number overflow --- .../calculation/CraftingState.java | 11 +++++++- .../api/autocrafting/preview/PreviewTest.java | 26 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingState.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingState.java index 6e287893b..cc4acc9c5 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingState.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/calculation/CraftingState.java @@ -1,6 +1,7 @@ package com.refinedmods.refinedstorage.api.autocrafting.calculation; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +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; @@ -28,10 +29,18 @@ void extractFromStorage(final ResourceKey resource, final long amount) { void addOutputsToInternalStorage(final Pattern pattern, final Amount amount) { pattern.getOutputs().forEach( - output -> internalStorage.add(output.resource(), output.amount() * amount.iterations()) + output -> addOutputToInternalStorage(amount, output) ); } + private void addOutputToInternalStorage(final Amount amount, final ResourceAmount output) { + final long totalAmount = output.amount() * amount.iterations(); + if (totalAmount < 0) { + throw new NumberOverflowDuringCalculationException(); + } + internalStorage.add(output.resource(), totalAmount); + } + CraftingState copy() { return new CraftingState(storage.copy(), internalStorage.copy()); } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java index c36703359..2071c8bdf 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/preview/PreviewTest.java @@ -629,7 +629,7 @@ void shouldDetectNumberOverflowInIngredient() { } @Test - void shouldDetectNumberOverflowInChildPattern() { + void shouldDetectNumberOverflowWithRootPattern() { // Arrange final RootStorage storage = storage(); final PatternRepository patterns = patterns( @@ -650,4 +650,28 @@ void shouldDetectNumberOverflowInChildPattern() { // Assert assertThat(preview).usingRecursiveComparison().isEqualTo(PreviewBuilder.ofType(OVERFLOW).build()); } + + @Test + void shouldDetectNumberOverflowWithOutputOfChildPattern() { + // Arrange + final RootStorage storage = storage(); + final PatternRepository patterns = patterns( + pattern() + .ingredient(OAK_LOG, 1) + .output(OAK_PLANKS, 4) + .output(SIGN, Long.MAX_VALUE) + .build(), + pattern() + .ingredient(OAK_PLANKS, 4) + .output(CRAFTING_TABLE, 1) + .build() + ); + final CraftingCalculator sut = new CraftingCalculatorImpl(patterns, storage); + + // Act + final Preview preview = calculatePreview(sut, CRAFTING_TABLE, 2); + + // Assert + assertThat(preview).usingRecursiveComparison().isEqualTo(PreviewBuilder.ofType(OVERFLOW).build()); + } } From c1457c6945c43a8f9210f6e104be3b69d3c57005 Mon Sep 17 00:00:00 2001 From: raoulvdberge Date: Fri, 27 Dec 2024 22:18:35 +0100 Subject: [PATCH 6/6] fix: max button shouldn't become active when not needed yet after resizing --- .../common/autocrafting/preview/AutocraftingPreviewScreen.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java index 6f390824a..c503766d3 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/preview/AutocraftingPreviewScreen.java @@ -164,10 +164,12 @@ protected void init() { getMenu().loadCurrentRequest(); } + final boolean wasActive = maxButton == null || maxButton.active; maxButton = Button.builder(MAX, this::requestMaxAmount) .size(22, 15) .pos(leftPos + 185 - 1, topPos + 49 - 1) .build(); + maxButton.active = wasActive; addRenderableWidget(maxButton); getExclusionZones().add(new Rect2i(