diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEAnalysisResult.java index 0dc2789..216a773 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEAnalysisResult.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEAnalysisResult.java @@ -151,6 +151,8 @@ public boolean apply(IBlockApplyContext ctx) { customName.setCustomName(mAECustomName); } + boolean success = true; + // add/remove/update ae parts and cables if (te instanceof IPartHost partHost && mAEParts != null) { for (ForgeDirection dir : AEAnalysisResult.ALL_DIRECTIONS) { @@ -189,19 +191,25 @@ public boolean apply(IBlockApplyContext ctx) { continue; } - if (!installPart(ctx, partHost, dir, expected, false)) { return false; } + if (!installPart(ctx, partHost, dir, expected, false)) { + success = false; + continue; + } } } if (expected != null) { - if (!expected.updatePart(ctx, partHost, dir)) { return false; } + if (!expected.updatePart(ctx, partHost, dir)) { + success = false; + continue; + } } Platform.notifyBlocksOfNeighbors(te.getWorldObj(), te.xCoord, te.yCoord, te.zCoord); } } - return true; + return success; } private void removePart(IBlockApplyContext context, IPartHost partHost, ForgeDirection side, boolean simulate) { diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEPartData.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEPartData.java index d1356e3..66a98eb 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEPartData.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEPartData.java @@ -143,6 +143,8 @@ public boolean isAttunable() { public boolean updatePart(IBlockApplyContext context, IPartHost partHost, ForgeDirection side) { IPart part = partHost.getPart(side); + boolean success = true; + if (part instanceof PartP2PTunnelNormal && isAttunable()) { partHost.removePart(side, true); @@ -154,8 +156,8 @@ public boolean updatePart(IBlockApplyContext context, IPartHost partHost, ForgeD tunnel.output = mP2POutput; try { - final P2PCache p2p = tunnel.getProxy() - .getP2P(); + final P2PCache p2p = tunnel.getProxy().getP2P(); + // calls setFrequency p2p.updateFreq(tunnel, mP2PFreq); } catch (final GridAccessException e) { @@ -172,33 +174,24 @@ public boolean updatePart(IBlockApplyContext context, IPartHost partHost, ForgeD if (part instanceof IConfigurableObject configurable && configurable.getConfigManager() != null) { NBTTagCompound settings = mSettings == null ? new NBTTagCompound() : mSettings; - configurable.getConfigManager() - .readFromNBT(settings); + configurable.getConfigManager().readFromNBT(settings); } if (part instanceof ISegmentedInventory segmentedInventory) { if (segmentedInventory.getInventoryByName("upgrades") instanceof UpgradeInventory upgradeInv) { - ItemStackMap targetMap = MMUtils.getItemStackHistogram( - Arrays.stream(mAEUpgrades) - .map(PortableItemStack::toStack) - .collect(Collectors.toList()) - ); - ItemStackMap actualMap = MMUtils - .getItemStackHistogram(Arrays.asList(MMUtils.inventoryToArray(upgradeInv))); - - if (!targetMap.equals(actualMap)) { - if (!MMUtils.installUpgrades(context, upgradeInv, mAEUpgrades, true, false)) { return false; } + if (!MMUtils.installUpgrades(context, upgradeInv, mAEUpgrades, true, false)) { + success = false; } } IInventory config = segmentedInventory.getInventoryByName("config"); if (config != null) { - mConfig.apply(context, config, false, false); + if (!mConfig.apply(context, config, false, false)) success = false; } IInventory patterns = segmentedInventory.getInventoryByName("patterns"); if (mAEPatterns != null && patterns != null) { - mAEPatterns.apply(context, patterns, true, false); + if (!mAEPatterns.apply(context, patterns, true, false)) success = false; } } @@ -210,7 +203,7 @@ public boolean updatePart(IBlockApplyContext context, IPartHost partHost, ForgeD priorityHost.setPriority(priority); } - return true; + return success; } /** @@ -226,28 +219,20 @@ public boolean getRequiredItemsForExistingPart( ) { IPart part = partHost.getPart(side); + boolean success = true; + if (part instanceof ISegmentedInventory segmentedInventory) { if (segmentedInventory.getInventoryByName("upgrades") instanceof UpgradeInventory upgradeInv) { - ItemStackMap targetMap = MMUtils.getItemStackHistogram( - Arrays.stream(mAEUpgrades) - .map(PortableItemStack::toStack) - .collect(Collectors.toList()) - ); - ItemStackMap actualMap = MMUtils - .getItemStackHistogram(Arrays.asList(MMUtils.inventoryToArray(upgradeInv))); - - if (!targetMap.equals(actualMap)) { - if (!MMUtils.installUpgrades(context, upgradeInv, mAEUpgrades, true, true)) { return false; } - } + if (!MMUtils.installUpgrades(context, upgradeInv, mAEUpgrades, true, false)) success = false; } IInventory patterns = segmentedInventory.getInventoryByName("patterns"); if (mAEPatterns != null && patterns != null) { - mAEPatterns.apply(context, patterns, true, true); + if (!mAEPatterns.apply(context, patterns, true, true)) success = false; } } - return true; + return success; } /** diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MMInventory.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MMInventory.java index 0c9dd6d..e5c4a05 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MMInventory.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MMInventory.java @@ -60,7 +60,7 @@ public class MMInventory implements IPseudoInventory { private boolean printedUplinkWarning = false; - private HashSet visitedGrids = new HashSet<>(); + private final HashSet visitedGrids = new HashSet<>(); public MMInventory(EntityPlayer player, MMState state, ManipulatorTier tier) { this.player = player; diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/ItemId.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/ItemId.java index edb2854..8df2782 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/ItemId.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/ItemId.java @@ -4,6 +4,8 @@ import static net.minecraftforge.common.util.Constants.NBT.TAG_COMPOUND; import static net.minecraftforge.common.util.Constants.NBT.TAG_INT; +import java.util.Objects; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -168,4 +170,13 @@ public ItemStack getItemStack(int stackSize) { itemStack.setTagCompound(nbt == null ? null : (NBTTagCompound) nbt.copy()); return itemStack; } + + public boolean isSameAs(ItemStack stack) { + if (stack == null) return false; + if (item() != stack.getItem()) return false; + if (metaData() != Items.feather.getDamage(stack)) return false; + if (!Objects.equals(nbt(), stack.getTagCompound())) return false; + + return true; + } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/MMUtils.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/MMUtils.java index 652b689..6f114a9 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/MMUtils.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/MMUtils.java @@ -23,6 +23,7 @@ import java.util.Base64; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -31,6 +32,7 @@ import java.util.UUID; import java.util.function.Function; import java.util.function.IntFunction; +import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -72,6 +74,7 @@ import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.IFluidHandler; +import com.recursive_pineapple.matter_manipulator.MMMod; import cpw.mods.fml.relauncher.ReflectionHelper; import gregtech.api.GregTechAPI; @@ -100,10 +103,10 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; -import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; import com.gtnewhorizon.structurelib.util.XSTR; import com.recursive_pineapple.matter_manipulator.asm.Optional; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer; +import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.RequiredItemAnalysis; import com.recursive_pineapple.matter_manipulator.common.building.BlockSpec; import com.recursive_pineapple.matter_manipulator.common.building.IPseudoInventory; @@ -120,6 +123,7 @@ import org.joml.Vector3i; import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; public class MMUtils { @@ -444,24 +448,50 @@ public static EntityPlayer getPlayerById(UUID playerId) { return null; } - public static ItemStackMap getItemStackHistogram(Iterable stacks) { + public static Object2LongOpenHashMap getItemStackHistogram(Iterable stacks) { return getItemStackHistogram(stacks, true); } - public static ItemStackMap getItemStackHistogram(Iterable stacks, boolean NBTSensitive) { - ItemStackMap histogram = new ItemStackMap<>(NBTSensitive); + public static Object2LongOpenHashMap getItemStackHistogram(Iterable stacks, boolean NBTSensitive) { + Object2LongOpenHashMap histogram = new Object2LongOpenHashMap<>(); if (stacks == null) return histogram; for (ItemStack stack : stacks) { if (stack == null || stack.getItem() == null) continue; - histogram.merge(stack, (long) stack.stackSize, (Long a, Long b) -> a + b); + histogram.addTo(ItemId.create(stack), stack.stackSize); } return histogram; } - public static List getStacksOfSize(ItemStackMap map, int maxStackSize) { + public static class StackMapDiff { + public Object2LongOpenHashMap added = new Object2LongOpenHashMap<>(); + public Object2LongOpenHashMap removed = new Object2LongOpenHashMap<>(); + } + + public static StackMapDiff getStackMapDiff(Object2LongOpenHashMap before, Object2LongOpenHashMap after) { + HashSet keys = new HashSet<>(); + keys.addAll(before.keySet()); + keys.addAll(after.keySet()); + + StackMapDiff diff = new StackMapDiff(); + + for (ItemId id : keys) { + long beforeAmount = before.getLong(id); + long afterAmount = after.getLong(id); + + if (afterAmount < beforeAmount) { + diff.removed.addTo(id, beforeAmount - afterAmount); + } else if (beforeAmount < afterAmount) { + diff.added.addTo(id, afterAmount - beforeAmount); + } + } + + return diff; + } + + public static List getStacksOfSize(Object2LongOpenHashMap map, int maxStackSize) { ArrayList list = new ArrayList<>(); map.forEach((item, amount) -> { @@ -469,9 +499,7 @@ public static List getStacksOfSize(ItemStackMap map, int maxSta int toRemove = Math .min(amount > Integer.MAX_VALUE ? Integer.MAX_VALUE : amount.intValue(), maxStackSize); - ItemStack copy = item.copy(); - copy.stackSize = toRemove; - list.add(copy); + list.add(item.getItemStack(toRemove)); amount -= toRemove; } @@ -775,35 +803,101 @@ public static boolean installUpgrades( boolean consume, boolean simulate ) { + boolean success = true; + List stacks = mapToList(pupgrades, PortableItemStack::toStack); stacks.removeIf(i -> i == null || !(i.getItem() instanceof IUpgradeModule)); for (ItemStack stack : stacks) { - stack.stackSize = Math - .min(stack.stackSize, dest.getMaxInstalled(((IUpgradeModule) stack.getItem()).getType(stack))); + stack.stackSize = Math.min(stack.stackSize, dest.getMaxInstalled(((IUpgradeModule) stack.getItem()).getType(stack))); + } + + Object2LongOpenHashMap actual = getItemStackHistogram(Arrays.asList(inventoryToArray(dest))); + Object2LongOpenHashMap target = getItemStackHistogram(stacks); + + StackMapDiff diff = getStackMapDiff(actual, target); + + if (diff.removed.isEmpty() && diff.added.isEmpty()) return success; + + List toInstall = getStacksOfSize(diff.added, dest.getInventoryStackLimit()); + + long installable = dest.getSizeInventory() - actual.values().longStream().sum() + diff.removed.values().longStream().sum(); + + List toInstallBig = toInstall.subList(0, Math.min(toInstall.size(), (int) installable)) + .stream() + .map(BigItemStack::new) + .collect(Collectors.toList()); + + var result = src.tryConsumeItems(toInstallBig, IPseudoInventory.CONSUME_PARTIAL); + + List extracted = result.right(); + + for (BigItemStack wanted : toInstallBig) { + for (BigItemStack found : extracted) { + if (!found.isSameType(wanted)) continue; + + wanted.stackSize -= found.stackSize; + } } - List split = getStacksOfSize(stacks, dest.getInventoryStackLimit()); + if (src instanceof IBlockApplyContext ctx) { + for (BigItemStack wanted : toInstallBig) { + if (wanted.stackSize > 0) { + ctx.warn("Could not find upgrade: " + wanted.getItemStack().getDisplayName() + " x " + wanted.stackSize); + success = false; + } + } + } - ItemStack[] upgrades = split.subList(0, Math.min(split.size(), dest.getSizeInventory())) - .toArray(new ItemStack[0]); + if (!simulate) { + for (var e : diff.removed.object2LongEntrySet()) { + long amount = e.getLongValue(); - if (!consume || src.tryConsumeItems(upgrades)) { - if (!simulate) { - emptyInventory(src, dest); + for (int slot = 0; slot < dest.getSizeInventory(); slot++) { + if (amount <= 0) break; - for (int i = 0; i < upgrades.length; i++) { - dest.setInventorySlotContents(i, upgrades[i]); + ItemStack inSlot = dest.getStackInSlot(slot); + + if (e.getKey().isSameAs(inSlot)) { + src.givePlayerItems(inSlot); + dest.setInventorySlotContents(slot, null); + + amount--; + } } + } + + int slot = 0; - dest.markDirty(); + outer: for (BigItemStack stack : extracted) { + for (ItemStack split : stack.toStacks(1)) { + while (dest.getStackInSlot(slot) != null) { + slot++; + + if (slot >= dest.getSizeInventory()) { + MMMod.LOG.error( + "Tried to install too many upgrades: voiding the rest. Dest={}, upgrade={}, slot={}", + dest, + split, + slot, + new Exception()); + + if (src instanceof IBlockApplyContext ctx) { + ctx.error("Tried to install too many upgrades: voiding the rest (this is a bug, please report it)"); + } + break outer; + } + } + + dest.setInventorySlotContents(slot++, split); + } } - return true; - } else { - return false; + dest.markDirty(); } + + return success; } public static NBTTagCompound copy(NBTTagCompound tag) {