diff --git a/src/main/java/me/aleksilassila/litematica/printer/config/Configs.java b/src/main/java/me/aleksilassila/litematica/printer/config/Configs.java index 8de5b3ad9..f601784ea 100644 --- a/src/main/java/me/aleksilassila/litematica/printer/config/Configs.java +++ b/src/main/java/me/aleksilassila/litematica/printer/config/Configs.java @@ -193,6 +193,11 @@ public static class Core { .defaultValue(false) .build(); + public static final ConfigBoolean SELECTION_MATERIALS = bool("selectionMaterials") + .defaultValue(false) + .setVisible(isLoadChestTrackerLoaded) + .build(); + // 远程交互 - 开关 public static final ConfigBoolean CLOUD_INVENTORY = bool("cloudInventory") .defaultValue(false) @@ -239,6 +244,7 @@ public static class Core { AUTO_DISABLE_PRINTER, UPDATE_CHECK, DEBUG_OUTPUT, + SELECTION_MATERIALS, CLOUD_INVENTORY, AUTO_INVENTORY, INVENTORY_LIST, diff --git a/src/main/java/me/aleksilassila/litematica/printer/mixin/printer/litematica/MixinMaterialListUtils.java b/src/main/java/me/aleksilassila/litematica/printer/mixin/printer/litematica/MixinMaterialListUtils.java new file mode 100644 index 000000000..99f5025be --- /dev/null +++ b/src/main/java/me/aleksilassila/litematica/printer/mixin/printer/litematica/MixinMaterialListUtils.java @@ -0,0 +1,38 @@ +package me.aleksilassila.litematica.printer.mixin.printer.litematica; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import fi.dy.masa.litematica.materials.MaterialListUtils; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import me.aleksilassila.litematica.printer.utils.LitematicaUtils; +import net.minecraft.world.Container; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +//#if MC >= 260000 +//$$ import fi.dy.masa.malilib.util.data.ItemType; +//#else +import fi.dy.masa.malilib.util.ItemType; +//#endif + +@Mixin(MaterialListUtils.class) +public class MixinMaterialListUtils { + //#if MC == 12111 + @SuppressWarnings("deprecation") + //#endif + @WrapOperation(method = "getMaterialList", at = @At(value = "INVOKE", target = "Lfi/dy/masa/litematica/materials/MaterialListUtils;getInventoryItemCounts(Lnet/minecraft/world/Container;)Lit/unimi/dsi/fastutil/objects/Object2IntOpenHashMap;")) + private static Object2IntOpenHashMap initApplySelectionArea(Container inv, Operation> original) { + Object2IntOpenHashMap result = original.call(inv); + LitematicaUtils.applySelectionArea(result); + return result; + } + //#if MC == 12111 + @SuppressWarnings("deprecation") + //#endif + @WrapOperation(method = "updateAvailableCounts", at = @At(value = "INVOKE", target = "Lfi/dy/masa/litematica/materials/MaterialListUtils;getInventoryItemCounts(Lnet/minecraft/world/Container;)Lit/unimi/dsi/fastutil/objects/Object2IntOpenHashMap;")) + private static Object2IntOpenHashMap updateApplySelectionArea(Container inv, Operation> original) { + Object2IntOpenHashMap result = original.call(inv); + LitematicaUtils.applySelectionArea(result); + return result; + } +} diff --git a/src/main/java/me/aleksilassila/litematica/printer/printer/zxy/chesttracker/MemoryUtils.java b/src/main/java/me/aleksilassila/litematica/printer/printer/zxy/chesttracker/MemoryUtils.java index 34f203bc3..fa7b48efd 100644 --- a/src/main/java/me/aleksilassila/litematica/printer/printer/zxy/chesttracker/MemoryUtils.java +++ b/src/main/java/me/aleksilassila/litematica/printer/printer/zxy/chesttracker/MemoryUtils.java @@ -1,25 +1,34 @@ package me.aleksilassila.litematica.printer.printer.zxy.chesttracker; //#if MC >= 12001 + +import fi.dy.masa.litematica.materials.MaterialListUtils; +import fi.dy.masa.malilib.util.InventoryUtils; import me.aleksilassila.litematica.printer.I18n; import me.aleksilassila.litematica.printer.config.Configs; import me.aleksilassila.litematica.printer.handler.ClientPlayerTickManager; import me.aleksilassila.litematica.printer.printer.zxy.inventory.OpenInventoryPacket; import me.aleksilassila.litematica.printer.printer.zxy.utils.ZxyUtils; import me.aleksilassila.litematica.printer.utils.MessageUtils; +import me.aleksilassila.litematica.printer.printer.PrinterBox; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.BundleItem; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.NotNull; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import red.jackf.chesttracker.api.memory.Memory; import red.jackf.chesttracker.api.memory.MemoryBank; +import red.jackf.chesttracker.api.memory.MemoryKey; import red.jackf.chesttracker.api.providers.MemoryBuilder; import red.jackf.chesttracker.api.providers.ProviderUtils; import red.jackf.chesttracker.impl.events.AfterPlayerDestroyBlock; @@ -34,8 +43,15 @@ import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; +//#if MC >= 260000 +//$$ import fi.dy.masa.malilib.util.data.ItemType; +//#else +import fi.dy.masa.malilib.util.ItemType; +//#endif + public class MemoryUtils { @NotNull static Minecraft client = Minecraft.getInstance(); @@ -69,7 +85,7 @@ public static void setup() { // 破坏方块后清除打印机库存的该记录 AfterPlayerDestroyBlock.EVENT.register(cbs -> { if (PRINTER_MEMORY != null - && PRINTER_MEMORY.getMetadata().getIntegritySettings().removeOnPlayerBlockBreak + && PRINTER_MEMORY.getMetadata().getIntegritySettings().removeOnPlayerBlockBreak ) { ProviderUtils.getPlayersCurrentKey().ifPresent(currentKey -> PRINTER_MEMORY.removeMemory(currentKey, cbs.pos())); } @@ -89,13 +105,15 @@ public static void setup() { //保存打印机库存 ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> unLoad()); } - public static void saveMemory(AbstractContainerMenu sc){ - if(PRINTER_MEMORY != null && ZxyUtils.printerMemoryAdding || ClientPlayerTickManager.PRINT.isPrinterMemorySync()) - save(sc , PRINTER_MEMORY); + + public static void saveMemory(AbstractContainerMenu sc) { + if (PRINTER_MEMORY != null && ZxyUtils.printerMemoryAdding || ClientPlayerTickManager.PRINT.isPrinterMemorySync()) + save(sc, PRINTER_MEMORY); MemoryBankAccessImpl.INSTANCE.getLoadedInternal().ifPresent(memoryBank -> save(sc, memoryBank)); ClientPlayerTickManager.PRINT.setPrinterMemorySync(false); } - public static void createPrinterMemory(){ + + public static void createPrinterMemory() { Optional current = Coordinate.getCurrent(); if (current.isPresent()) { Coordinate coordinate = current.get(); @@ -110,7 +128,7 @@ public static void createPrinterMemory(){ bank.setId(s1); return bank; }); - if(printerMetadata != null){ + if (printerMetadata != null) { PRINTER_MEMORY.setMetadata(printerMetadata); } // Metadata metadata = PRINTER_MEMORY.getMetadata(); @@ -136,29 +154,115 @@ public static void save() { Storage.save(PRINTER_MEMORY); } - public static void save(AbstractContainerMenu screen , MemoryBank memoryBank) { - if (memoryBank == null || OpenInventoryPacket.key == null || blockState == null || !Configs.Core.CLOUD_INVENTORY.getBooleanValue()) return; + public static void save(AbstractContainerMenu screen, MemoryBank memoryBank) { + if (memoryBank == null || OpenInventoryPacket.key == null || blockState == null || !Configs.Core.CLOUD_INVENTORY.getBooleanValue()) + return; List connected; if (ZxyUtils.printerMemoryAdding && client.level != null) { connected = ConnectedBlocksGrabber.getConnected(client.level, client.level.getBlockState(OpenInventoryPacket.pos), OpenInventoryPacket.pos); } else connected = null; List items; - if (screen !=null) + if (screen != null) items = screen.slots.stream() - .filter(slot -> !(slot.container instanceof Inventory)) - .map(Slot::getItem) - .toList(); + .filter(slot -> !(slot.container instanceof Inventory)) + .map(Slot::getItem) + .toList(); else return; Memory memory = MemoryBuilder.create(items) - .inContainer(blockState.getBlock()) - .otherPositions(connected != null ? connected.stream() - .filter(pos -> !pos.equals(connected.get(0))) - .toList() : List.of(OpenInventoryPacket.pos) - ).build(); + .inContainer(blockState.getBlock()) + .otherPositions(connected != null ? connected.stream() + .filter(pos -> !pos.equals(connected.get(0))) + .toList() : List.of(OpenInventoryPacket.pos) + ).build(); if (memory != null) { - memoryBank.addMemory(OpenInventoryPacket.key.identifier(),OpenInventoryPacket.pos,memory); + memoryBank.addMemory(OpenInventoryPacket.key.identifier(), OpenInventoryPacket.pos, memory); + } + } + + //#if MC == 12111 + @SuppressWarnings("deprecation") + //#endif + public static void getSelectionContainerItems(List boxes, List> result) { + if (boxes == null || result == null || boxes.isEmpty() || client.level == null) { + return; + } + + MemoryBankImpl bank = MemoryBankAccessImpl.INSTANCE.getLoadedInternal().orElse(null); + if (bank == null) { + return; } + + Identifier dimensionKey = client.level.dimension().identifier(); + Optional optional = bank.getKey(dimensionKey); + if (optional.isEmpty()) { + return; + } + Map memories = optional.get().getMemories(); + + if (memories == null || memories.isEmpty()) { + return; + } + + for (PrinterBox box : boxes) { + if (box == null) { + continue; + } + + for (BlockPos pos : box) { + Memory memory = memories.get(pos); + if (memory == null) { + continue; + } + + result.add(convertChestTrackerItems(memory)); + } + } + } + + //#if MC == 12111 + @SuppressWarnings("deprecation") + //#endif + private static Object2IntOpenHashMap convertChestTrackerItems(Memory memory) { + Object2IntOpenHashMap result = new Object2IntOpenHashMap<>(); + if (memory == null || memory.items() == null) { + return result; + } + + for (ItemStack stack : memory.items()) { + if (stack == null || stack.isEmpty()) { + continue; + } + + // From `MaterialListUtils.getInventoryItemCounts#L174-L203` + Item item = stack.getItem(); + if (item instanceof BlockItem blockItem && + blockItem.getBlock() instanceof ShulkerBoxBlock && + InventoryUtils.shulkerBoxHasItems(stack) + ) { + Object2IntOpenHashMap boxCounts = MaterialListUtils.getStoredItemCounts(stack); + + for (ItemType boxType : boxCounts.keySet()) { + result.addTo(boxType, boxCounts.getInt(boxType)); + } + + boxCounts.clear(); + //#if MC > 12004 + } else if (item instanceof BundleItem && InventoryUtils.bundleHasItems(stack)) { + Object2IntOpenHashMap bundleCounts = MaterialListUtils.getBundleItemCounts(stack); + + for (ItemType bundleType : bundleCounts.keySet()) { + result.addTo(bundleType, bundleCounts.getInt(bundleType)); + } + + bundleCounts.clear(); + //#endif + } else { + result.addTo(new ItemType(stack, true, false), stack.getCount()); + } + } + + return result; } } //#endif \ No newline at end of file diff --git a/src/main/java/me/aleksilassila/litematica/printer/utils/LitematicaUtils.java b/src/main/java/me/aleksilassila/litematica/printer/utils/LitematicaUtils.java index 8f40a9ea4..16b40d721 100644 --- a/src/main/java/me/aleksilassila/litematica/printer/utils/LitematicaUtils.java +++ b/src/main/java/me/aleksilassila/litematica/printer/utils/LitematicaUtils.java @@ -8,6 +8,8 @@ import fi.dy.masa.litematica.util.EasyPlaceProtocol; import fi.dy.masa.litematica.util.PlacementHandler; import fi.dy.masa.litematica.util.WorldUtils; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import me.aleksilassila.litematica.printer.config.Configs; import me.aleksilassila.litematica.printer.printer.PrinterBox; import net.fabricmc.api.EnvType; @@ -19,15 +21,18 @@ import java.util.*; +//#if MC >= 260000 +//$$ import fi.dy.masa.malilib.util.data.ItemType; +//#else +import fi.dy.masa.malilib.util.ItemType; +//#endif //#if MC < 11900 //$$ import fi.dy.masa.malilib.util.SubChunkPos; //#endif -@SuppressWarnings({"BooleanMethodIsAlwaysInverted", "BooleanMethodIsAlwaysInverted"}) @Environment(EnvType.CLIENT) public class LitematicaUtils { public static final Minecraft client = Minecraft.getInstance(); - public static final LitematicaUtils INSTANCE = new LitematicaUtils(); private LitematicaUtils() { } @@ -94,4 +99,58 @@ static boolean comparePos(Box box, BlockPos pos) { return printerBox.contains(pos); } + /** + * 获取当前 Litematica 选区内所有容器方块的物品内容。 + */ + //#if MC == 12111 + @SuppressWarnings("deprecation") + //#endif + public static void applySelectionArea(Object2IntOpenHashMap items) { + if (!Configs.Core.SELECTION_MATERIALS.getBooleanValue()) { + return; + } + + AreaSelection selection = DataManager.getSelectionManager().getCurrentSelection(); + if (selection == null) { + return; + } + + List boxes = getSelectionBoxes(selection); + if (boxes.isEmpty()) { + return; + } + + //#if MC >= 12001 + if (ModUtils.isChestTrackerLoaded()) { + List> containerItems = new ObjectArrayList<>(); + me.aleksilassila.litematica.printer.printer.zxy.chesttracker.MemoryUtils.getSelectionContainerItems(boxes, containerItems); + for (Object2IntOpenHashMap map : containerItems) { + map.forEach(items::addTo); + } + } + //#endif + + } + + private static List getSelectionBoxes(AreaSelection selection) { + if (selection == null) { + return Collections.emptyList(); + } + + if (DataManager.getSelectionManager().getSelectionMode() == SelectionMode.NORMAL) { + return selection.getAllSubRegionBoxes().stream().map(LitematicaUtils::toPrinterBox).toList(); + } + + Box box = selection.getSubRegionBox(DataManager.getSimpleArea().getName()); + PrinterBox printerBox = toPrinterBox(box); + return printerBox != null ? Collections.singletonList(printerBox) : Collections.emptyList(); + } + + private static PrinterBox toPrinterBox(Box box) { + if (box == null || box.getPos1() == null || box.getPos2() == null) { + return null; + } + return new PrinterBox(box.getPos1(), box.getPos2()); + } + } \ No newline at end of file diff --git a/src/main/resources/assets/litematica-printer/lang/en_us.json b/src/main/resources/assets/litematica-printer/lang/en_us.json index 0c026b273..36339366e 100644 --- a/src/main/resources/assets/litematica-printer/lang/en_us.json +++ b/src/main/resources/assets/litematica-printer/lang/en_us.json @@ -241,6 +241,8 @@ "litematica-printer.config.renderHud.desc": "Display current mode and work progress within range on operation page when printer is working;\nIf §6§lLag Detection§r is enabled, will show prompt when lag is excessive.", "litematica-printer.config.renderLayerLimit": "Render Layer Limit", "litematica-printer.config.renderLayerLimit.desc": "Limit printer work range to Litematica's currently rendered layer range.", + "litematica-printer.config.selectionMaterials": "Selection Materials", + "litematica-printer.config.selectionMaterials.desc": "In the Material List, also count items stored in containers within the selected area as already available items. (Requires ChestTracker)", "litematica-printer.config.selectionType.litematica.renderLayer": "Projection Render Layer", "litematica-printer.config.selectionType.litematica.selection": "Projection Selection", "litematica-printer.config.selectionType.litematica.selection.abovePlayer": "Projection Selection (Above Player)", diff --git a/src/main/resources/assets/litematica-printer/lang/zh_cn.json b/src/main/resources/assets/litematica-printer/lang/zh_cn.json index b4b6fdbca..7f7c70843 100644 --- a/src/main/resources/assets/litematica-printer/lang/zh_cn.json +++ b/src/main/resources/assets/litematica-printer/lang/zh_cn.json @@ -241,6 +241,8 @@ "litematica-printer.config.renderHud.desc": "打印机工作时,在操作页面显示当前模式和范围内工作进度;\n如果开启了§6§l延迟检测§r,那么会在延迟过大时显示提示。", "litematica-printer.config.renderLayerLimit": "渲染层数限制", "litematica-printer.config.renderLayerLimit.desc": "将打印机工作范围限制为Litematica当前渲染的图层范围。", + "litematica-printer.config.selectionMaterials": "选区算作材料列表", + "litematica-printer.config.selectionMaterials.desc": "将选区内容器中的物品算做材料列表中的已有物品。 (需要 ChestTracker)", "litematica-printer.config.selectionType.litematica.renderLayer": "投影渲染层", "litematica-printer.config.selectionType.litematica.selection": "投影选区", "litematica-printer.config.selectionType.litematica.selection.abovePlayer": "投影选区(玩家之上)", diff --git a/src/main/resources/litematica-printer.mixins.json b/src/main/resources/litematica-printer.mixins.json index 6a9fa0c06..94aff6465 100644 --- a/src/main/resources/litematica-printer.mixins.json +++ b/src/main/resources/litematica-printer.mixins.json @@ -16,6 +16,7 @@ "printer.litematica.EasyPlaceUtilsAccessor", "printer.litematica.InventoryUtilsAccessor", "printer.litematica.MixinInventoryUtils", + "printer.litematica.MixinMaterialListUtils", "printer.malilib.config.MixinConfigBase", "printer.malilib.config.MixinIConfigBase", "printer.mc.MixinBlockItem",