-
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add IndexedInventory to expose slotless item handler
- Loading branch information
1 parent
3cb8aeb
commit f01eb2a
Showing
5 changed files
with
346 additions
and
22 deletions.
There are no files selected for viewing
Submodule api
updated
6 files
196 changes: 196 additions & 0 deletions
196
src/main/java/org/cyclops/cyclopscore/inventory/IndexedInventory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
118 changes: 118 additions & 0 deletions
118
src/main/java/org/cyclops/cyclopscore/inventory/IndexedSlotlessItemHandlerWrapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.