diff --git a/src/main/java/com/simibubi/create/AllPackets.java b/src/main/java/com/simibubi/create/AllPackets.java index aac0004732..45dafc873e 100644 --- a/src/main/java/com/simibubi/create/AllPackets.java +++ b/src/main/java/com/simibubi/create/AllPackets.java @@ -62,6 +62,7 @@ import com.simibubi.create.content.logistics.packagePort.PackagePortPlacementPac import com.simibubi.create.content.logistics.stockTicker.LogisticalStockRequestPacket; import com.simibubi.create.content.logistics.stockTicker.LogisticalStockResponsePacket; import com.simibubi.create.content.logistics.stockTicker.PackageOrderRequestPacket; +import com.simibubi.create.content.logistics.stockTicker.StockTickerConfigurationPacket; import com.simibubi.create.content.logistics.tunnel.TunnelFlapPacket; import com.simibubi.create.content.redstone.displayLink.DisplayLinkConfigurationPacket; import com.simibubi.create.content.redstone.link.controller.LinkedControllerBindPacket; @@ -177,6 +178,7 @@ public enum AllPackets { CHAIN_CONVEYOR_CONNECT(ChainConveyorConnectionPacket.class, ChainConveyorConnectionPacket::new, PLAY_TO_SERVER), CHAIN_CONVEYOR_RIDING(ChainConveyorRidingPacket.class, ChainConveyorRidingPacket::new, PLAY_TO_SERVER), CHAIN_PACKAGE_INTERACTION(ChainPackageInteractionPacket.class, ChainPackageInteractionPacket::new, PLAY_TO_SERVER), + STOCK_TICKER_CONFIGURATION(StockTickerConfigurationPacket.class, StockTickerConfigurationPacket::new, PLAY_TO_SERVER), // Server to Client SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT), diff --git a/src/main/java/com/simibubi/create/content/logistics/packager/PackagerBlockEntity.java b/src/main/java/com/simibubi/create/content/logistics/packager/PackagerBlockEntity.java index 454108aa54..57b228109a 100644 --- a/src/main/java/com/simibubi/create/content/logistics/packager/PackagerBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/packager/PackagerBlockEntity.java @@ -520,4 +520,27 @@ public class PackagerBlockEntity extends SmartBlockEntity { return animationTicks >= CYCLE / 2 ? ItemStack.EMPTY : heldBox; } + public boolean isTargetingSameInventory(IItemHandler inventory) { + IItemHandler myInventory = targetInventory.getInventory(); + if (myInventory == null || inventory == null) + return false; + + if (myInventory == inventory) + return true; + + // If a contained ItemStack instance is the same, we can be pretty sure these + // inventories are the same (works for compound inventories) + for (int i = 0; i < inventory.getSlots(); i++) { + ItemStack stackInSlot = inventory.getStackInSlot(i); + if (stackInSlot.isEmpty()) + continue; + for (int j = 0; j < myInventory.getSlots(); j++) + if (stackInSlot == myInventory.getStackInSlot(j)) + return true; + break; + } + + return false; + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/packagerLink/LogisticallyLinkedBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/packagerLink/LogisticallyLinkedBehaviour.java index 90eb7479ba..ad5fc93304 100644 --- a/src/main/java/com/simibubi/create/content/logistics/packagerLink/LogisticallyLinkedBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/packagerLink/LogisticallyLinkedBehaviour.java @@ -30,6 +30,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraftforge.items.IItemHandler; public class LogisticallyLinkedBehaviour extends BlockEntityBehaviour { @@ -169,9 +170,10 @@ public class LogisticallyLinkedBehaviour extends BlockEntityBehaviour { } public int processRequest(ItemStack stack, int amount, String address, int linkIndex, MutableBoolean finalLink, - int orderId, @Nullable PackageOrder orderContext) { + int orderId, @Nullable PackageOrder orderContext, @Nullable IItemHandler ignoredHandler) { if (blockEntity instanceof PackagerLinkBlockEntity plbe) - return plbe.processRequest(stack, amount, address, linkIndex, finalLink, orderId, orderContext); + return plbe.processRequest(stack, amount, address, linkIndex, finalLink, orderId, orderContext, + ignoredHandler); return 0; } diff --git a/src/main/java/com/simibubi/create/content/logistics/packagerLink/PackagerLinkBlockEntity.java b/src/main/java/com/simibubi/create/content/logistics/packagerLink/PackagerLinkBlockEntity.java index bdcb84cfad..a7953d3580 100644 --- a/src/main/java/com/simibubi/create/content/logistics/packagerLink/PackagerLinkBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/packagerLink/PackagerLinkBlockEntity.java @@ -18,6 +18,7 @@ import net.minecraft.core.Direction; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.items.IItemHandler; public class PackagerLinkBlockEntity extends LinkWithBulbBlockEntity { @@ -38,10 +39,12 @@ public class PackagerLinkBlockEntity extends LinkWithBulbBlockEntity { } public int processRequest(ItemStack stack, int amount, String address, int linkIndex, MutableBoolean finalLink, - int orderId, @Nullable PackageOrder orderContext) { + int orderId, @Nullable PackageOrder orderContext, @Nullable IItemHandler ignoredHandler) { PackagerBlockEntity packager = getPackager(); if (packager == null || packager.defragmenterActive) return 0; + if (packager.isTargetingSameInventory(ignoredHandler)) + return 0; InventorySummary summary = packager.getAvailableItems(); int availableCount = summary.getCountOf(stack); diff --git a/src/main/java/com/simibubi/create/content/logistics/stockTicker/PackageOrderRequestPacket.java b/src/main/java/com/simibubi/create/content/logistics/stockTicker/PackageOrderRequestPacket.java index 8891553ea4..43929f2591 100644 --- a/src/main/java/com/simibubi/create/content/logistics/stockTicker/PackageOrderRequestPacket.java +++ b/src/main/java/com/simibubi/create/content/logistics/stockTicker/PackageOrderRequestPacket.java @@ -38,7 +38,7 @@ public class PackageOrderRequestPacket extends BlockEntityConfigurationPacket modifiedAmounts; + + public StockTickerAutoRequestScreen(StockTickerBlockEntity be) { + super(be.getBlockState() + .getBlock() + .getName()); + blockEntity = be; + blockEntity.lastClientsideStockSnapshot = null; + modifiedAmounts = null; + } + + private void resetAmounts() { + modifiedAmounts = new ArrayList<>(); + for (IntAttached intAttached : blockEntity.restockAmounts) + modifiedAmounts.add(intAttached.getFirst()); + } + + @Override + protected void init() { + setWindowSize(256, 128); + super.init(); + + int x = guiLeft; + int y = guiTop; + + takeSnapshotButton = Button.builder(Components.literal("Take Snapshot"), this::onPress) + .bounds(x, y + 44, 100, 20) + .build(); + + MutableComponent addressLabel = CreateLang.translateDirect("gui.stock_ticker.package_adress"); + boolean initial = addressBox == null; + addressBox = new EditBox(this.font, x, y + 80, 120, 9, addressLabel); + addressBox.setMaxLength(50); + addressBox.setBordered(false); + addressBox.setTextColor(0xffffff); + if (initial) + addressBox.setValue(blockEntity.restockAddress); + addRenderableWidget(addressBox); + addRenderableWidget(takeSnapshotButton); + } + + private void onPress(Button button) { + if (button == takeSnapshotButton) { + AllPackets.getChannel() + .sendToServer( + new StockTickerConfigurationPacket(blockEntity.getBlockPos(), true, "", Collections.emptyList())); + modifiedAmounts = null; + } + } + + @Override + public void removed() { + if (modifiedAmounts != null) + for (int i = 0; i < blockEntity.restockAmounts.size(); i++) + blockEntity.restockAmounts.get(i) + .setFirst(modifiedAmounts.get(i)); + AllPackets.getChannel() + .sendToServer(new StockTickerConfigurationPacket(blockEntity.getBlockPos(), false, addressBox.getValue(), + blockEntity.restockAmounts)); + super.removed(); + } + + final int rows = 8; + final int cols = 8; + final int rowHeight = 18; + final int colWidth = 26; + + @Override + protected void renderWindow(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + if (modifiedAmounts != null && blockEntity.restockAmounts.size() != modifiedAmounts.size()) + resetAmounts(); + + Color color = new Color(255, 255, 255, 50); + int hoveredSlot = getHoveredSlot(mouseX, mouseY); + + // Render some boxes + graphics.renderOutline(addressBox.getX() - 4, addressBox.getY() - 4, addressBox.getWidth() + 12, + addressBox.getHeight() + 7, color.getRGB()); + graphics.renderOutline(guiLeft - 3, guiTop - 4 + 20, cols * colWidth + 6, rowHeight + 8, color.getRGB()); + + // Render text input hints + if (addressBox.getValue() + .isBlank()) + graphics.drawString(font, addressBox.getMessage(), addressBox.getX(), addressBox.getY(), 0x88dddddd); + + graphics.drawString(font, Components.literal("Target Amounts:"), guiLeft, guiTop, 0x88dddddd); + PoseStack ms = graphics.pose(); + + for (int i = 0; i < blockEntity.restockAmounts.size(); i++) { + IntAttached entry = blockEntity.restockAmounts.get(i); + ms.pushPose(); + + ms.translate(guiLeft + i % cols * colWidth, guiTop + 20 + i / cols * rowHeight, 200); + + int customCount = modifiedAmounts == null ? blockEntity.restockAmounts.get(i) + .getFirst() : modifiedAmounts.get(i); + drawItemCount(graphics, customCount, customCount); + ms.translate(0, 0, -200); + + float scaleFromHover = hoveredSlot == i ? 1.075f : 1; + ms.translate((colWidth - 18) / 2.0, (rowHeight - 18) / 2.0, 0); + ms.translate(18 / 2.0, 18 / 2.0, 0); + ms.scale(scaleFromHover, scaleFromHover, scaleFromHover); + ms.translate(-18 / 2.0, -18 / 2.0, 0); + GuiGameElement.of(entry.getSecond()) + .render(graphics); + ms.popPose(); + } + + if (hoveredSlot != -1) + graphics.renderTooltip(font, blockEntity.restockAmounts.get(hoveredSlot) + .getValue(), mouseX, mouseY); + } + + private int getHoveredSlot(int mouseX, int mouseY) { + if (mouseY < guiTop + 20 || mouseY > guiTop + 20 + rowHeight) + return -1; + int slot = (mouseX - guiLeft) / colWidth; + if (slot >= blockEntity.restockAmounts.size()) + return -1; + return Math.max(-1, slot); + } + + @Override + public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) { + if (modifiedAmounts == null) + resetAmounts(); + int hoveredSlot = getHoveredSlot(Mth.floor(pMouseX), Mth.floor(pMouseY)); + if (hoveredSlot != -1) { + int amount = modifiedAmounts.get(hoveredSlot); + amount += (hasShiftDown() ? 64 : 1) * Math.signum(pDelta); + if (hasShiftDown()) + amount = 64 * (amount / 64); + amount = Math.max(amount, 1); + modifiedAmounts.set(hoveredSlot, amount); + return true; + } + + return super.mouseScrolled(pMouseX, pMouseY, pDelta); + } + + private void drawItemCount(GuiGraphics graphics, int count, int customCount) { + boolean special = customCount != count; + if (!special && count == 1) + return; + + count = customCount; + String text = count >= 1000000 ? (count / 1000000) + "m" + : count >= 10000 ? (count / 1000) + "k" + : count >= 1000 ? ((count * 10) / 1000) / 10f + "k" + : count >= 100 ? count + "" : count > 0 ? " " + count : " \u2714"; + + int lightOutline = 0x444444; + int darkOutline = 0x222222; + int middleColor = special ? 0xaaffaa : 0xdddddd; + + for (int xi : Iterate.positiveAndNegative) + graphics.drawString(font, CreateLang.text(text) + .component(), 11 + xi, 10, xi < 0 ? lightOutline : darkOutline, false); + for (int yi : Iterate.positiveAndNegative) + graphics.drawString(font, CreateLang.text(text) + .component(), 11, 10 + yi, yi < 0 ? lightOutline : darkOutline, false); + graphics.drawString(font, CreateLang.text(text) + .component(), 11, 10, middleColor, false); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerBlock.java b/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerBlock.java index 31fb315306..859c8e3d52 100644 --- a/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerBlock.java @@ -6,21 +6,29 @@ import com.simibubi.create.AllPartialModels; import com.simibubi.create.AllShapes; import com.simibubi.create.foundation.block.IBE; +import net.createmod.catnip.gui.ScreenOpener; +import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.HorizontalDirectionalBlock; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition.Builder; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.DistExecutor; public class StockTickerBlock extends HorizontalDirectionalBlock implements IBE { @@ -42,6 +50,31 @@ public class StockTickerBlock extends HorizontalDirectionalBlock implements IBE< super.createBlockStateDefinition(pBuilder.add(FACING)); } + @Override + public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, + BlockHitResult pHit) { + return onBlockEntityUse(pLevel, pPos, stbe -> { + if (!stbe.observedInventory.hasInventory()) + return InteractionResult.PASS; + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> displayScreen(stbe, pPlayer)); + return InteractionResult.SUCCESS; + }); + } + + @Override + public void neighborChanged(BlockState pState, Level pLevel, BlockPos pPos, Block pNeighborBlock, + BlockPos pNeighborPos, boolean pMovedByPiston) { + if (pLevel.isClientSide()) + return; + withBlockEntityDo(pLevel, pPos, StockTickerBlockEntity::onRedstonePowerChanged); + } + + @OnlyIn(value = Dist.CLIENT) + protected void displayScreen(StockTickerBlockEntity be, Player player) { + if (player instanceof LocalPlayer) + ScreenOpener.open(new StockTickerAutoRequestScreen(be)); + } + @Override public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) { return AllShapes.STOCK_TICKER; diff --git a/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerBlockEntity.java b/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerBlockEntity.java index faa3b0d157..60f9b3c8a1 100644 --- a/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerBlockEntity.java @@ -6,27 +6,49 @@ import java.util.List; import org.apache.commons.lang3.mutable.MutableBoolean; import com.simibubi.create.AllPackets; +import com.simibubi.create.content.logistics.packager.InventorySummary; import com.simibubi.create.content.logistics.packagerLink.LogisticallyLinkedBehaviour; +import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; +import com.simibubi.create.foundation.blockEntity.behaviour.inventory.InvManipulationBehaviour; +import net.createmod.catnip.utility.BlockFace; import net.createmod.catnip.utility.IntAttached; +import net.createmod.catnip.utility.NBTHelper; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.entity.player.Player; +import net.minecraft.nbt.Tag; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.items.IItemHandler; public class StockTickerBlockEntity extends StockCheckingBlockEntity { + // Player-interface Feature protected List> lastClientsideStockSnapshot; protected List> newlyReceivedStockSnapshot; - protected String previouslyUsedAddress; protected int activeLinks; + // Auto-restock Feature + protected InvManipulationBehaviour observedInventory; + protected List> restockAmounts; + protected String restockAddress; + protected boolean powered; + public StockTickerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); previouslyUsedAddress = ""; + restockAddress = ""; + restockAmounts = new ArrayList<>(); + } + + @Override + public void addBehaviours(List behaviours) { + super.addBehaviours(behaviours); + behaviours + .add(observedInventory = new InvManipulationBehaviour(this, (w, p, s) -> new BlockFace(p, Direction.DOWN))); } public void refreshClientStockSnapshot() { @@ -53,6 +75,11 @@ public class StockTickerBlockEntity extends StockCheckingBlockEntity { protected void write(CompoundTag tag, boolean clientPacket) { super.write(tag, clientPacket); tag.putString("PreviousAddress", previouslyUsedAddress); + tag.put("RestockAmounts", + NBTHelper.writeCompoundList(restockAmounts, ia -> ia.serializeNBT(ItemStack::serializeNBT))); + tag.putString("RestockAddress", restockAddress); + tag.putBoolean("Powered", powered); + if (clientPacket) tag.putInt("ActiveLinks", activeLinks); } @@ -61,10 +88,73 @@ public class StockTickerBlockEntity extends StockCheckingBlockEntity { protected void read(CompoundTag tag, boolean clientPacket) { super.read(tag, clientPacket); previouslyUsedAddress = tag.getString("PreviousAddress"); + restockAmounts = NBTHelper.readCompoundList(tag.getList("RestockAmounts", Tag.TAG_COMPOUND), + c -> IntAttached.read(c, ItemStack::of)); + restockAddress = tag.getString("RestockAddress"); + powered = tag.getBoolean("Powered"); + if (clientPacket) activeLinks = tag.getInt("ActiveLinks"); } + protected void takeInventoryStockSnapshot() { + restockAmounts = new ArrayList<>(); + IItemHandler inventory = observedInventory.getInventory(); + if (inventory == null) + return; + restockAmounts = summariseObservedInventory().getStacksByCount(); + if (restockAmounts.size() > 8) + restockAmounts.subList(8, restockAmounts.size()) + .clear(); + notifyUpdate(); + } + + private InventorySummary summariseObservedInventory() { + IItemHandler inventory = observedInventory.getInventory(); + if (inventory == null) + return InventorySummary.EMPTY; + InventorySummary inventorySummary = new InventorySummary(); + for (int i = 0; i < inventory.getSlots(); i++) + inventorySummary.add(inventory.getStackInSlot(i)); + return inventorySummary; + } + + protected void onRedstonePowerChanged() { + boolean hasNeighborSignal = level.hasNeighborSignal(worldPosition); + if (powered == hasNeighborSignal) + return; + + if (hasNeighborSignal) + triggerRestock(); + + powered = hasNeighborSignal; + setChanged(); + } + + protected void triggerRestock() { + if (!observedInventory.hasInventory() || restockAmounts.isEmpty()) + return; + + InventorySummary presentStock = summariseObservedInventory(); + List> missingItems = new ArrayList<>(); + for (IntAttached required : restockAmounts) { + int diff = required.getFirst() - presentStock.getCountOf(required.getValue()); + if (diff > 0) + missingItems.add(IntAttached.with(diff, required.getValue())); + } + + if (missingItems.isEmpty()) + return; + + broadcastPackageRequest(new PackageOrder(missingItems), observedInventory.getInventory(), restockAddress); + } + + protected void updateAutoRestockSettings(String address, List> amounts) { + restockAmounts = amounts; + restockAddress = address; + notifyUpdate(); + } + public void receiveStockPacket(List> stacks, boolean endOfTransmission) { if (newlyReceivedStockSnapshot == null) newlyReceivedStockSnapshot = new ArrayList<>(); @@ -75,7 +165,7 @@ public class StockTickerBlockEntity extends StockCheckingBlockEntity { newlyReceivedStockSnapshot = null; } - public void receivePackageRequest(PackageOrder order, Player player, String address) { + public void broadcastPackageRequest(PackageOrder order, IItemHandler ignoredHandler, String address) { List> stacks = order.stacks(); // Packages need to track their index and successors for successful defrag @@ -87,7 +177,7 @@ public class StockTickerBlockEntity extends StockCheckingBlockEntity { PackageOrder contextToSend = order; // Packages from future orders should not be merged in the packager queue - int orderId = player.level().random.nextInt(); + int orderId = level.random.nextInt(); for (int i = 0; i < stacks.size(); i++) { IntAttached entry = stacks.get(i); @@ -103,7 +193,7 @@ public class StockTickerBlockEntity extends StockCheckingBlockEntity { isFinalLink = finalLinkTracker; int processedCount = link.processRequest(requestedItem, remainingCount, address, linkIndex, isFinalLink, - orderId, contextToSend); + orderId, contextToSend, ignoredHandler); if (processedCount > 0 && usedIndex == -1) { contextToSend = null; usedLinks.add(link); diff --git a/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerConfigurationPacket.java b/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerConfigurationPacket.java new file mode 100644 index 0000000000..60b72454a3 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/stockTicker/StockTickerConfigurationPacket.java @@ -0,0 +1,62 @@ +package com.simibubi.create.content.logistics.stockTicker; + +import java.util.ArrayList; +import java.util.List; + +import com.simibubi.create.foundation.networking.BlockEntityConfigurationPacket; + +import net.createmod.catnip.utility.IntAttached; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.item.ItemStack; + +public class StockTickerConfigurationPacket extends BlockEntityConfigurationPacket { + + private boolean takeSnapshot; + private String address; + private List> amounts; + + public StockTickerConfigurationPacket(BlockPos pos, boolean takeSnapshot, String address, + List> amounts) { + super(pos); + this.takeSnapshot = takeSnapshot; + this.address = address; + this.amounts = amounts; + } + + public StockTickerConfigurationPacket(FriendlyByteBuf buffer) { + super(buffer); + } + + @Override + protected void writeSettings(FriendlyByteBuf buffer) { + buffer.writeBoolean(takeSnapshot); + buffer.writeUtf(address); + buffer.writeVarInt(amounts.size()); + for (IntAttached intAttached : amounts) { + buffer.writeVarInt(intAttached.getFirst()); + buffer.writeItem(intAttached.getValue()); + } + } + + @Override + protected void readSettings(FriendlyByteBuf buffer) { + takeSnapshot = buffer.readBoolean(); + address = buffer.readUtf(); + int items = buffer.readVarInt(); + amounts = new ArrayList<>(); + for (int i = 0; i < items; i++) + amounts.add(IntAttached.with(buffer.readVarInt(), buffer.readItem())); + } + + @Override + protected void applySettings(StockTickerBlockEntity be) { + if (takeSnapshot) { + be.takeInventoryStockSnapshot(); + return; + } + + be.updateAutoRestockSettings(address, amounts); + } + +}