From c72d1cc09cf7f62d25018dced63b6533544dae5b Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sat, 30 Nov 2024 18:32:23 +0100 Subject: [PATCH] added a setting in the crafter to emit a redstone signal when all items have arrived --- .../prettypipes/packets/PacketButton.java | 6 +++ .../ellpeck/prettypipes/pipe/PipeBlock.java | 6 +++ .../prettypipes/pipe/PipeBlockEntity.java | 18 ++++++- .../craft/CraftingModuleContainer.java | 4 +- .../pipe/modules/craft/CraftingModuleGui.java | 7 +++ .../modules/craft/CraftingModuleItem.java | 50 +++++++++++-------- .../assets/prettypipes/lang/en_us.json | 3 ++ 7 files changed, 70 insertions(+), 24 deletions(-) diff --git a/src/main/java/de/ellpeck/prettypipes/packets/PacketButton.java b/src/main/java/de/ellpeck/prettypipes/packets/PacketButton.java index 3b556e5..9548240 100644 --- a/src/main/java/de/ellpeck/prettypipes/packets/PacketButton.java +++ b/src/main/java/de/ellpeck/prettypipes/packets/PacketButton.java @@ -107,6 +107,12 @@ public record PacketButton(BlockPos pos, int result, List data) impleme container.modified = true; } }), + EMIT_REDSTONE_BUTTON((pos, data, player) -> { + if (player.containerMenu instanceof CraftingModuleContainer container) { + container.emitRedstone = !container.emitRedstone; + container.modified = true; + } + }), STACK_SIZE_MODULE_BUTTON((pos, data, player) -> { var container = (AbstractPipeContainer) player.containerMenu; var moduleData = container.moduleStack.getOrDefault(StackSizeModuleItem.Data.TYPE, StackSizeModuleItem.Data.DEFAULT); diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/PipeBlock.java b/src/main/java/de/ellpeck/prettypipes/pipe/PipeBlock.java index 1d29bb5..9e108b3 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/PipeBlock.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/PipeBlock.java @@ -298,6 +298,12 @@ public class PipeBlock extends BaseEntityBlock implements SimpleWaterloggedBlock return Math.min(15, pipe.getItems().size()); } + @Override + protected int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) { + var pipe = Utility.getBlockEntity(PipeBlockEntity.class, blockAccess, pos); + return pipe.redstoneTicks > 0 ? 15 : 0; + } + @org.jetbrains.annotations.Nullable @Override public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/PipeBlockEntity.java b/src/main/java/de/ellpeck/prettypipes/pipe/PipeBlockEntity.java index 16f6f76..09bb625 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/PipeBlockEntity.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/PipeBlockEntity.java @@ -70,14 +70,18 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC PipeBlockEntity.this.setChanged(); } }; + public PressurizerBlockEntity pressurizer; public BlockState cover; public int moduleDropCheck; + public int redstoneTicks; + + private final Lazy workRandomizer = Lazy.of(() -> this.level.random.nextInt(200)); + private List itemCache; private List activeCraftCache; private int lastItemAmount; private int priority; - private final Lazy workRandomizer = Lazy.of(() -> this.level.random.nextInt(200)); public PipeBlockEntity(BlockPos pos, BlockState state) { super(Registry.pipeBlockEntity, pos, state); @@ -99,6 +103,7 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC compound.putInt("module_drop_check", this.moduleDropCheck); if (this.cover != null) compound.put("cover", NbtUtils.writeBlockState(this.cover)); + compound.putInt("priority", this.priority); } @Override @@ -106,6 +111,7 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC this.modules.deserializeNBT(provider, compound.getCompound("modules")); this.moduleDropCheck = compound.getInt("module_drop_check"); this.cover = compound.contains("cover") ? NbtUtils.readBlockState(this.level != null ? this.level.holderLookup(Registries.BLOCK) : BuiltInRegistries.BLOCK.asLookup(), compound.getCompound("cover")) : null; + this.priority = compound.getInt("priority"); super.loadAdditional(compound, provider); } @@ -404,6 +410,10 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC return stack; } + public void setRedstoneTicks(int ticks) { + this.redstoneTicks = ticks; + } + @Override public Component getDisplayName() { return Component.translatable("container." + PrettyPipes.ID + ".pipe"); @@ -433,6 +443,12 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC if (pipe.pressurizer != null && pipe.pressurizer.isRemoved()) pipe.pressurizer = null; + if (pipe.redstoneTicks > 0) { + pipe.redstoneTicks--; + if (pipe.redstoneTicks <= 0) + level.updateNeighborsAt(pos, state.getBlock()); + } + if (!pipe.level.isAreaLoaded(pipe.worldPosition, 1)) return; var profiler = pipe.level.getProfiler(); diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleContainer.java b/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleContainer.java index 3136949..1aa1552 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleContainer.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleContainer.java @@ -17,6 +17,7 @@ public class CraftingModuleContainer extends AbstractPipeContainer type, int id, Player player, BlockPos pos, int moduleIndex) { @@ -28,6 +29,7 @@ public class CraftingModuleContainer extends AbstractPipeContainer button.setMessage(Component.translatable(singleText.get())); }).bounds(this.leftPos + this.imageWidth - 7 - 20 - 22, this.topPos + 17 + 32 + 18 * 2 + 2, 20, 20).tooltip( Tooltip.create(Component.translatable("info." + PrettyPipes.ID + ".insert_singles.description").withStyle(ChatFormatting.GRAY))).build()); + + var redstoneText = (Supplier) () -> "info." + PrettyPipes.ID + ".emit_redstone_" + (this.menu.emitRedstone ? "on" : "off"); + this.addRenderableWidget(Button.builder(Component.translatable(redstoneText.get()), button -> { + PacketButton.sendAndExecute(this.menu.tile.getBlockPos(), PacketButton.ButtonResult.EMIT_REDSTONE_BUTTON, List.of()); + button.setMessage(Component.translatable(redstoneText.get())); + }).bounds(this.leftPos + 7, this.topPos + 17 + 32 + 18 * 2 + 2, 20, 20).tooltip( + Tooltip.create(Component.translatable("info." + PrettyPipes.ID + ".emit_redstone.description").withStyle(ChatFormatting.GRAY))).build()); } } diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleItem.java b/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleItem.java index 34d5488..badf575 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleItem.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleItem.java @@ -35,7 +35,7 @@ public class CraftingModuleItem extends ModuleItem { private final int speed; public CraftingModuleItem(String name, ModuleTier tier) { - super(name, new Properties().component(Contents.TYPE, new Contents(new ItemStackHandler(tier.forTier(1, 4, 9)), new ItemStackHandler(tier.forTier(1, 2, 4)), false, false))); + super(name, new Properties().component(Contents.TYPE, new Contents(new ItemStackHandler(tier.forTier(1, 4, 9)), new ItemStackHandler(tier.forTier(1, 2, 4)), false, false, false))); this.speed = tier.forTier(20, 10, 5); } @@ -172,6 +172,7 @@ public class CraftingModuleItem extends ModuleItem { if (craftableAmount <= 0) return Pair.of(stack, List.of()); var slot = tile.getModuleSlot(module); + var contents = module.get(Contents.TYPE); var network = PipeNetwork.get(tile.getLevel()); var items = network.getOrderedNetworkItems(tile.getBlockPos()); @@ -183,11 +184,10 @@ public class CraftingModuleItem extends ModuleItem { var craftableCrafts = Mth.ceil(craftableAmount / (float) resultAmount); var toCraft = Math.min(craftableCrafts, requiredCrafts); - var locks = new ArrayList(); - var crafts = new ArrayList(); - var contents = module.get(Contents.TYPE); + var allCrafts = new ArrayList(); // if we're ensuring item order, all items for a single recipe should be sent in order first before starting on the next one! for (var c = contents.ensureItemOrder ? toCraft : 1; c > 0; c--) { + var locks = new ArrayList(); for (var i = 0; i < contents.input.getSlots(); i++) { var in = contents.input.getStackInSlot(i); if (in.isEmpty()) @@ -196,29 +196,27 @@ public class CraftingModuleItem extends ModuleItem { if (!contents.ensureItemOrder) copy.setCount(in.getCount() * toCraft); var ret = network.requestLocksAndStartCrafting(tile.getBlockPos(), items, unavailableConsumer, copy, CraftingModuleItem.addDependency(dependencyChain, module), equalityTypes); + // set crafting dependencies as in progress immediately so that, when canceling, they don't leave behind half-crafted inbetween dependencies + // TODO to be more optimal, we should really do this when setting the main craft as in progress, but that would require storing references to all of the dependencies + ret.getRight().forEach(a -> a.inProgress = true); locks.addAll(ret.getLeft()); - crafts.addAll(ret.getRight()); + allCrafts.addAll(ret.getRight()); } + var result = stack.copyWithCount(contents.ensureItemOrder ? resultAmount : resultAmount * toCraft); + var activeCraft = new ActiveCraft(tile.getBlockPos(), slot, locks, destPipe, result); + tile.getActiveCrafts().add(activeCraft); + allCrafts.add(activeCraft); } - // set crafting dependencies as in progress immediately so that, when canceling, they don't leave behind half-crafted inbetween dependencies - // TODO to be more optimal, we should really do this when setting the main craft as in progress, but that would require storing references to all of the dependencies - crafts.forEach(c -> c.inProgress = true); var remain = stack.copy(); remain.shrink(resultAmount * toCraft); - var result = stack.copy(); - result.shrink(remain.getCount()); - - var activeCraft = new ActiveCraft(tile.getBlockPos(), slot, locks, destPipe, result); - tile.getActiveCrafts().add(activeCraft); - crafts.add(activeCraft); - - return Pair.of(remain, crafts); + return Pair.of(remain, allCrafts); } @Override public ItemStack store(ItemStack module, PipeBlockEntity tile, ItemStack stack, Direction direction) { var slot = tile.getModuleSlot(module); + var contents = module.get(Contents.TYPE); var equalityTypes = ItemFilter.getEqualityTypes(tile); var crafts = tile.getActiveCrafts(); var craft = crafts.stream() @@ -227,7 +225,7 @@ public class CraftingModuleItem extends ModuleItem { if (craft != null) { craft.travelingIngredients.remove(craft.getTravelingIngredient(stack, equalityTypes)); - if (module.get(Contents.TYPE).insertSingles) { + if (contents.insertSingles) { var handler = tile.getItemHandler(direction); if (handler != null) { while (!stack.isEmpty()) { @@ -239,9 +237,16 @@ public class CraftingModuleItem extends ModuleItem { } } - // if we canceled the request and all input items are delivered (ie the machine actually got what it expected), remove it from the queue - if (craft.canceled && craft.travelingIngredients.size() <= 0 && craft.ingredientsToRequest.size() <= 0) - crafts.remove(craft); + if (craft.travelingIngredients.size() <= 0 && craft.ingredientsToRequest.size() <= 0) { + if (contents.emitRedstone) { + tile.redstoneTicks = 5; + tile.getLevel().updateNeighborsAt(tile.getBlockPos(), tile.getBlockState().getBlock()); + } + + // if we canceled the request and all input items are delivered (ie the machine actually got what it expected), remove it from the queue + if (craft.canceled) + crafts.remove(craft); + } } return stack; } @@ -264,13 +269,14 @@ public class CraftingModuleItem extends ModuleItem { return deps; } - public record Contents(ItemStackHandler input, ItemStackHandler output, boolean ensureItemOrder, boolean insertSingles) { + public record Contents(ItemStackHandler input, ItemStackHandler output, boolean ensureItemOrder, boolean insertSingles, boolean emitRedstone) { public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( Utility.ITEM_STACK_HANDLER_CODEC.fieldOf("input").forGetter(d -> d.input), Utility.ITEM_STACK_HANDLER_CODEC.fieldOf("output").forGetter(d -> d.output), Codec.BOOL.optionalFieldOf("ensure_item_order", false).forGetter(d -> d.ensureItemOrder), - Codec.BOOL.optionalFieldOf("insert_singles", false).forGetter(d -> d.insertSingles) + Codec.BOOL.optionalFieldOf("insert_singles", false).forGetter(d -> d.insertSingles), + Codec.BOOL.optionalFieldOf("emit_redstone", false).forGetter(d -> d.emitRedstone) ).apply(i, Contents::new)); public static final DataComponentType TYPE = DataComponentType.builder().persistent(Contents.CODEC).cacheEncoding().build(); diff --git a/src/main/resources/assets/prettypipes/lang/en_us.json b/src/main/resources/assets/prettypipes/lang/en_us.json index bfab943..4b30b2a 100644 --- a/src/main/resources/assets/prettypipes/lang/en_us.json +++ b/src/main/resources/assets/prettypipes/lang/en_us.json @@ -72,6 +72,9 @@ "info.prettypipes.ensure_item_order_on": "\u00A72O", "info.prettypipes.ensure_item_order_off": "\u00A74\u00A7mO", "info.prettypipes.ensure_item_order.description": "Whether the module should wait for items to be inserted in the order they appear in the input slots\n\u00A7oRecommended for use with the Crafter", + "info.prettypipes.emit_redstone_on": "\u00A72R", + "info.prettypipes.emit_redstone_off": "\u00A74\u00A7mR", + "info.prettypipes.emit_redstone.description": "Whether a redstone signal should be emitted when all crafting items have arrived\nIf the module waits for items to be inserted in order, a redstone signal is emitted for each set of items required for a single craft\n\u00A7oRecommended for use with the Crafter", "info.prettypipes.shift": "Hold Shift for info", "info.prettypipes.populate": "P", "info.prettypipes.populate.description": "Populate filter slots with items from adjacent inventories",