added a setting in the crafter to emit a redstone signal when all items have arrived

This commit is contained in:
Ell 2024-11-30 18:32:23 +01:00
parent cee38be5cd
commit c72d1cc09c
7 changed files with 70 additions and 24 deletions

View file

@ -107,6 +107,12 @@ public record PacketButton(BlockPos pos, int result, List<Integer> data) impleme
container.modified = true; 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) -> { STACK_SIZE_MODULE_BUTTON((pos, data, player) -> {
var container = (AbstractPipeContainer<?>) player.containerMenu; var container = (AbstractPipeContainer<?>) player.containerMenu;
var moduleData = container.moduleStack.getOrDefault(StackSizeModuleItem.Data.TYPE, StackSizeModuleItem.Data.DEFAULT); var moduleData = container.moduleStack.getOrDefault(StackSizeModuleItem.Data.TYPE, StackSizeModuleItem.Data.DEFAULT);

View file

@ -298,6 +298,12 @@ public class PipeBlock extends BaseEntityBlock implements SimpleWaterloggedBlock
return Math.min(15, pipe.getItems().size()); 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 @org.jetbrains.annotations.Nullable
@Override @Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {

View file

@ -70,14 +70,18 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
PipeBlockEntity.this.setChanged(); PipeBlockEntity.this.setChanged();
} }
}; };
public PressurizerBlockEntity pressurizer; public PressurizerBlockEntity pressurizer;
public BlockState cover; public BlockState cover;
public int moduleDropCheck; public int moduleDropCheck;
public int redstoneTicks;
private final Lazy<Integer> workRandomizer = Lazy.of(() -> this.level.random.nextInt(200));
private List<IPipeItem> itemCache; private List<IPipeItem> itemCache;
private List<ActiveCraft> activeCraftCache; private List<ActiveCraft> activeCraftCache;
private int lastItemAmount; private int lastItemAmount;
private int priority; private int priority;
private final Lazy<Integer> workRandomizer = Lazy.of(() -> this.level.random.nextInt(200));
public PipeBlockEntity(BlockPos pos, BlockState state) { public PipeBlockEntity(BlockPos pos, BlockState state) {
super(Registry.pipeBlockEntity, pos, 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); compound.putInt("module_drop_check", this.moduleDropCheck);
if (this.cover != null) if (this.cover != null)
compound.put("cover", NbtUtils.writeBlockState(this.cover)); compound.put("cover", NbtUtils.writeBlockState(this.cover));
compound.putInt("priority", this.priority);
} }
@Override @Override
@ -106,6 +111,7 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
this.modules.deserializeNBT(provider, compound.getCompound("modules")); this.modules.deserializeNBT(provider, compound.getCompound("modules"));
this.moduleDropCheck = compound.getInt("module_drop_check"); 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.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); super.loadAdditional(compound, provider);
} }
@ -404,6 +410,10 @@ public class PipeBlockEntity extends BlockEntity implements MenuProvider, IPipeC
return stack; return stack;
} }
public void setRedstoneTicks(int ticks) {
this.redstoneTicks = ticks;
}
@Override @Override
public Component getDisplayName() { public Component getDisplayName() {
return Component.translatable("container." + PrettyPipes.ID + ".pipe"); 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()) if (pipe.pressurizer != null && pipe.pressurizer.isRemoved())
pipe.pressurizer = null; 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)) if (!pipe.level.isAreaLoaded(pipe.worldPosition, 1))
return; return;
var profiler = pipe.level.getProfiler(); var profiler = pipe.level.getProfiler();

View file

@ -17,6 +17,7 @@ public class CraftingModuleContainer extends AbstractPipeContainer<CraftingModul
public ItemStackHandler output; public ItemStackHandler output;
public boolean ensureItemOrder; public boolean ensureItemOrder;
public boolean insertSingles; public boolean insertSingles;
public boolean emitRedstone;
public boolean modified; public boolean modified;
public CraftingModuleContainer(MenuType<?> type, int id, Player player, BlockPos pos, int moduleIndex) { public CraftingModuleContainer(MenuType<?> type, int id, Player player, BlockPos pos, int moduleIndex) {
@ -28,6 +29,7 @@ public class CraftingModuleContainer extends AbstractPipeContainer<CraftingModul
var contents = this.moduleStack.get(CraftingModuleItem.Contents.TYPE); var contents = this.moduleStack.get(CraftingModuleItem.Contents.TYPE);
this.ensureItemOrder = contents.ensureItemOrder(); this.ensureItemOrder = contents.ensureItemOrder();
this.insertSingles = contents.insertSingles(); this.insertSingles = contents.insertSingles();
this.emitRedstone = contents.emitRedstone();
this.input = Utility.copy(contents.input()); this.input = Utility.copy(contents.input());
for (var i = 0; i < this.input.getSlots(); i++) { for (var i = 0; i < this.input.getSlots(); i++) {
@ -57,7 +59,7 @@ public class CraftingModuleContainer extends AbstractPipeContainer<CraftingModul
public void removed(Player playerIn) { public void removed(Player playerIn) {
super.removed(playerIn); super.removed(playerIn);
if (this.modified) { if (this.modified) {
this.moduleStack.set(CraftingModuleItem.Contents.TYPE, new CraftingModuleItem.Contents(this.input, this.output, this.ensureItemOrder, this.insertSingles)); this.moduleStack.set(CraftingModuleItem.Contents.TYPE, new CraftingModuleItem.Contents(this.input, this.output, this.ensureItemOrder, this.insertSingles, this.emitRedstone));
this.tile.setChanged(); this.tile.setChanged();
} }
} }

View file

@ -41,6 +41,13 @@ public class CraftingModuleGui extends AbstractPipeGui<CraftingModuleContainer>
button.setMessage(Component.translatable(singleText.get())); button.setMessage(Component.translatable(singleText.get()));
}).bounds(this.leftPos + this.imageWidth - 7 - 20 - 22, this.topPos + 17 + 32 + 18 * 2 + 2, 20, 20).tooltip( }).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()); Tooltip.create(Component.translatable("info." + PrettyPipes.ID + ".insert_singles.description").withStyle(ChatFormatting.GRAY))).build());
var redstoneText = (Supplier<String>) () -> "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());
} }
} }

View file

@ -35,7 +35,7 @@ public class CraftingModuleItem extends ModuleItem {
private final int speed; private final int speed;
public CraftingModuleItem(String name, ModuleTier tier) { 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); this.speed = tier.forTier(20, 10, 5);
} }
@ -172,6 +172,7 @@ public class CraftingModuleItem extends ModuleItem {
if (craftableAmount <= 0) if (craftableAmount <= 0)
return Pair.of(stack, List.of()); return Pair.of(stack, List.of());
var slot = tile.getModuleSlot(module); var slot = tile.getModuleSlot(module);
var contents = module.get(Contents.TYPE);
var network = PipeNetwork.get(tile.getLevel()); var network = PipeNetwork.get(tile.getLevel());
var items = network.getOrderedNetworkItems(tile.getBlockPos()); var items = network.getOrderedNetworkItems(tile.getBlockPos());
@ -183,11 +184,10 @@ public class CraftingModuleItem extends ModuleItem {
var craftableCrafts = Mth.ceil(craftableAmount / (float) resultAmount); var craftableCrafts = Mth.ceil(craftableAmount / (float) resultAmount);
var toCraft = Math.min(craftableCrafts, requiredCrafts); var toCraft = Math.min(craftableCrafts, requiredCrafts);
var locks = new ArrayList<NetworkLock>(); var allCrafts = new ArrayList<ActiveCraft>();
var crafts = new ArrayList<ActiveCraft>();
var contents = module.get(Contents.TYPE);
// if we're ensuring item order, all items for a single recipe should be sent in order first before starting on the next one! // 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--) { for (var c = contents.ensureItemOrder ? toCraft : 1; c > 0; c--) {
var locks = new ArrayList<NetworkLock>();
for (var i = 0; i < contents.input.getSlots(); i++) { for (var i = 0; i < contents.input.getSlots(); i++) {
var in = contents.input.getStackInSlot(i); var in = contents.input.getStackInSlot(i);
if (in.isEmpty()) if (in.isEmpty())
@ -196,29 +196,27 @@ public class CraftingModuleItem extends ModuleItem {
if (!contents.ensureItemOrder) if (!contents.ensureItemOrder)
copy.setCount(in.getCount() * toCraft); copy.setCount(in.getCount() * toCraft);
var ret = network.requestLocksAndStartCrafting(tile.getBlockPos(), items, unavailableConsumer, copy, CraftingModuleItem.addDependency(dependencyChain, module), equalityTypes); 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()); 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(); var remain = stack.copy();
remain.shrink(resultAmount * toCraft); remain.shrink(resultAmount * toCraft);
var result = stack.copy(); return Pair.of(remain, allCrafts);
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);
} }
@Override @Override
public ItemStack store(ItemStack module, PipeBlockEntity tile, ItemStack stack, Direction direction) { public ItemStack store(ItemStack module, PipeBlockEntity tile, ItemStack stack, Direction direction) {
var slot = tile.getModuleSlot(module); var slot = tile.getModuleSlot(module);
var contents = module.get(Contents.TYPE);
var equalityTypes = ItemFilter.getEqualityTypes(tile); var equalityTypes = ItemFilter.getEqualityTypes(tile);
var crafts = tile.getActiveCrafts(); var crafts = tile.getActiveCrafts();
var craft = crafts.stream() var craft = crafts.stream()
@ -227,7 +225,7 @@ public class CraftingModuleItem extends ModuleItem {
if (craft != null) { if (craft != null) {
craft.travelingIngredients.remove(craft.getTravelingIngredient(stack, equalityTypes)); craft.travelingIngredients.remove(craft.getTravelingIngredient(stack, equalityTypes));
if (module.get(Contents.TYPE).insertSingles) { if (contents.insertSingles) {
var handler = tile.getItemHandler(direction); var handler = tile.getItemHandler(direction);
if (handler != null) { if (handler != null) {
while (!stack.isEmpty()) { 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.travelingIngredients.size() <= 0 && craft.ingredientsToRequest.size() <= 0) {
if (craft.canceled && craft.travelingIngredients.size() <= 0 && craft.ingredientsToRequest.size() <= 0) if (contents.emitRedstone) {
crafts.remove(craft); 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; return stack;
} }
@ -264,13 +269,14 @@ public class CraftingModuleItem extends ModuleItem {
return deps; 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<Contents> CODEC = RecordCodecBuilder.create(i -> i.group( public static final Codec<Contents> CODEC = RecordCodecBuilder.create(i -> i.group(
Utility.ITEM_STACK_HANDLER_CODEC.fieldOf("input").forGetter(d -> d.input), Utility.ITEM_STACK_HANDLER_CODEC.fieldOf("input").forGetter(d -> d.input),
Utility.ITEM_STACK_HANDLER_CODEC.fieldOf("output").forGetter(d -> d.output), 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("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)); ).apply(i, Contents::new));
public static final DataComponentType<Contents> TYPE = DataComponentType.<Contents>builder().persistent(Contents.CODEC).cacheEncoding().build(); public static final DataComponentType<Contents> TYPE = DataComponentType.<Contents>builder().persistent(Contents.CODEC).cacheEncoding().build();

View file

@ -72,6 +72,9 @@
"info.prettypipes.ensure_item_order_on": "\u00A72O", "info.prettypipes.ensure_item_order_on": "\u00A72O",
"info.prettypipes.ensure_item_order_off": "\u00A74\u00A7mO", "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.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.shift": "Hold Shift for info",
"info.prettypipes.populate": "P", "info.prettypipes.populate": "P",
"info.prettypipes.populate.description": "Populate filter slots with items from adjacent inventories", "info.prettypipes.populate.description": "Populate filter slots with items from adjacent inventories",