Skip to content

Commit

Permalink
Add IndexedInventory to expose slotless item handler
Browse files Browse the repository at this point in the history
  • Loading branch information
rubensworks committed Jan 16, 2017
1 parent 3cb8aeb commit f01eb2a
Show file tree
Hide file tree
Showing 5 changed files with 346 additions and 22 deletions.
196 changes: 196 additions & 0 deletions src/main/java/org/cyclops/cyclopscore/inventory/IndexedInventory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package org.cyclops.cyclopscore.inventory;

import com.google.common.collect.Maps;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import org.apache.logging.log4j.Level;
import org.cyclops.cyclopscore.CyclopsCore;

import java.util.Map;

/**
* An inventory that adds an index to a regular inventory.
* @author rubensworks
*
*/
public class IndexedInventory extends LargeInventory implements IndexedSlotlessItemHandlerWrapper.IInventoryIndexReference {

private final Map<Item, TIntObjectMap<ItemStack>> index = Maps.newIdentityHashMap();
private int firstEmptySlot;
private int lastEmptySlot;
private int firstNonEmptySlot;
private int lastNonEmptySlot;

/**
* Default constructor for NBT persistence, don't call this yourself.
*/
public IndexedInventory() {
this(0, "", 0);
}

/**
* Make a new instance.
* @param size The amount of slots in the inventory.
* @param name The name of the inventory, used for NBT storage.
* @param stackLimit The stack limit for each slot.
*/
public IndexedInventory(int size, String name, int stackLimit) {
super(size, name, stackLimit);
this.firstEmptySlot = 0;
this.lastEmptySlot = size - 1;
this.firstNonEmptySlot = -1;
this.lastNonEmptySlot = -1;
}

protected void createIndex() {
index.clear();
firstEmptySlot = -1;
lastEmptySlot = -1;
firstNonEmptySlot = -1;
lastNonEmptySlot = -1;
for (int i = 0; i < getSizeInventory(); i++) {
ItemStack itemStack = getStackInSlot(i);
if (itemStack != null) {
TIntObjectMap<ItemStack> stacks = index.get(itemStack.getItem());
if (stacks == null) {
stacks = new TIntObjectHashMap<ItemStack>();
index.put(itemStack.getItem(), stacks);
}
stacks.put(i, itemStack);
if (firstNonEmptySlot < 0) {
firstNonEmptySlot = i;
}
lastNonEmptySlot = i;
} else {
if (firstEmptySlot < 0) {
firstEmptySlot = i;
}
lastEmptySlot = i;
}
}
}

@Override
public void readFromNBT(NBTTagCompound data, String tag) {
super.readFromNBT(data, tag);
createIndex();
}

@Override
public void setInventorySlotContents(int slotId, ItemStack itemStack) {
// Update index
ItemStack oldStack = getStackInSlot(slotId);
if (oldStack != null) {
TIntObjectMap<ItemStack> stacks = index.get(oldStack.getItem());
stacks.remove(slotId);
}
if (itemStack != null) {
TIntObjectMap<ItemStack> stacks = index.get(itemStack.getItem());
if (stacks == null) {
stacks = new TIntObjectHashMap<ItemStack>();
index.put(itemStack.getItem(), stacks);
}
stacks.put(slotId, itemStack);
}

// Call super
super.setInventorySlotContents(slotId, itemStack);

// Update first and last values
if (slotId == firstEmptySlot && oldStack == null && itemStack != null) {
// Search forwards
int oldFirstEmptySlot = firstEmptySlot;
firstEmptySlot = -1;
for (int i = Math.max(0, oldFirstEmptySlot); i < getSizeInventory(); i++) {
if (getStackInSlot(i) == null) {
firstEmptySlot = i;
break;
}
}
}
if (slotId == lastEmptySlot && oldStack == null && itemStack != null) {
// Search backwards
int oldLastEmptySlot = lastEmptySlot;
lastEmptySlot = -1;
for (int i = oldLastEmptySlot; i >= 0; i--) {
if (getStackInSlot(i) == null) {
lastEmptySlot = i;
break;
}
}
}
if (slotId == firstNonEmptySlot && oldStack != null && itemStack == null) {
// Search forwards
int oldFirstNonEmptySlot = firstNonEmptySlot;
firstNonEmptySlot = -1;
for (int i = Math.max(0, oldFirstNonEmptySlot); i < getSizeInventory(); i++) {
if (getStackInSlot(i) != null) {
firstNonEmptySlot = i;
break;
}
}
}
if (slotId == lastNonEmptySlot && oldStack != null && itemStack == null) {
// Search backwards
int oldLastNonEmptySlot = lastNonEmptySlot;
lastNonEmptySlot = -1;
for (int i = oldLastNonEmptySlot; i >= 0; i--) {
if (getStackInSlot(i) != null) {
lastNonEmptySlot = i;
break;
}
}
}
if ((slotId < firstEmptySlot || firstEmptySlot < 0) && itemStack == null) {
firstEmptySlot = slotId;
}
if (slotId > lastEmptySlot && itemStack == null) {
lastEmptySlot = slotId;
}
if ((slotId < firstNonEmptySlot || firstNonEmptySlot < 0) && itemStack != null) {
firstNonEmptySlot = slotId;
}
if (slotId > lastNonEmptySlot && itemStack != null) {
lastNonEmptySlot = slotId;
}

if (firstEmptySlot == firstNonEmptySlot) CyclopsCore.clog(Level.WARN, String.format(
"Indexed inventory at inconsistent with first empty %s and first non-empty %s.", firstEmptySlot, firstNonEmptySlot));
if (lastEmptySlot == lastNonEmptySlot) CyclopsCore.clog(Level.WARN, String.format(
"Indexed inventory at inconsistent with last empty %s and last non-empty %s.", lastEmptySlot, lastNonEmptySlot));
}

@Override
public void clear() {
super.clear();
index.clear();
}

@Override
public Map<Item, TIntObjectMap<ItemStack>> getIndex() {
return index;
}

@Override
public int getFirstEmptySlot() {
return firstEmptySlot;
}

@Override
public int getLastEmptySlot() {
return lastEmptySlot;
}

@Override
public int getFirstNonEmptySlot() {
return firstNonEmptySlot;
}

@Override
public int getLastNonEmptySlot() {
return lastNonEmptySlot;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.cyclops.cyclopscore.inventory;

import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.map.TIntObjectMap;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;
import org.cyclops.commoncapabilities.api.capability.itemhandler.SlotlessItemHandlerWrapper;

import javax.annotation.Nonnull;
import java.util.Map;

/**
* A {@link org.cyclops.commoncapabilities.api.capability.itemhandler.ISlotlessItemHandler}
* that uses the index from a {@link IndexedInventory}.
* @author rubensworks
*/
public class IndexedSlotlessItemHandlerWrapper extends SlotlessItemHandlerWrapper {

private final IInventoryIndexReference inventory;
private MovementStrategy inputStrategy;
private MovementStrategy outputStrategy;

public IndexedSlotlessItemHandlerWrapper(IItemHandler itemHandler, IInventoryIndexReference inventory,
MovementStrategy inputStrategy, MovementStrategy outputStrategy) {
super(itemHandler);
this.inventory = inventory;
this.inputStrategy = inputStrategy;
this.outputStrategy = outputStrategy;
}

public IndexedSlotlessItemHandlerWrapper(IItemHandler itemHandler, IInventoryIndexReference inventory) {
this(itemHandler, inventory, MovementStrategy.FIRST, MovementStrategy.FIRST);
}

@Override
protected int getNonFullSlotWithItemStack(@Nonnull ItemStack itemStack, int matchFlags) {
Map<Item, TIntObjectMap<ItemStack>> items = inventory.getIndex();
TIntObjectMap<ItemStack> stacks = items.get(itemStack.getItem());
if (stacks != null) {
for (TIntObjectIterator<ItemStack> it = stacks.iterator(); it.hasNext(); ) {
it.advance();
if (it.value().stackSize < Math.min(inventory.getInventoryStackLimit(), it.value().getMaxStackSize())
&& ItemMatch.areItemStacksEqual(it.value(), itemStack, matchFlags)) {
return it.key();
}
}
}
return -1;
}

@Override
protected int getNonEmptySlotWithItemStack(@Nonnull ItemStack itemStack, int matchFlags) {
Map<Item, TIntObjectMap<ItemStack>> items = inventory.getIndex();
TIntObjectMap<ItemStack> stacks = items.get(itemStack.getItem());
if (stacks != null) {
for (TIntObjectIterator<ItemStack> it = stacks.iterator(); it.hasNext(); ) {
it.advance();
if (ItemMatch.areItemStacksEqual(it.value(), itemStack, matchFlags)) {
return it.key();
}
}
}
return -1;
}

@Override
protected int getEmptySlot() {
return inputStrategy == MovementStrategy.FIRST ? inventory.getFirstEmptySlot() : inventory.getLastEmptySlot();
}

@Override
protected int getNonEmptySlot() {
return outputStrategy == MovementStrategy.FIRST ? inventory.getFirstNonEmptySlot() : inventory.getLastNonEmptySlot();
}

public MovementStrategy getInputStrategy() {
return inputStrategy;
}

public void setInputStrategy(MovementStrategy inputStrategy) {
this.inputStrategy = inputStrategy;
}

public MovementStrategy getOutputStrategy() {
return outputStrategy;
}

public void setOutputStrategy(MovementStrategy outputStrategy) {
this.outputStrategy = outputStrategy;
}

/**
* Strategies indicating which slots to pick first.
*/
public static enum MovementStrategy {
/**
* Pick the earliest possible slot, with the smallest slot id.
*/
FIRST,
/**
* Pick the latest possible slot, with the largest slot id.
*/
LAST
}

public static interface IInventoryIndexReference {

public int getInventoryStackLimit();
public Map<Item, TIntObjectMap<ItemStack>> getIndex();
public int getFirstEmptySlot();
public int getLastEmptySlot();
public int getFirstNonEmptySlot();
public int getLastNonEmptySlot();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
*/
public class LargeInventory extends SimpleInventory {

/**
* Default constructor for NBT persistence, don't call this yourself.
*/
public LargeInventory() {
this(0, "", 0);
}

/**
* Make a new instance.
* @param size The amount of slots in the inventory.
Expand All @@ -30,7 +37,7 @@ public LargeInventory(int size, String name, int stackLimit) {
public void readFromNBT(NBTTagCompound data, String tag) {
NBTTagList nbttaglist = data.getTagList(tag, MinecraftHelpers.NBTTag_Types.NBTTagCompound.ordinal());

for (int j = 0; j < _contents.length; ++j)
for (int j = 0; j < getSizeInventory(); ++j)
_contents[j] = null;

for (int j = 0; j < nbttaglist.tagCount(); ++j) {
Expand All @@ -41,7 +48,7 @@ public void readFromNBT(NBTTagCompound data, String tag) {
} else {
index = slot.getInteger("Slot");
}
if (index >= 0 && index < _contents.length) {
if (index >= 0 && index < getSizeInventory()) {
_contents[index] = ItemStack.loadItemStackFromNBT(slot);
}
}
Expand All @@ -54,12 +61,13 @@ public void readFromNBT(NBTTagCompound data, String tag) {
*/
public void writeToNBT(NBTTagCompound data, String tag) {
NBTTagList slots = new NBTTagList();
for (int index = 0; index < _contents.length; ++index) {
if (_contents[index] != null && _contents[index].stackSize > 0) {
for (int index = 0; index < getSizeInventory(); ++index) {
ItemStack itemStack = getStackInSlot(index);
if (itemStack != null && itemStack.stackSize > 0) {
NBTTagCompound slot = new NBTTagCompound();
slots.appendTag(slot);
slot.setInteger("Slot", index);
_contents[index].writeToNBT(slot);
itemStack.writeToNBT(slot);
}
}
data.setTag(tag, slots);
Expand Down
Loading

0 comments on commit f01eb2a

Please sign in to comment.