Search in sources :

Example 1 with BaseSimulatedInventory

use of mekanism.common.content.qio.QIOCraftingTransferHelper.BaseSimulatedInventory in project Mekanism by mekanism.

the class QIOCraftingTransferHandler method hasRoomToShuffle.

/**
 * Loosely based on how {@link mekanism.common.content.qio.QIOServerCraftingTransferHandler}'s hasRoomToShuffle method works.
 */
private static boolean hasRoomToShuffle(QIOCraftingTransferHelper qioTransferHelper, @Nullable QIOFrequency frequency, QIOCraftingWindow craftingWindow, List<HotBarSlot> hotBarSlots, List<MainInventorySlot> mainInventorySlots, Map<HashedItemSource, List<List<SingularHashedItemSource>>> shuffleLookup) {
    // Map used to keep track of inputs while also merging identical inputs, so we can cut down
    // on how many times we have to check if things can stack
    Object2IntMap<HashedItem> leftOverInput = new Object2IntArrayMap<>(9);
    for (byte slotIndex = 0; slotIndex < 9; slotIndex++) {
        IInventorySlot slot = craftingWindow.getInputSlot(slotIndex);
        if (!slot.isEmpty()) {
            // Note: We can use raw as we are not modifying the stack or persisting the reference
            HashedItem type = HashedItem.raw(slot.getStack());
            HashedItemSource source = qioTransferHelper.getSource(type);
            if (source == null) {
                // Something went wrong, this should never be null for the things in the crafting slots
                return false;
            }
            int remaining = source.getSlotRemaining(slotIndex);
            if (remaining > 0) {
                // Don't bother adding any that we fully used
                leftOverInput.mergeInt(type, remaining, Integer::sum);
            }
        }
    }
    if (!leftOverInput.isEmpty()) {
        // If we have any leftover inputs in the crafting inventory, then get a simulated view of what the player's inventory
        // will look like after things are changed
        BaseSimulatedInventory simulatedInventory = new BaseSimulatedInventory(hotBarSlots, mainInventorySlots) {

            @Override
            protected int getRemaining(int slot, ItemStack currentStored) {
                HashedItemSource source = qioTransferHelper.getSource(HashedItem.raw(currentStored));
                if (source == null) {
                    return currentStored.getCount();
                }
                return source.getSlotRemaining((byte) (slot + 9));
            }
        };
        Object2IntMap<HashedItem> stillLeftOver = simulatedInventory.shuffleInputs(leftOverInput, frequency != null);
        if (stillLeftOver == null) {
            // If we have remaining items and no frequency then we don't have room to shuffle
            return false;
        }
        if (!stillLeftOver.isEmpty() && frequency != null) {
            // If we still have left over things try adding them to the frequency. We only are able to do a rough check and estimate
            // on if the frequency has room or not as depending on how things are stored in the drives there is a chance that we
            // do not actually have as much item space or types available, but this is the best we can do on the client side
            // Note: We validate the frequency is not null, even though it shouldn't be null if we have anything still left over
            // Note: We calculate these numbers as a difference so that it is easier to make sure none of the numbers accidentally overflow
            int availableItemTypes = frequency.getTotalItemTypeCapacity() - frequency.getTotalItemTypes(true);
            long availableItemSpace = frequency.getTotalItemCountCapacity() - frequency.getTotalItemCount();
            Object2BooleanMap<HashedItemSource> usedQIOSource = new Object2BooleanArrayMap<>(shuffleLookup.size());
            for (Map.Entry<HashedItemSource, List<List<SingularHashedItemSource>>> entry : shuffleLookup.entrySet()) {
                HashedItemSource source = entry.getKey();
                boolean usedQIO = false;
                for (List<SingularHashedItemSource> usedSources : entry.getValue()) {
                    for (SingularHashedItemSource usedSource : usedSources) {
                        UUID qioSource = usedSource.getQioSource();
                        if (qioSource != null) {
                            // Free up however much space as we used of the item
                            availableItemSpace += usedSource.getUsed();
                            if (source.getQIORemaining(qioSource) == 0) {
                                // If we used all that is available, we need to also free up an item type
                                availableItemTypes++;
                                usedQIO = true;
                            }
                        }
                    }
                }
                usedQIOSource.put(source, usedQIO);
            }
            for (Object2IntMap.Entry<HashedItem> entry : stillLeftOver.object2IntEntrySet()) {
                availableItemSpace -= entry.getIntValue();
                if (availableItemSpace <= 0) {
                    // No room for all our items, fail
                    return false;
                }
                HashedItemSource source = qioTransferHelper.getSource(entry.getKey());
                if (source == null) {
                    // Something went wrong, this should never be null for the things in the crafting slots
                    return false;
                } else if (source.hasQIOSources()) {
                    // It is stored, check to make sure it isn't a type we are removing at least one of fully
                    if (usedQIOSource.containsKey(source) && usedQIOSource.getBoolean(source)) {
                        // if it is, then we need to reclaim the item type as being available
                        availableItemTypes--;
                        if (availableItemTypes <= 0) {
                            // Not enough room for types
                            return false;
                        }
                    }
                } else {
                    // The item is not stored in the QIO frequency, we need to use an item type up
                    // Note: This is not super accurate due to the fact that we don't know for
                    // certain if our used source actually matched or differed in server side only
                    // NBT, but it is the best we can do on the client side
                    availableItemTypes--;
                    if (availableItemTypes <= 0) {
                        // Not enough room for types
                        return false;
                    }
                }
            }
        }
    }
    return true;
}
Also used : IInventorySlot(mekanism.api.inventory.IInventorySlot) HashedItem(mekanism.common.lib.inventory.HashedItem) HashedItemSource(mekanism.common.content.qio.QIOCraftingTransferHelper.HashedItemSource) SingularHashedItemSource(mekanism.common.content.qio.QIOCraftingTransferHelper.SingularHashedItemSource) SingularHashedItemSource(mekanism.common.content.qio.QIOCraftingTransferHelper.SingularHashedItemSource) Object2IntMap(it.unimi.dsi.fastutil.objects.Object2IntMap) Object2BooleanArrayMap(it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap) Object2IntArrayMap(it.unimi.dsi.fastutil.objects.Object2IntArrayMap) BaseSimulatedInventory(mekanism.common.content.qio.QIOCraftingTransferHelper.BaseSimulatedInventory) List(java.util.List) ArrayList(java.util.ArrayList) ByteList(it.unimi.dsi.fastutil.bytes.ByteList) ByteArrayList(it.unimi.dsi.fastutil.bytes.ByteArrayList) ItemStack(net.minecraft.item.ItemStack) UUID(java.util.UUID) Object2BooleanMap(it.unimi.dsi.fastutil.objects.Object2BooleanMap) Object2IntArrayMap(it.unimi.dsi.fastutil.objects.Object2IntArrayMap) Object2BooleanArrayMap(it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap) Map(java.util.Map) Byte2ObjectArrayMap(it.unimi.dsi.fastutil.bytes.Byte2ObjectArrayMap) HashMap(java.util.HashMap) Byte2ObjectMap(it.unimi.dsi.fastutil.bytes.Byte2ObjectMap) Object2IntMap(it.unimi.dsi.fastutil.objects.Object2IntMap)

Example 2 with BaseSimulatedInventory

use of mekanism.common.content.qio.QIOCraftingTransferHelper.BaseSimulatedInventory in project Mekanism by mekanism.

the class QIOServerCraftingTransferHandler method hasRoomToShuffle.

private boolean hasRoomToShuffle() {
    // Map used to keep track of inputs while also merging identical inputs, so we can cut down
    // on how many times we have to check if things can stack
    Object2IntMap<HashedItem> leftOverInput = new Object2IntArrayMap<>(9);
    for (byte inputSlot = 0; inputSlot < 9; inputSlot++) {
        SlotData inputSlotData = availableItems.get(inputSlot);
        if (inputSlotData != null && inputSlotData.getAvailable() > 0) {
            // If there was an item in the slot and there still is we need to see if we have room for it anywhere
            // Note: We can just make the hashed item be raw as the stack does not get modified, and we don't persist this map
            leftOverInput.mergeInt(HashedItem.raw(inputSlotData.getStack()), inputSlotData.getAvailable(), Integer::sum);
        }
    }
    if (!leftOverInput.isEmpty()) {
        // If we have any leftover inputs in the crafting inventory, then get a simulated view of what the player's inventory
        // will look like after things are changed
        BaseSimulatedInventory simulatedInventory = new BaseSimulatedInventory(hotBarSlots, mainInventorySlots) {

            @Override
            protected int getRemaining(int slot, ItemStack currentStored) {
                SlotData slotData = availableItems.get((byte) (slot + 9));
                return slotData == null ? currentStored.getCount() : slotData.getAvailable();
            }
        };
        Object2IntMap<HashedItem> stillLeftOver = simulatedInventory.shuffleInputs(leftOverInput, frequency != null);
        if (stillLeftOver == null) {
            // If we have remaining items and no frequency then we don't have room to shuffle
            return false;
        }
        if (!stillLeftOver.isEmpty() && frequency != null) {
            // If we still have left over things try adding them to the frequency
            // Note: We validate the frequency is not null, even though it shouldn't be null if we have anything still left over
            // We start by doing a "precheck" to see if it is potentially even possible to fit the contents in based on type counts
            // Note: We calculate these numbers as a difference so that it is easier to make sure none of the numbers accidentally overflow
            int availableItemTypes = frequency.getTotalItemTypeCapacity() - frequency.getTotalItemTypes(false);
            long availableItemSpace = frequency.getTotalItemCountCapacity() - frequency.getTotalItemCount();
            for (FrequencySlotData slotData : frequencyAvailableItems.values()) {
                // Free up however much space as we used of the item
                availableItemSpace += slotData.getUsed();
                if (slotData.getAvailable() == 0) {
                    // If we used all that is available, we need to also free up an item type
                    // Note: Given we can't use as much as a full integer as we have nine slots that stack up to a max of 64
                    // if we get down to zero then we know that we actually used it all, and it isn't just the case that we
                    // think we are at zero because of clamping a long to an int
                    availableItemTypes++;
                }
            }
            for (Object2IntMap.Entry<HashedItem> entry : stillLeftOver.object2IntEntrySet()) {
                availableItemSpace -= entry.getIntValue();
                if (availableItemSpace <= 0) {
                    // No room for all our items, fail
                    return false;
                }
                UUID uuid = frequency.getUUIDForType(entry.getKey());
                if (frequency.getTypeByUUID(uuid) == null) {
                    // It is not stored, we need to use an item type up
                    // Note: We first get the uuid of the item as we need it for looking up in the slot data,
                    // and then we also validate against getting the type by uuid to see if it is actually
                    // there as uuids stay valid for a tick longer than they are stored
                    availableItemTypes--;
                    if (availableItemTypes <= 0) {
                        // Not enough room for types
                        return false;
                    }
                } else {
                    // It is stored, check to make sure it isn't a type we are removing fully
                    FrequencySlotData slotData = frequencyAvailableItems.get(uuid);
                    if (slotData != null && slotData.getAvailable() == 0) {
                        // if it is, then we need to reclaim the item type as being available
                        availableItemTypes--;
                        if (availableItemTypes <= 0) {
                            // Not enough room for types
                            return false;
                        }
                    }
                }
            }
            Collection<QIODriveData> drives = frequency.getAllDrives();
            List<SimulatedQIODrive> simulatedDrives = new ArrayList<>(drives.size());
            for (QIODriveData drive : drives) {
                simulatedDrives.add(new SimulatedQIODrive(drive));
            }
            for (Map.Entry<UUID, FrequencySlotData> entry : frequencyAvailableItems.entrySet()) {
                FrequencySlotData slotData = entry.getValue();
                HashedItem type = slotData.getType();
                if (type != null) {
                    // If there is something actually stored in the frequency for this UUID, we need to try and remove it from our simulated drives
                    int toRemove = slotData.getUsed();
                    for (SimulatedQIODrive drive : simulatedDrives) {
                        toRemove = drive.remove(type, toRemove);
                        if (toRemove == 0) {
                            break;
                        }
                    }
                }
            }
            for (Object2IntMap.Entry<HashedItem> entry : stillLeftOver.object2IntEntrySet()) {
                HashedItem item = entry.getKey();
                int toAdd = entry.getIntValue();
                // Start by trying to add to ones it can stack with
                for (SimulatedQIODrive drive : simulatedDrives) {
                    toAdd = drive.add(item, toAdd, true);
                    if (toAdd == 0) {
                        break;
                    }
                }
                // accurate simulation result
                if (toAdd > 0) {
                    // And then add to empty slots if we couldn't add it all in a way that stacks
                    for (SimulatedQIODrive drive : simulatedDrives) {
                        toAdd = drive.add(item, toAdd, false);
                        if (toAdd == 0) {
                            break;
                        }
                    }
                    if (toAdd > 0) {
                        // There are some items we can't fit anywhere, fail
                        return false;
                    }
                }
            }
        }
    }
    return true;
}
Also used : HashedItem(mekanism.common.lib.inventory.HashedItem) Object2IntMap(it.unimi.dsi.fastutil.objects.Object2IntMap) ArrayList(java.util.ArrayList) Object2IntArrayMap(it.unimi.dsi.fastutil.objects.Object2IntArrayMap) BaseSimulatedInventory(mekanism.common.content.qio.QIOCraftingTransferHelper.BaseSimulatedInventory) ItemStack(net.minecraft.item.ItemStack) UUID(java.util.UUID) Object2IntArrayMap(it.unimi.dsi.fastutil.objects.Object2IntArrayMap) HashMap(java.util.HashMap) Byte2ObjectOpenHashMap(it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap) Object2LongMap(it.unimi.dsi.fastutil.objects.Object2LongMap) Map(java.util.Map) Byte2ObjectMap(it.unimi.dsi.fastutil.bytes.Byte2ObjectMap) Object2IntMap(it.unimi.dsi.fastutil.objects.Object2IntMap) Byte2ObjectArrayMap(it.unimi.dsi.fastutil.bytes.Byte2ObjectArrayMap)

Aggregations

Byte2ObjectArrayMap (it.unimi.dsi.fastutil.bytes.Byte2ObjectArrayMap)2 Byte2ObjectMap (it.unimi.dsi.fastutil.bytes.Byte2ObjectMap)2 Object2IntArrayMap (it.unimi.dsi.fastutil.objects.Object2IntArrayMap)2 Object2IntMap (it.unimi.dsi.fastutil.objects.Object2IntMap)2 ArrayList (java.util.ArrayList)2 HashMap (java.util.HashMap)2 Map (java.util.Map)2 UUID (java.util.UUID)2 BaseSimulatedInventory (mekanism.common.content.qio.QIOCraftingTransferHelper.BaseSimulatedInventory)2 HashedItem (mekanism.common.lib.inventory.HashedItem)2 ItemStack (net.minecraft.item.ItemStack)2 Byte2ObjectOpenHashMap (it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap)1 ByteArrayList (it.unimi.dsi.fastutil.bytes.ByteArrayList)1 ByteList (it.unimi.dsi.fastutil.bytes.ByteList)1 Object2BooleanArrayMap (it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap)1 Object2BooleanMap (it.unimi.dsi.fastutil.objects.Object2BooleanMap)1 Object2LongMap (it.unimi.dsi.fastutil.objects.Object2LongMap)1 List (java.util.List)1 IInventorySlot (mekanism.api.inventory.IInventorySlot)1 HashedItemSource (mekanism.common.content.qio.QIOCraftingTransferHelper.HashedItemSource)1