diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d827027d..a26738ba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). highest priority first. If there are missing resources, lower priority patterns are checked. - The Autocrafter now faces the block you're clicking when placing it, like the other cable blocks (like the Exporter or Importer). - You can no longer cancel autocrafting tasks if there is not enough space in storage to return the intermediate task storage. +- The Autocrafting Monitor now shows the machine in which a resource is processing. ### Fixed diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatus.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatus.java index 0570fc394..c9f5f1f2e 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatus.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatus.java @@ -1,9 +1,11 @@ package com.refinedmods.refinedstorage.api.autocrafting.status; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import java.util.List; +import javax.annotation.Nullable; import org.apiguardian.api.API; @@ -15,6 +17,7 @@ public record TaskInfo(TaskId id, ResourceKey resource, long amount, long startT public record Item( ResourceKey resource, ItemType type, + @Nullable ExternalPatternInputSinkKey sinkKey, long stored, long processing, long scheduled, diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatusBuilder.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatusBuilder.java index 577111b74..94de7a8e0 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatusBuilder.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/status/TaskStatusBuilder.java @@ -1,11 +1,13 @@ package com.refinedmods.refinedstorage.api.autocrafting.status; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.core.CoreValidations; import com.refinedmods.refinedstorage.api.resource.ResourceKey; import java.util.LinkedHashMap; import java.util.Map; +import javax.annotation.Nullable; public class TaskStatusBuilder { private final TaskStatus.TaskInfo info; @@ -21,9 +23,12 @@ public TaskStatusBuilder stored(final ResourceKey resource, final long stored) { return this; } - public TaskStatusBuilder processing(final ResourceKey resource, final long processing) { + public TaskStatusBuilder processing(final ResourceKey resource, + final long processing, + @Nullable final ExternalPatternInputSinkKey sinkKey) { CoreValidations.validateLargerThanZero(processing, "Processing"); get(resource).processing += processing; + get(resource).sinkKey = sinkKey; return this; } @@ -62,6 +67,7 @@ public TaskStatus build(final double percentageCompleted) { return new TaskStatus(info, percentageCompleted, items.entrySet().stream().map(entry -> new TaskStatus.Item( entry.getKey(), entry.getValue().type, + entry.getValue().sinkKey, entry.getValue().stored, entry.getValue().processing, entry.getValue().scheduled, @@ -73,6 +79,8 @@ private static class MutableItem { private TaskStatus.ItemType type; private long stored; private long processing; + @Nullable + private ExternalPatternInputSinkKey sinkKey; private long scheduled; private long crafting; diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java index 8893ee97b..71f4dcae8 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSink.java @@ -5,14 +5,17 @@ import com.refinedmods.refinedstorage.api.resource.ResourceAmount; import java.util.Collection; +import javax.annotation.Nullable; import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") -@FunctionalInterface public interface ExternalPatternInputSink { Result accept(Pattern pattern, Collection resources, Action action); + @Nullable + ExternalPatternInputSinkKey getKey(Pattern pattern); + enum Result { ACCEPTED, REJECTED, diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkKey.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkKey.java new file mode 100644 index 000000000..d9eaa12e0 --- /dev/null +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkKey.java @@ -0,0 +1,9 @@ +package com.refinedmods.refinedstorage.api.autocrafting.task; + +import org.apiguardian.api.API; + +@FunctionalInterface +@API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") +public interface ExternalPatternInputSinkKey { + String getName(); +} diff --git a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java index 19b8da18d..2416e28be 100644 --- a/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java +++ b/refinedstorage-autocrafting-api/src/main/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalTaskPattern.java @@ -27,6 +27,8 @@ class ExternalTaskPattern extends AbstractTaskPattern { private boolean interceptedAnythingSinceLastStep; @Nullable private ExternalPatternInputSink.Result lastSinkResult; + @Nullable + private ExternalPatternInputSinkKey lastSinkResultKey; ExternalTaskPattern(final Pattern pattern, final TaskPlan.PatternPlan plan) { super(pattern, plan); @@ -103,7 +105,11 @@ void appendStatus(final TaskStatusBuilder builder) { final long iterationsProcessing = iterationsSentToSink - iterationsReceived; if (iterationsProcessing > 0) { for (final ResourceKey input : simulatedIterationInputs.getAll()) { - builder.processing(input, simulatedIterationInputs.get(input) * iterationsProcessing); + builder.processing( + input, + simulatedIterationInputs.get(input) * iterationsProcessing, + lastSinkResultKey + ); } } if (lastSinkResult != null) { @@ -137,6 +143,7 @@ private boolean acceptsIterationInputs(final MutableResourceList internalStorage Action.SIMULATE ); lastSinkResult = simulatedResult; + lastSinkResultKey = externalPatternInputSink.getKey(pattern); if (simulatedResult != ExternalPatternInputSink.Result.ACCEPTED) { return false; } diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java index d4c4417fe..45b50101e 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/ExternalPatternInputSinkBuilder.java @@ -28,14 +28,31 @@ Sink storageSink(final Pattern pattern) { } ExternalPatternInputSink build() { - return (pattern, resources, action) -> { - final Sink sink = sinks.get(pattern); - return sink != null && sink.accept(resources, action) - ? ExternalPatternInputSink.Result.ACCEPTED - : ExternalPatternInputSink.Result.REJECTED; + return new ExternalPatternInputSink() { + @Override + public Result accept(final Pattern pattern, + final Collection resources, + final Action action) { + final Sink sink = sinks.get(pattern); + return sink != null && sink.accept(resources, action) + ? ExternalPatternInputSink.Result.ACCEPTED + : ExternalPatternInputSink.Result.REJECTED; + } + + @Override + public ExternalPatternInputSinkKey getKey(final Pattern pattern) { + return new SinkKey(pattern); + } }; } + record SinkKey(Pattern pattern) implements ExternalPatternInputSinkKey { + @Override + public String getName() { + return pattern.id().toString(); + } + } + static class Sink { private final Storage storage; private boolean enabled = true; diff --git a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java index d37de381b..b8ef33cdd 100644 --- a/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java +++ b/refinedstorage-autocrafting-api/src/test/java/com/refinedmods/refinedstorage/api/autocrafting/task/TaskImplTest.java @@ -16,6 +16,9 @@ import com.refinedmods.refinedstorage.api.storage.root.RootStorage; import com.refinedmods.refinedstorage.api.storage.root.RootStorageImpl; +import java.util.Collection; +import javax.annotation.Nullable; + import org.assertj.core.api.recursive.comparison.RecursiveComparisonConfiguration; import org.junit.jupiter.api.Test; @@ -54,8 +57,24 @@ class TaskImplTest { private static final RecursiveComparisonConfiguration STATUS_CONFIG = RecursiveComparisonConfiguration.builder() .withIgnoredFields("info.startTime") .build(); - private static final ExternalPatternInputSink EMPTY_SINK = (pattern, resources, action) -> - ExternalPatternInputSink.Result.SKIPPED; + private static final ExternalPatternInputSink EMPTY_SINK = fixedResultSink(ExternalPatternInputSink.Result.SKIPPED); + + private static ExternalPatternInputSink fixedResultSink(final ExternalPatternInputSink.Result result) { + return new ExternalPatternInputSink() { + @Override + public Result accept(final Pattern pattern, + final Collection resources, + final Action action) { + return result; + } + + @Nullable + @Override + public ExternalPatternInputSinkKey getKey(final Pattern pattern) { + return null; + } + }; + } @Test void testInitialState() { @@ -661,10 +680,22 @@ void shouldNotCompleteTaskWithExternalPatternIfSinkDoesNotAcceptResourcesOnlyWhe new ResourceAmount(IRON_ORE, 3) ); final PatternRepository patterns = patterns(IRON_INGOT_PATTERN, IRON_PICKAXE_PATTERN); - final ExternalPatternInputSink sink = (pattern, resources, action) -> - action == Action.SIMULATE - ? ExternalPatternInputSink.Result.ACCEPTED - : ExternalPatternInputSink.Result.REJECTED; + final ExternalPatternInputSink sink = new ExternalPatternInputSink() { + @Override + public Result accept(final Pattern pattern, + final Collection resources, + final Action action) { + return action == Action.SIMULATE + ? ExternalPatternInputSink.Result.ACCEPTED + : ExternalPatternInputSink.Result.REJECTED; + } + + @Nullable + @Override + public ExternalPatternInputSinkKey getKey(final Pattern pattern) { + return null; + } + }; final Task task = getRunningTask(storage, patterns, sink, IRON_PICKAXE, 1); assertThat(storage.getAll()).isEmpty(); @@ -808,7 +839,7 @@ void shouldReportStatusCorrectly() { new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) .crafting(IRON_PICKAXE, 1) .scheduled(IRON_INGOT, 2) - .processing(IRON_ORE, 1) + .processing(IRON_ORE, 1, new ExternalPatternInputSinkBuilder.SinkKey(IRON_INGOT_PATTERN)) .crafting(STICKS, 4) .stored(OAK_PLANKS, 4) .stored(IRON_ORE, 2) @@ -829,7 +860,7 @@ void shouldReportStatusCorrectly() { new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) .crafting(IRON_PICKAXE, 1) .scheduled(IRON_INGOT, 1) - .processing(IRON_ORE, 2) + .processing(IRON_ORE, 2, new ExternalPatternInputSinkBuilder.SinkKey(IRON_INGOT_PATTERN)) .stored(OAK_PLANKS, 2) .stored(IRON_ORE, 1) .stored(STICKS, 4) @@ -850,7 +881,7 @@ void shouldReportStatusCorrectly() { assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) .crafting(IRON_PICKAXE, 1) - .processing(IRON_ORE, 3) + .processing(IRON_ORE, 3, new ExternalPatternInputSinkBuilder.SinkKey(IRON_INGOT_PATTERN)) .stored(OAK_PLANKS, 2) .stored(STICKS, 4) .build(0.6666666666666666)); @@ -869,7 +900,7 @@ void shouldReportStatusCorrectly() { assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( new TaskStatusBuilder(task.getId(), IRON_PICKAXE, 1, 0) .crafting(IRON_PICKAXE, 1) - .processing(IRON_ORE, 1) + .processing(IRON_ORE, 1, new ExternalPatternInputSinkBuilder.SinkKey(IRON_INGOT_PATTERN)) .stored(OAK_PLANKS, 2) .stored(STICKS, 4) .stored(IRON_INGOT, 2) @@ -965,7 +996,7 @@ void shouldReportWhetherSinkIsRejectingInputsOnStatus() { new TaskStatusBuilder(task.getId(), IRON_INGOT, 2, 0) .scheduled(IRON_INGOT, 1) .stored(IRON_ORE, 1) - .processing(IRON_ORE, 1) + .processing(IRON_ORE, 1, new ExternalPatternInputSinkBuilder.SinkKey(IRON_INGOT_PATTERN)) .build(0)); ironOreSink.setEnabled(false); @@ -974,7 +1005,7 @@ void shouldReportWhetherSinkIsRejectingInputsOnStatus() { new TaskStatusBuilder(task.getId(), IRON_INGOT, 2, 0) .scheduled(IRON_INGOT, 1) .stored(IRON_ORE, 1) - .processing(IRON_ORE, 1) + .processing(IRON_ORE, 1, new ExternalPatternInputSinkBuilder.SinkKey(IRON_INGOT_PATTERN)) .rejected(IRON_INGOT) .build(0)); } @@ -995,7 +1026,7 @@ void shouldReportWhetherSinkIsNotFoundOnStatus() { new TaskStatusBuilder(task.getId(), IRON_INGOT, 2, 0) .scheduled(IRON_INGOT, 1) .stored(IRON_ORE, 1) - .processing(IRON_ORE, 1) + .processing(IRON_ORE, 1, new ExternalPatternInputSinkBuilder.SinkKey(IRON_INGOT_PATTERN)) .build(0)); ironOreSink.setEnabled(false); @@ -1004,7 +1035,7 @@ void shouldReportWhetherSinkIsNotFoundOnStatus() { new TaskStatusBuilder(task.getId(), IRON_INGOT, 2, 0) .scheduled(IRON_INGOT, 1) .stored(IRON_ORE, 1) - .processing(IRON_ORE, 1) + .processing(IRON_ORE, 1, null) .noneFound(IRON_INGOT) .build(0)); } @@ -1025,20 +1056,20 @@ void shouldReportWhetherSinkIsLockedFoundOnStatus() { new TaskStatusBuilder(task.getId(), IRON_INGOT, 2, 0) .scheduled(IRON_INGOT, 1) .stored(IRON_ORE, 1) - .processing(IRON_ORE, 1) + .processing(IRON_ORE, 1, new ExternalPatternInputSinkBuilder.SinkKey(IRON_INGOT_PATTERN)) .build(0)); ironOreSink.setEnabled(false); task.step( storage, - (pattern, resources, action) -> ExternalPatternInputSink.Result.LOCKED, + fixedResultSink(ExternalPatternInputSink.Result.LOCKED), StepBehavior.DEFAULT ); assertThat(task.getStatus()).usingRecursiveComparison(STATUS_CONFIG).isEqualTo( new TaskStatusBuilder(task.getId(), IRON_INGOT, 2, 0) .scheduled(IRON_INGOT, 1) .stored(IRON_ORE, 1) - .processing(IRON_ORE, 1) + .processing(IRON_ORE, 1, null) .locked(IRON_INGOT) .build(0)); } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterBlockEntity.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterBlockEntity.java index 86ab28da8..1588c9a47 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterBlockEntity.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/AutocrafterBlockEntity.java @@ -1,11 +1,13 @@ package com.refinedmods.refinedstorage.common.autocrafting.autocrafter; import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.StepBehavior; import com.refinedmods.refinedstorage.api.network.Network; import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; import com.refinedmods.refinedstorage.api.network.autocrafting.PatternProvider; import com.refinedmods.refinedstorage.api.network.impl.node.patternprovider.PatternProviderNetworkNode; +import com.refinedmods.refinedstorage.api.network.impl.node.patternprovider.ExternalPatternInputSinkKeyProvider; import com.refinedmods.refinedstorage.common.Platform; import com.refinedmods.refinedstorage.common.api.RefinedStorageApi; import com.refinedmods.refinedstorage.common.api.support.network.InWorldNetworkNodeContainer; @@ -46,7 +48,8 @@ import static com.refinedmods.refinedstorage.common.support.AbstractDirectionalBlock.tryExtractDirection; public class AutocrafterBlockEntity extends AbstractBaseNetworkNodeContainerBlockEntity - implements ExtendedMenuProvider, BlockEntityWithDrops, PatternInventory.Listener, StepBehavior { + implements ExtendedMenuProvider, BlockEntityWithDrops, PatternInventory.Listener, StepBehavior, + ExternalPatternInputSinkKeyProvider { static final int PATTERNS = 9; private static final int MAX_CHAINED_AUTOCRAFTERS = 8; @@ -63,6 +66,8 @@ public class AutocrafterBlockEntity extends AbstractBaseNetworkNodeContainerBloc private int ticks; private int steps = getSteps(0); private int tickRate = getTickRate(0); + @Nullable + private ExternalPatternInputSinkKey externalPatternInputSinkKey; public AutocrafterBlockEntity(final BlockPos pos, final BlockState state) { super( @@ -89,6 +94,7 @@ public AutocrafterBlockEntity(final BlockPos pos, final BlockState state) { }); this.patternContainer.setListener(this); this.mainNetworkNode.setStepBehavior(this); + this.mainNetworkNode.setSinkKeyProvider(this); } @Override @@ -320,6 +326,7 @@ protected void initialize(final ServerLevel level, final Direction direction) { super.initialize(level, direction); final Direction incomingDirection = direction.getOpposite(); final BlockPos sourcePosition = worldPosition.relative(direction); + invalidateExternalPatternInputSinkKey(); mainNetworkNode.setExternalPatternInputSink( RefinedStorageApi.INSTANCE.getPatternProviderExternalPatternInputSinkFactory() .create(level, sourcePosition, incomingDirection)); @@ -402,4 +409,47 @@ protected boolean doesBlockStateChangeWarrantNetworkNodeUpdate(final BlockState final BlockState newBlockState) { return AbstractDirectionalBlock.didDirectionChange(oldBlockState, newBlockState); } + + @Override + @Nullable + public ExternalPatternInputSinkKey getKey() { + if (externalPatternInputSinkKey == null) { + tryUpdateKey(); + } + return externalPatternInputSinkKey; + } + + private void tryUpdateKey() { + if (!(level instanceof ServerLevel serverLevel)) { + return; + } + final Direction direction = tryExtractDirection(getBlockState()); + if (direction == null) { + return; + } + final AutocrafterBlockEntity root = getChainingRoot(); + final BlockEntity connectedMachine = root.getConnectedMachine(); + if (connectedMachine == null) { + invalidateExternalPatternInputSinkKey(); + return; + } + final BlockState state = connectedMachine.getBlockState(); + final Player fakePlayer = getFakePlayer(serverLevel); + final ItemStack stack = Platform.INSTANCE.getBlockAsItemStack( + state.getBlock(), + state, + direction.getOpposite(), + serverLevel, + connectedMachine.getBlockPos(), + fakePlayer + ); + externalPatternInputSinkKey = new InWorldExternalPatternInputSinkKey( + getName().getString(), + stack + ); + } + + private void invalidateExternalPatternInputSinkKey() { + externalPatternInputSinkKey = null; + } } diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/InWorldExternalPatternInputSinkKey.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/InWorldExternalPatternInputSinkKey.java new file mode 100644 index 000000000..76eae18c6 --- /dev/null +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/autocrafter/InWorldExternalPatternInputSinkKey.java @@ -0,0 +1,12 @@ +package com.refinedmods.refinedstorage.common.autocrafting.autocrafter; + +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; + +import net.minecraft.world.item.ItemStack; + +public record InWorldExternalPatternInputSinkKey(String name, ItemStack stack) implements ExternalPatternInputSinkKey { + @Override + public String getName() { + return name; + } +} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorItemTooltip.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorItemTooltip.java new file mode 100644 index 000000000..6aeaa71ef --- /dev/null +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorItemTooltip.java @@ -0,0 +1,100 @@ +package com.refinedmods.refinedstorage.common.autocrafting.monitor; + +import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.common.api.RefinedStorageClientApi; +import com.refinedmods.refinedstorage.common.api.support.resource.ResourceRendering; +import com.refinedmods.refinedstorage.common.autocrafting.autocrafter.InWorldExternalPatternInputSinkKey; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.item.ItemStack; + +import static com.refinedmods.refinedstorage.common.util.IdentifierUtil.createTranslation; + +class AutocraftingMonitorItemTooltip implements ClientTooltipComponent { + private static final int SPACING = 2; + + private static final MutableComponent MACHINE_DOES_NOT_ACCEPT_RESOURCE = createTranslation( + "gui", + "autocrafting_monitor.machine_does_not_accept_resource" + ).withStyle(ChatFormatting.RED); + private static final MutableComponent NO_MACHINE_FOUND = createTranslation( + "gui", + "autocrafting_monitor.no_machine_found" + ).withStyle(ChatFormatting.RED); + private static final MutableComponent AUTOCRAFTER_IS_LOCKED = createTranslation( + "gui", + "autocrafting_monitor.autocrafter_is_locked" + ).withStyle(ChatFormatting.RED); + + private final TaskStatus.Item item; + private final ResourceRendering rendering; + + AutocraftingMonitorItemTooltip(final TaskStatus.Item item) { + this.item = item; + this.rendering = RefinedStorageClientApi.INSTANCE.getResourceRendering(item.resource().getClass()); + } + + @Override + public void renderImage(final Font font, final int x, final int y, final GuiGraphics graphics) { + int yy = y; + graphics.drawString( + font, + rendering.getDisplayName(item.resource()), + x, + yy, + 0xFFFFFF + ); + yy += 9 + SPACING; + if (item.type() != TaskStatus.ItemType.NORMAL) { + graphics.drawString( + font, + getErrorTooltip(item.type()), + x, + yy, + 0xAAAAAA + ); + yy += 9 + SPACING; + } + if (item.sinkKey() instanceof InWorldExternalPatternInputSinkKey(String name, ItemStack stack)) { + graphics.renderItem(stack, x, yy); + graphics.drawString( + font, + name, + x + 18 + SPACING, + yy + 4, + 0xAAAAAA + ); + } + } + + @Override + public int getHeight() { + return 9 + SPACING + + (item.type() != TaskStatus.ItemType.NORMAL ? 9 + SPACING : 0) + + (item.sinkKey() != null ? 18 : 0); + } + + @Override + public int getWidth(final Font font) { + final int resourceWidth = font.width(rendering.getDisplayName(item.resource())); + final int errorWidth = item.type() != TaskStatus.ItemType.NORMAL ? font.width(getErrorTooltip(item.type())) : 0; + final int sinkWidth = item.sinkKey() != null + ? (18 + SPACING + font.width(item.sinkKey().getName())) + : 0; + return Math.max(resourceWidth, Math.max(errorWidth, sinkWidth)); + } + + private Component getErrorTooltip(final TaskStatus.ItemType type) { + return switch (type) { + case REJECTED -> MACHINE_DOES_NOT_ACCEPT_RESOURCE; + case NONE_FOUND -> NO_MACHINE_FOUND; + case LOCKED -> AUTOCRAFTER_IS_LOCKED; + default -> Component.empty(); + }; + } +} diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorScreen.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorScreen.java index a05169f2e..79f44909b 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorScreen.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorScreen.java @@ -12,17 +12,14 @@ import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import javax.annotation.Nullable; -import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.client.renderer.Rect2i; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.FormattedCharSequence; import net.minecraft.world.entity.player.Inventory; import static com.refinedmods.refinedstorage.common.support.Sprites.ERROR; @@ -50,19 +47,6 @@ public class AutocraftingMonitorScreen extends AbstractBaseScreen getItemTooltip(final TaskStatus.Item item, final ResourceRendering rendering) { - final List tooltip = rendering.getTooltip(item.resource()).stream() - .map(Component::getVisualOrderText) - .collect(Collectors.toList()); - if (item.type() != TaskStatus.ItemType.NORMAL) { - tooltip.add(getErrorTooltip(item.type()).getVisualOrderText()); - } - return tooltip; - } - - private Component getErrorTooltip(final TaskStatus.ItemType type) { - return switch (type) { - case REJECTED -> MACHINE_DOES_NOT_ACCEPT_RESOURCE; - case NONE_FOUND -> NO_MACHINE_FOUND; - case LOCKED -> AUTOCRAFTER_IS_LOCKED; - default -> Component.empty(); - }; - } - private void renderItemText(final GuiGraphics graphics, final TaskStatus.Item item, final ResourceRendering rendering, diff --git a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorStreamCodecs.java b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorStreamCodecs.java index 4d067adac..4621758a8 100644 --- a/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorStreamCodecs.java +++ b/refinedstorage-common/src/main/java/com/refinedmods/refinedstorage/common/autocrafting/monitor/AutocraftingMonitorStreamCodecs.java @@ -1,18 +1,22 @@ package com.refinedmods.refinedstorage.common.autocrafting.monitor; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.common.api.support.resource.PlatformResourceKey; +import com.refinedmods.refinedstorage.common.autocrafting.autocrafter.InWorldExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.common.support.resource.ResourceCodecs; import com.refinedmods.refinedstorage.common.util.PlatformUtil; import java.util.ArrayList; +import javax.annotation.Nullable; import io.netty.buffer.ByteBuf; import net.minecraft.core.UUIDUtil; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.ItemStack; public final class AutocraftingMonitorStreamCodecs { public static final StreamCodec TASK_ID_STREAM_CODEC = StreamCodec.composite( @@ -50,6 +54,7 @@ public TaskStatus.Item decode(final RegistryFriendlyByteBuf buf) { return new TaskStatus.Item( ResourceCodecs.STREAM_CODEC.decode(buf), TYPE_STREAM_CODEC.decode(buf), + decodeSinkKey(buf), buf.readLong(), buf.readLong(), buf.readLong(), @@ -57,14 +62,34 @@ public TaskStatus.Item decode(final RegistryFriendlyByteBuf buf) { ); } + @Nullable + private ExternalPatternInputSinkKey decodeSinkKey(final RegistryFriendlyByteBuf buf) { + if (buf.readBoolean()) { + return new InWorldExternalPatternInputSinkKey(buf.readUtf(), ItemStack.STREAM_CODEC.decode(buf)); + } + return null; + } + @Override public void encode(final RegistryFriendlyByteBuf buf, final TaskStatus.Item item) { ResourceCodecs.STREAM_CODEC.encode(buf, (PlatformResourceKey) item.resource()); TYPE_STREAM_CODEC.encode(buf, item.type()); + encodeSinkKey(buf, item.sinkKey()); buf.writeLong(item.stored()); buf.writeLong(item.processing()); buf.writeLong(item.scheduled()); buf.writeLong(item.crafting()); } + + private void encodeSinkKey(final RegistryFriendlyByteBuf buf, + @Nullable final ExternalPatternInputSinkKey sinkKey) { + if (sinkKey instanceof InWorldExternalPatternInputSinkKey(String name, ItemStack stack)) { + buf.writeBoolean(true); + buf.writeUtf(name); + ItemStack.STREAM_CODEC.encode(buf, stack); + } else { + buf.writeBoolean(false); + } + } } } diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java index 53a24e4fa..d22a300db 100644 --- a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProvider.java @@ -1,12 +1,15 @@ package com.refinedmods.refinedstorage.api.network.autocrafting; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.StepBehavior; import com.refinedmods.refinedstorage.api.autocrafting.task.Task; import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import java.util.List; +import javax.annotation.Nullable; + import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.8") @@ -24,4 +27,7 @@ default boolean contains(AutocraftingNetworkComponent component) { void cancelTask(TaskId taskId); List getTaskStatuses(); + + @Nullable + ExternalPatternInputSinkKey getSinkKey(); } diff --git a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java index 3417e17d8..83207755c 100644 --- a/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java +++ b/refinedstorage-network-api/src/main/java/com/refinedmods/refinedstorage/api/network/autocrafting/PatternProviderExternalPatternInputSink.java @@ -9,7 +9,6 @@ import org.apiguardian.api.API; @API(status = API.Status.STABLE, since = "2.0.0-milestone.4.12") -@FunctionalInterface public interface PatternProviderExternalPatternInputSink { ExternalPatternInputSink.Result accept(Collection resources, Action action); } 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 1d96e95d9..b2d9e7e31 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 @@ -8,6 +8,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewCraftingCalculatorListener; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.Task; import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.autocrafting.task.TaskImpl; @@ -239,4 +240,14 @@ public Result accept(final Pattern pattern, final Collection res } return patternProvider.accept(resources, action); } + + @Nullable + @Override + public ExternalPatternInputSinkKey getKey(final Pattern pattern) { + final PatternProvider patternProvider = providerByPattern.get(pattern); + if (patternProvider == null) { + return null; + } + return patternProvider.getSinkKey(); + } } diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/ExternalPatternInputSinkKeyProvider.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/ExternalPatternInputSinkKeyProvider.java new file mode 100644 index 000000000..a5261d316 --- /dev/null +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/ExternalPatternInputSinkKeyProvider.java @@ -0,0 +1,11 @@ +package com.refinedmods.refinedstorage.api.network.impl.node.patternprovider; + +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; + +import javax.annotation.Nullable; + +@FunctionalInterface +public interface ExternalPatternInputSinkKeyProvider { + @Nullable + ExternalPatternInputSinkKey getKey(); +} diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java index 0584a9bbd..641d8fd3d 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/patternprovider/PatternProviderNetworkNode.java @@ -3,6 +3,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSink; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.StepBehavior; import com.refinedmods.refinedstorage.api.autocrafting.task.Task; import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; @@ -33,6 +34,8 @@ public class PatternProviderNetworkNode extends SimpleNetworkNode implements Pat private int priority; @Nullable private PatternProviderExternalPatternInputSink externalPatternInputSink; + @Nullable + private ExternalPatternInputSinkKeyProvider externalPatternInputSinkKeyProvider; private StepBehavior stepBehavior = StepBehavior.DEFAULT; public PatternProviderNetworkNode(final long energyUsage, final int patterns) { @@ -137,6 +140,12 @@ public List getTaskStatuses() { return tasks.stream().map(Task::getStatus).toList(); } + @Override + @Nullable + public ExternalPatternInputSinkKey getSinkKey() { + return externalPatternInputSinkKeyProvider != null ? externalPatternInputSinkKeyProvider.getKey() : null; + } + private void setupTask(final Task task, final StorageNetworkComponent storage) { storage.addListener(task); } @@ -196,4 +205,8 @@ public void setPriority(final int priority) { public void setStepBehavior(final StepBehavior stepBehavior) { this.stepBehavior = stepBehavior; } + + public void setSinkKeyProvider(final ExternalPatternInputSinkKeyProvider provider) { + this.externalPatternInputSinkKeyProvider = provider; + } } diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java index 016318bea..66fe43a3f 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputNetworkNode.java @@ -2,6 +2,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSink; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.Task; import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.core.Action; @@ -184,4 +185,10 @@ public void onRemovedFromContainer(final ParentContainer parentContainer) { public ExternalPatternInputSink.Result accept(final Collection resources, final Action action) { return ExternalPatternInputSink.Result.SKIPPED; // TODO(feat): relay support } + + @Nullable + @Override + public ExternalPatternInputSinkKey getSinkKey() { + return null; // TODO(feat): relay support + } } diff --git a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java index 1a78e7ebd..82aa4de5b 100644 --- a/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java +++ b/refinedstorage-network/src/main/java/com/refinedmods/refinedstorage/api/network/impl/node/relay/RelayOutputPatternProvider.java @@ -3,6 +3,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.Pattern; import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSink; +import com.refinedmods.refinedstorage.api.autocrafting.task.ExternalPatternInputSinkKey; import com.refinedmods.refinedstorage.api.autocrafting.task.Task; import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; import com.refinedmods.refinedstorage.api.core.Action; @@ -132,4 +133,10 @@ public void onRemovedFromContainer(final ParentContainer parentContainer) { public ExternalPatternInputSink.Result accept(final Collection resources, final Action action) { return ExternalPatternInputSink.Result.SKIPPED; // TODO(feat): relay support } + + @Nullable + @Override + public ExternalPatternInputSinkKey getSinkKey() { + return null; // TODO(feat): relay support + } }