Transactor Optimization
It was an O(n^2) operation to insert an item. Now its just O(n). The functions for checking to see if a stack is valid in a slot are expensive and should never be called more than once per slot, let alone n^2 times (where n is the length of the inventory). Additionally, by wrapping inventory in a slot iterator, I was able to eliminate the need to handle FSided and VSided differently from IInventory.
This commit is contained in:
parent
0a18ae34ff
commit
e3420842fe
7 changed files with 264 additions and 76 deletions
43
common/buildcraft/core/inventory/InventoryIterator.java
Normal file
43
common/buildcraft/core/inventory/InventoryIterator.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package buildcraft.core.inventory;
|
||||
|
||||
import buildcraft.core.inventory.InventoryIterator.IInvSlot;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.inventory.ISidedInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.ForgeDirection;
|
||||
|
||||
/**
|
||||
* @author CovertJaguar <http://www.railcraft.info>
|
||||
*/
|
||||
public class InventoryIterator {
|
||||
|
||||
/**
|
||||
* Returns an Iterable object for the specified side of the inventory.
|
||||
*
|
||||
* @param inv
|
||||
* @param side
|
||||
* @return Iterable
|
||||
*/
|
||||
public static Iterable<IInvSlot> getIterable(IInventory inv, ForgeDirection side) {
|
||||
if (inv instanceof ISidedInventory) {
|
||||
return new InventoryIteratorSided((ISidedInventory) inv, side);
|
||||
}
|
||||
if (inv instanceof net.minecraftforge.common.ISidedInventory) {
|
||||
return new InventoryIteratorSided(InventoryWrapper.getWrappedInventory(inv), side);
|
||||
}
|
||||
return new InventoryIteratorSimple(inv);
|
||||
}
|
||||
|
||||
public interface IInvSlot {
|
||||
|
||||
boolean canPutStackInSlot(ItemStack stack);
|
||||
|
||||
boolean canTakeStackFromSlot(ItemStack stack);
|
||||
|
||||
ItemStack decreaseStackInSlot();
|
||||
|
||||
ItemStack getStackInSlot();
|
||||
|
||||
void setStackInSlot(ItemStack stack);
|
||||
}
|
||||
}
|
80
common/buildcraft/core/inventory/InventoryIteratorSided.java
Normal file
80
common/buildcraft/core/inventory/InventoryIteratorSided.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
package buildcraft.core.inventory;
|
||||
|
||||
import buildcraft.core.inventory.InventoryIterator.IInvSlot;
|
||||
import java.util.Iterator;
|
||||
import net.minecraft.inventory.ISidedInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.ForgeDirection;
|
||||
|
||||
/**
|
||||
* @author CovertJaguar <http://www.railcraft.info>
|
||||
*/
|
||||
public class InventoryIteratorSided implements Iterable<IInvSlot> {
|
||||
|
||||
private final ISidedInventory inv;
|
||||
private final int side;
|
||||
|
||||
InventoryIteratorSided(ISidedInventory inv, ForgeDirection side) {
|
||||
this.inv = inv;
|
||||
this.side = side.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<IInvSlot> iterator() {
|
||||
return new Iterator<IInvSlot>() {
|
||||
int[] slots = inv.getAccessibleSlotsFromSide(side);
|
||||
int index = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return index < slots.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IInvSlot next() {
|
||||
return new InvSlot(slots[index++]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Remove not supported.");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private class InvSlot implements IInvSlot {
|
||||
|
||||
private int slot;
|
||||
|
||||
public InvSlot(int slot) {
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getStackInSlot() {
|
||||
return inv.getStackInSlot(slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStackInSlot(ItemStack stack) {
|
||||
inv.setInventorySlotContents(slot, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPutStackInSlot(ItemStack stack) {
|
||||
return inv.canInsertItem(slot, stack, side);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTakeStackFromSlot(ItemStack stack) {
|
||||
return inv.canExtractItem(slot, stack, side);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack decreaseStackInSlot() {
|
||||
return inv.decrStackSize(slot, 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package buildcraft.core.inventory;
|
||||
|
||||
import buildcraft.core.inventory.InventoryIterator.IInvSlot;
|
||||
import java.util.Iterator;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* @author CovertJaguar <http://www.railcraft.info>
|
||||
*/
|
||||
public class InventoryIteratorSimple implements Iterable<IInvSlot> {
|
||||
|
||||
private final IInventory inv;
|
||||
|
||||
InventoryIteratorSimple(IInventory inv) {
|
||||
this.inv = inv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<IInvSlot> iterator() {
|
||||
return new Iterator<IInvSlot>() {
|
||||
int slot = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return slot < inv.getSizeInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IInvSlot next() {
|
||||
return new InvSlot(slot++);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Remove not supported.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class InvSlot implements IInvSlot {
|
||||
|
||||
private int slot;
|
||||
|
||||
public InvSlot(int slot) {
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getStackInSlot() {
|
||||
return inv.getStackInSlot(slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStackInSlot(ItemStack stack) {
|
||||
inv.setInventorySlotContents(slot, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPutStackInSlot(ItemStack stack) {
|
||||
return inv.isStackValidForSlot(slot, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTakeStackFromSlot(ItemStack stack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack decreaseStackInSlot() {
|
||||
return inv.decrStackSize(slot, 1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ public class InventoryWrapperSimple extends InventoryWrapper {
|
|||
|
||||
@Override
|
||||
public boolean canInsertItem(int slotIndex, ItemStack itemstack, int side) {
|
||||
return true;
|
||||
return isStackValidForSlot(slotIndex, itemstack);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,10 +24,10 @@ public abstract class Transactor implements ITransactor {
|
|||
return new TransactorSpecial((ISpecialInventory) object);
|
||||
|
||||
else if (object instanceof ISidedInventory)
|
||||
return new TransactorVanillaSided((ISidedInventory) object);
|
||||
return new TransactorSimple((ISidedInventory) object);
|
||||
|
||||
else if (object instanceof net.minecraftforge.common.ISidedInventory)
|
||||
return new TransactorForgeSided((net.minecraftforge.common.ISidedInventory) object);
|
||||
return new TransactorSimple(InventoryWrapper.getWrappedInventory(object));
|
||||
|
||||
else if (object instanceof IInventory)
|
||||
return new TransactorSimple(Utils.getInventory((IInventory) object));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package buildcraft.core.inventory;
|
||||
|
||||
import buildcraft.core.inventory.InventoryIterator.IInvSlot;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.ForgeDirection;
|
||||
|
@ -13,16 +14,15 @@ public class TransactorRoundRobin extends TransactorSimple {
|
|||
@Override
|
||||
public int inject(ItemStack stack, ForgeDirection orientation, boolean doAdd) {
|
||||
|
||||
int oneLessThanStackSize = stack.stackSize - 1;
|
||||
int added = 0;
|
||||
|
||||
for (int itemLoop = 0; itemLoop < stack.stackSize; ++itemLoop) { // add 1 item n times.
|
||||
|
||||
int minSimilar = Integer.MAX_VALUE;
|
||||
int minSlot = -1;
|
||||
int smallestStackSize = Integer.MAX_VALUE;
|
||||
IInvSlot minSlot = null;
|
||||
|
||||
for (int j = 0; j < inventory.getSizeInventory() && minSimilar > 1; ++j) {
|
||||
ItemStack stackInInventory = inventory.getStackInSlot(j);
|
||||
for (IInvSlot slot : InventoryIterator.getIterable(inventory, orientation)) {
|
||||
ItemStack stackInInventory = slot.getStackInSlot();
|
||||
|
||||
if (stackInInventory == null) {
|
||||
continue;
|
||||
|
@ -36,15 +36,17 @@ public class TransactorRoundRobin extends TransactorSimple {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (stackInInventory.stackSize > 0 && stackInInventory.itemID == stack.itemID && stackInInventory.getItemDamage() == stack.getItemDamage()
|
||||
&& stackInInventory.stackSize < minSimilar) {
|
||||
minSimilar = stackInInventory.stackSize;
|
||||
minSlot = j;
|
||||
if (MERGE_HELPER.canStacksMerge(stack, stackInInventory) && stackInInventory.stackSize < smallestStackSize) {
|
||||
smallestStackSize = stackInInventory.stackSize;
|
||||
minSlot = slot;
|
||||
}
|
||||
if (smallestStackSize <= 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (minSlot != -1) {
|
||||
added += addToSlot(minSlot, stack, oneLessThanStackSize, doAdd); // add 1 item n times, into the selected slot
|
||||
if (minSlot != null) {
|
||||
added += addToSlot(minSlot, stack, stack.stackSize - 1, doAdd); // add 1 item n times, into the selected slot
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -53,5 +55,4 @@ public class TransactorRoundRobin extends TransactorSimple {
|
|||
|
||||
return added;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package buildcraft.core.inventory;
|
||||
|
||||
import buildcraft.core.inventory.InventoryIterator.IInvSlot;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.ForgeDirection;
|
||||
|
||||
public class TransactorSimple extends Transactor {
|
||||
|
||||
protected static final StackMergeHelper MERGE_HELPER = new StackMergeHelper();
|
||||
protected IInventory inventory;
|
||||
|
||||
public TransactorSimple(IInventory inventory) {
|
||||
|
@ -14,98 +18,84 @@ public class TransactorSimple extends Transactor {
|
|||
|
||||
@Override
|
||||
public int inject(ItemStack stack, ForgeDirection orientation, boolean doAdd) {
|
||||
List<IInvSlot> filledSlots = new ArrayList<IInvSlot>(inventory.getSizeInventory());
|
||||
List<IInvSlot> emptySlots = new ArrayList<IInvSlot>(inventory.getSizeInventory());
|
||||
for (IInvSlot slot : InventoryIterator.getIterable(inventory, orientation)) {
|
||||
if (slot.canPutStackInSlot(stack)) {
|
||||
if (slot.getStackInSlot() == null) {
|
||||
emptySlots.add(slot);
|
||||
} else {
|
||||
filledSlots.add(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int injected = 0;
|
||||
injected = tryPut(stack, filledSlots, injected, doAdd);
|
||||
injected = tryPut(stack, emptySlots, injected, doAdd);
|
||||
|
||||
int slotIndex = 0;
|
||||
int slot = 0;
|
||||
while (slotIndex < inventory.getSizeInventory() && injected < stack.stackSize) {
|
||||
slot = getPartialSlot(stack, orientation, slotIndex++);
|
||||
if (slot != -1){
|
||||
injected += addToSlot(slot, stack, injected, doAdd);
|
||||
}
|
||||
}
|
||||
|
||||
slotIndex = 0;
|
||||
while (slotIndex < inventory.getSizeInventory() && injected < stack.stackSize) {
|
||||
slot = getEmptySlot(stack, orientation, slotIndex++);
|
||||
if (slot != -1){
|
||||
injected += addToSlot(slot, stack, injected, doAdd);
|
||||
}
|
||||
}
|
||||
inventory.onInventoryChanged();
|
||||
return injected;
|
||||
}
|
||||
|
||||
protected int getPartialSlot(ItemStack stack, ForgeDirection orientation, int skipAhead) {
|
||||
return getPartialSlot(stack, skipAhead, inventory.getSizeInventory());
|
||||
}
|
||||
|
||||
protected int getPartialSlot(ItemStack stack, int startSlot, int endSlot) {
|
||||
|
||||
for (int i = startSlot; i < endSlot; i++) {
|
||||
if (inventory.getStackInSlot(i) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!inventory.isStackValidForSlot(i, stack)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inventory.getStackInSlot(i).isItemEqual(stack) || !ItemStack.areItemStackTagsEqual(inventory.getStackInSlot(i), stack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inventory.getStackInSlot(i).stackSize >= inventory.getStackInSlot(i).getMaxStackSize() || inventory.getStackInSlot(i).stackSize >= inventory.getInventoryStackLimit()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
private int tryPut(ItemStack stack, List<IInvSlot> slots, int injected, boolean doAdd) {
|
||||
if (injected >= stack.stackSize) {
|
||||
return injected;
|
||||
}
|
||||
|
||||
return -1;
|
||||
for (IInvSlot slot : slots) {
|
||||
ItemStack stackInSlot = slot.getStackInSlot();
|
||||
if (stackInSlot == null || MERGE_HELPER.canStacksMerge(stackInSlot, stack)) {
|
||||
int used = addToSlot(slot, stack, injected, doAdd);
|
||||
if (used > 0) {
|
||||
injected += used;
|
||||
if (injected >= stack.stackSize) {
|
||||
return injected;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return injected;
|
||||
}
|
||||
|
||||
protected int getEmptySlot(ItemStack stack, ForgeDirection orientation, int slotIndex) {
|
||||
return getEmptySlot(stack, 0, inventory.getSizeInventory());
|
||||
}
|
||||
|
||||
protected int getEmptySlot(ItemStack stack, int startSlot, int endSlot) {
|
||||
for (int i = startSlot; i < endSlot; i++)
|
||||
if (inventory.getStackInSlot(i) == null && inventory.isStackValidForSlot(i, stack))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected int addToSlot(int slot, ItemStack stack, int injected, boolean doAdd) {
|
||||
/**
|
||||
*
|
||||
* @param slot
|
||||
* @param stack
|
||||
* @param injected Amount not to move?
|
||||
* @param doAdd
|
||||
* @return Return the number of items moved.
|
||||
*/
|
||||
protected int addToSlot(IInvSlot slot, ItemStack stack, int injected, boolean doAdd) {
|
||||
int available = stack.stackSize - injected;
|
||||
int max = Math.min(stack.getMaxStackSize(), inventory.getInventoryStackLimit());
|
||||
|
||||
ItemStack stackInSlot = inventory.getStackInSlot(slot);
|
||||
ItemStack stackInSlot = slot.getStackInSlot();
|
||||
if (stackInSlot == null) {
|
||||
int wanted = Math.min(available, max);
|
||||
if (doAdd) {
|
||||
stackInSlot = stack.copy();
|
||||
stackInSlot.stackSize = wanted;
|
||||
inventory.setInventorySlotContents(slot, stackInSlot);
|
||||
slot.setStackInSlot(stackInSlot);
|
||||
}
|
||||
return wanted;
|
||||
}
|
||||
|
||||
if (!stackInSlot.isItemEqual(stack) || !ItemStack.areItemStackTagsEqual(stackInSlot, stack))
|
||||
if (!MERGE_HELPER.canStacksMerge(stack, stackInSlot)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wanted = max - stackInSlot.stackSize;
|
||||
if (wanted <= 0)
|
||||
if (wanted <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wanted > available)
|
||||
if (wanted > available) {
|
||||
wanted = available;
|
||||
}
|
||||
|
||||
if (doAdd) {
|
||||
stackInSlot.stackSize += wanted;
|
||||
inventory.setInventorySlotContents(slot, stackInSlot);
|
||||
slot.setStackInSlot(stackInSlot);
|
||||
}
|
||||
return wanted;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue