|
23 | 23 | import java.util.Base64;
|
24 | 24 | import java.util.Collection;
|
25 | 25 | import java.util.HashMap;
|
| 26 | +import java.util.HashSet; |
26 | 27 | import java.util.Iterator;
|
27 | 28 | import java.util.List;
|
28 | 29 | import java.util.Locale;
|
|
31 | 32 | import java.util.UUID;
|
32 | 33 | import java.util.function.Function;
|
33 | 34 | import java.util.function.IntFunction;
|
| 35 | +import java.util.stream.Collectors; |
34 | 36 | import java.util.stream.IntStream;
|
35 | 37 | import java.util.stream.Stream;
|
36 | 38 |
|
|
72 | 74 | import net.minecraftforge.common.util.ForgeDirection;
|
73 | 75 | import net.minecraftforge.fluids.IFluidHandler;
|
74 | 76 |
|
| 77 | +import com.recursive_pineapple.matter_manipulator.MMMod; |
75 | 78 | import cpw.mods.fml.relauncher.ReflectionHelper;
|
76 | 79 |
|
77 | 80 | import gregtech.api.GregTechAPI;
|
|
100 | 103 | import com.google.gson.JsonObject;
|
101 | 104 | import com.google.gson.JsonParseException;
|
102 | 105 | import com.google.gson.JsonPrimitive;
|
103 |
| -import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; |
104 | 106 | import com.gtnewhorizon.structurelib.util.XSTR;
|
105 | 107 | import com.recursive_pineapple.matter_manipulator.asm.Optional;
|
106 | 108 | import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer;
|
| 109 | +import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; |
107 | 110 | import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.RequiredItemAnalysis;
|
108 | 111 | import com.recursive_pineapple.matter_manipulator.common.building.BlockSpec;
|
109 | 112 | import com.recursive_pineapple.matter_manipulator.common.building.IPseudoInventory;
|
|
120 | 123 | import org.joml.Vector3i;
|
121 | 124 |
|
122 | 125 | import it.unimi.dsi.fastutil.Pair;
|
| 126 | +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; |
123 | 127 |
|
124 | 128 | public class MMUtils {
|
125 | 129 |
|
@@ -444,34 +448,58 @@ public static EntityPlayer getPlayerById(UUID playerId) {
|
444 | 448 | return null;
|
445 | 449 | }
|
446 | 450 |
|
447 |
| - public static ItemStackMap<Long> getItemStackHistogram(Iterable<ItemStack> stacks) { |
| 451 | + public static Object2LongOpenHashMap<ItemId> getItemStackHistogram(Iterable<ItemStack> stacks) { |
448 | 452 | return getItemStackHistogram(stacks, true);
|
449 | 453 | }
|
450 | 454 |
|
451 |
| - public static ItemStackMap<Long> getItemStackHistogram(Iterable<ItemStack> stacks, boolean NBTSensitive) { |
452 |
| - ItemStackMap<Long> histogram = new ItemStackMap<>(NBTSensitive); |
| 455 | + public static Object2LongOpenHashMap<ItemId> getItemStackHistogram(Iterable<ItemStack> stacks, boolean NBTSensitive) { |
| 456 | + Object2LongOpenHashMap<ItemId> histogram = new Object2LongOpenHashMap<>(); |
453 | 457 |
|
454 | 458 | if (stacks == null) return histogram;
|
455 | 459 |
|
456 | 460 | for (ItemStack stack : stacks) {
|
457 | 461 | if (stack == null || stack.getItem() == null) continue;
|
458 |
| - histogram.merge(stack, (long) stack.stackSize, (Long a, Long b) -> a + b); |
| 462 | + histogram.addTo(ItemId.create(stack), stack.stackSize); |
459 | 463 | }
|
460 | 464 |
|
461 | 465 | return histogram;
|
462 | 466 | }
|
463 | 467 |
|
464 |
| - public static List<ItemStack> getStacksOfSize(ItemStackMap<Long> map, int maxStackSize) { |
| 468 | + public static class StackMapDiff { |
| 469 | + public Object2LongOpenHashMap<ItemId> added = new Object2LongOpenHashMap<>(); |
| 470 | + public Object2LongOpenHashMap<ItemId> removed = new Object2LongOpenHashMap<>(); |
| 471 | + } |
| 472 | + |
| 473 | + public static StackMapDiff getStackMapDiff(Object2LongOpenHashMap<ItemId> before, Object2LongOpenHashMap<ItemId> after) { |
| 474 | + HashSet<ItemId> keys = new HashSet<>(); |
| 475 | + keys.addAll(before.keySet()); |
| 476 | + keys.addAll(after.keySet()); |
| 477 | + |
| 478 | + StackMapDiff diff = new StackMapDiff(); |
| 479 | + |
| 480 | + for (ItemId id : keys) { |
| 481 | + long beforeAmount = before.getLong(id); |
| 482 | + long afterAmount = after.getLong(id); |
| 483 | + |
| 484 | + if (afterAmount < beforeAmount) { |
| 485 | + diff.removed.addTo(id, beforeAmount - afterAmount); |
| 486 | + } else if (beforeAmount < afterAmount) { |
| 487 | + diff.added.addTo(id, afterAmount - beforeAmount); |
| 488 | + } |
| 489 | + } |
| 490 | + |
| 491 | + return diff; |
| 492 | + } |
| 493 | + |
| 494 | + public static List<ItemStack> getStacksOfSize(Object2LongOpenHashMap<ItemId> map, int maxStackSize) { |
465 | 495 | ArrayList<ItemStack> list = new ArrayList<>();
|
466 | 496 |
|
467 | 497 | map.forEach((item, amount) -> {
|
468 | 498 | while (amount > 0) {
|
469 | 499 | int toRemove = Math
|
470 | 500 | .min(amount > Integer.MAX_VALUE ? Integer.MAX_VALUE : amount.intValue(), maxStackSize);
|
471 | 501 |
|
472 |
| - ItemStack copy = item.copy(); |
473 |
| - copy.stackSize = toRemove; |
474 |
| - list.add(copy); |
| 502 | + list.add(item.getItemStack(toRemove)); |
475 | 503 |
|
476 | 504 | amount -= toRemove;
|
477 | 505 | }
|
@@ -775,35 +803,101 @@ public static boolean installUpgrades(
|
775 | 803 | boolean consume,
|
776 | 804 | boolean simulate
|
777 | 805 | ) {
|
| 806 | + boolean success = true; |
| 807 | + |
778 | 808 | List<ItemStack> stacks = mapToList(pupgrades, PortableItemStack::toStack);
|
779 | 809 |
|
780 | 810 | stacks.removeIf(i -> i == null || !(i.getItem() instanceof IUpgradeModule));
|
781 | 811 |
|
782 | 812 | for (ItemStack stack : stacks) {
|
783 |
| - stack.stackSize = Math |
784 |
| - .min(stack.stackSize, dest.getMaxInstalled(((IUpgradeModule) stack.getItem()).getType(stack))); |
| 813 | + stack.stackSize = Math.min(stack.stackSize, dest.getMaxInstalled(((IUpgradeModule) stack.getItem()).getType(stack))); |
| 814 | + } |
| 815 | + |
| 816 | + Object2LongOpenHashMap<ItemId> actual = getItemStackHistogram(Arrays.asList(inventoryToArray(dest))); |
| 817 | + Object2LongOpenHashMap<ItemId> target = getItemStackHistogram(stacks); |
| 818 | + |
| 819 | + StackMapDiff diff = getStackMapDiff(actual, target); |
| 820 | + |
| 821 | + if (diff.removed.isEmpty() && diff.added.isEmpty()) return success; |
| 822 | + |
| 823 | + List<ItemStack> toInstall = getStacksOfSize(diff.added, dest.getInventoryStackLimit()); |
| 824 | + |
| 825 | + long installable = dest.getSizeInventory() - actual.values().longStream().sum() + diff.removed.values().longStream().sum(); |
| 826 | + |
| 827 | + List<BigItemStack> toInstallBig = toInstall.subList(0, Math.min(toInstall.size(), (int) installable)) |
| 828 | + .stream() |
| 829 | + .map(BigItemStack::new) |
| 830 | + .collect(Collectors.toList()); |
| 831 | + |
| 832 | + var result = src.tryConsumeItems(toInstallBig, IPseudoInventory.CONSUME_PARTIAL); |
| 833 | + |
| 834 | + List<BigItemStack> extracted = result.right(); |
| 835 | + |
| 836 | + for (BigItemStack wanted : toInstallBig) { |
| 837 | + for (BigItemStack found : extracted) { |
| 838 | + if (!found.isSameType(wanted)) continue; |
| 839 | + |
| 840 | + wanted.stackSize -= found.stackSize; |
| 841 | + } |
785 | 842 | }
|
786 | 843 |
|
787 |
| - List<ItemStack> split = getStacksOfSize(stacks, dest.getInventoryStackLimit()); |
| 844 | + if (src instanceof IBlockApplyContext ctx) { |
| 845 | + for (BigItemStack wanted : toInstallBig) { |
| 846 | + if (wanted.stackSize > 0) { |
| 847 | + ctx.warn("Could not find upgrade: " + wanted.getItemStack().getDisplayName() + " x " + wanted.stackSize); |
| 848 | + success = false; |
| 849 | + } |
| 850 | + } |
| 851 | + } |
788 | 852 |
|
789 |
| - ItemStack[] upgrades = split.subList(0, Math.min(split.size(), dest.getSizeInventory())) |
790 |
| - .toArray(new ItemStack[0]); |
| 853 | + if (!simulate) { |
| 854 | + for (var e : diff.removed.object2LongEntrySet()) { |
| 855 | + long amount = e.getLongValue(); |
791 | 856 |
|
792 |
| - if (!consume || src.tryConsumeItems(upgrades)) { |
793 |
| - if (!simulate) { |
794 |
| - emptyInventory(src, dest); |
| 857 | + for (int slot = 0; slot < dest.getSizeInventory(); slot++) { |
| 858 | + if (amount <= 0) break; |
795 | 859 |
|
796 |
| - for (int i = 0; i < upgrades.length; i++) { |
797 |
| - dest.setInventorySlotContents(i, upgrades[i]); |
| 860 | + ItemStack inSlot = dest.getStackInSlot(slot); |
| 861 | + |
| 862 | + if (e.getKey().isSameAs(inSlot)) { |
| 863 | + src.givePlayerItems(inSlot); |
| 864 | + dest.setInventorySlotContents(slot, null); |
| 865 | + |
| 866 | + amount--; |
| 867 | + } |
798 | 868 | }
|
| 869 | + } |
| 870 | + |
| 871 | + int slot = 0; |
799 | 872 |
|
800 |
| - dest.markDirty(); |
| 873 | + outer: for (BigItemStack stack : extracted) { |
| 874 | + for (ItemStack split : stack.toStacks(1)) { |
| 875 | + while (dest.getStackInSlot(slot) != null) { |
| 876 | + slot++; |
| 877 | + |
| 878 | + if (slot >= dest.getSizeInventory()) { |
| 879 | + MMMod.LOG.error( |
| 880 | + "Tried to install too many upgrades: voiding the rest. Dest={}, upgrade={}, slot={}", |
| 881 | + dest, |
| 882 | + split, |
| 883 | + slot, |
| 884 | + new Exception()); |
| 885 | + |
| 886 | + if (src instanceof IBlockApplyContext ctx) { |
| 887 | + ctx.error("Tried to install too many upgrades: voiding the rest (this is a bug, please report it)"); |
| 888 | + } |
| 889 | + break outer; |
| 890 | + } |
| 891 | + } |
| 892 | + |
| 893 | + dest.setInventorySlotContents(slot++, split); |
| 894 | + } |
801 | 895 | }
|
802 | 896 |
|
803 |
| - return true; |
804 |
| - } else { |
805 |
| - return false; |
| 897 | + dest.markDirty(); |
806 | 898 | }
|
| 899 | + |
| 900 | + return success; |
807 | 901 | }
|
808 | 902 |
|
809 | 903 | public static NBTTagCompound copy(NBTTagCompound tag) {
|
|
0 commit comments