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;
}
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;
}
Aggregations