Merge pull request #2175 from Vexatos/itemhandlers

Rewrote InventoryUtils to use IItemHandlers.
This commit is contained in:
Florian "Sangar" Nücke 2016-12-17 14:28:31 +01:00 committed by GitHub
commit 1ad747e486
16 changed files with 219 additions and 171 deletions

View File

@ -13,6 +13,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.BlockPos; import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandler;
import java.util.Collection; import java.util.Collection;
@ -224,10 +225,20 @@ public final class Driver {
} }
/** /**
* Get an inventory implementation providing access to an item inventory. * @deprecated Use {@link #itemHandlerFor(ItemStack, EntityPlayer)} instead.
*/
@Deprecated // TODO Remove in OC 1.7
public static IInventory inventoryFor(ItemStack stack, EntityPlayer player) {
if (API.driver != null)
return API.driver.inventoryFor(stack, player);
return null;
}
/**
* Get an IItemHandler implementation providing access to an item inventory.
* <p/> * <p/>
* This will use the registered {@link InventoryProvider}s to find an * This will use the registered {@link InventoryProvider}s to find an
* inventory implementation providing access to the specified stack. * IItemHandler implementation providing access to the specified stack.
* If none can be found, returns <tt>null</tt>. * If none can be found, returns <tt>null</tt>.
* <p/> * <p/>
* Note that the specified <tt>player</tt> may be null, but will usually * Note that the specified <tt>player</tt> may be null, but will usually
@ -235,11 +246,11 @@ public final class Driver {
* *
* @param stack the item stack to get the inventory access for. * @param stack the item stack to get the inventory access for.
* @param player the player holding the item. May be <tt>null</tt>. * @param player the player holding the item. May be <tt>null</tt>.
* @return the inventory implementation interfacing the stack, or <tt>null</tt>. * @return the IItemHandler implementation interfacing the stack, or <tt>null</tt>.
*/ */
public static IInventory inventoryFor(ItemStack stack, EntityPlayer player) { public static IItemHandler itemHandlerFor(ItemStack stack, EntityPlayer player) {
if (API.driver != null) if (API.driver != null)
return API.driver.inventoryFor(stack, player); return API.driver.itemHandlerFor(stack, player);
return null; return null;
} }

View File

@ -13,6 +13,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.BlockPos; import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandler;
import java.util.Collection; import java.util.Collection;
@ -170,10 +171,16 @@ public interface DriverAPI {
Class<?> environmentFor(ItemStack stack); Class<?> environmentFor(ItemStack stack);
/** /**
* Get an inventory implementation providing access to an item inventory. * @deprecated Use {@link #itemHandlerFor(ItemStack, EntityPlayer)} instead.
*/
@Deprecated // TODO Remove in OC 1.7
IInventory inventoryFor(ItemStack stack, EntityPlayer player);
/**
* Get an IItemHandler implementation providing access to an item inventory.
* <p/> * <p/>
* This will use the registered {@link InventoryProvider}s to find an * This will use the registered {@link InventoryProvider}s to find an
* inventory implementation providing access to the specified stack. * IItemHandler implementation providing access to the specified stack.
* If none can be found, returns <tt>null</tt>. * If none can be found, returns <tt>null</tt>.
* <p/> * <p/>
* Note that the specified <tt>player</tt> may be null, but will usually * Note that the specified <tt>player</tt> may be null, but will usually
@ -181,9 +188,9 @@ public interface DriverAPI {
* *
* @param stack the item stack to get the inventory access for. * @param stack the item stack to get the inventory access for.
* @param player the player holding the item. May be <tt>null</tt>. * @param player the player holding the item. May be <tt>null</tt>.
* @return the inventory implementation interfacing the stack, or <tt>null</tt>. * @return the IItemHandler implementation interfacing the stack, or <tt>null</tt>.
*/ */
IInventory inventoryFor(ItemStack stack, EntityPlayer player); IItemHandler itemHandlerFor(ItemStack stack, EntityPlayer player);
/** /**
* Get a list of all registered block drivers. * Get a list of all registered block drivers.

View File

@ -1,6 +1,7 @@
package li.cil.oc.server.agent package li.cil.oc.server.agent
import li.cil.oc.api.internal import li.cil.oc.api.internal
import li.cil.oc.util.ExtendedInventory._
import li.cil.oc.util.InventoryUtils import li.cil.oc.util.InventoryUtils
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
@ -9,9 +10,9 @@ import net.minecraft.item.Item
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.nbt.NBTTagList import net.minecraft.nbt.NBTTagList
import li.cil.oc.util.ExtendedInventory._
class Inventory(val agent: internal.Agent) extends InventoryPlayer(null) { class Inventory(val agent: internal.Agent) extends InventoryPlayer(null) {
def selectedItemStack = agent.mainInventory.getStackInSlot(agent.selectedSlot) def selectedItemStack = agent.mainInventory.getStackInSlot(agent.selectedSlot)
def inventorySlots = (agent.selectedSlot until getSizeInventory) ++ (0 until agent.selectedSlot) def inventorySlots = (agent.selectedSlot until getSizeInventory) ++ (0 until agent.selectedSlot)
@ -53,7 +54,7 @@ class Inventory(val agent: internal.Agent) extends InventoryPlayer(null) {
override def addItemStackToInventory(stack: ItemStack) = { override def addItemStackToInventory(stack: ItemStack) = {
val slots = this.indices.drop(agent.selectedSlot) ++ this.indices.take(agent.selectedSlot) val slots = this.indices.drop(agent.selectedSlot) ++ this.indices.take(agent.selectedSlot)
InventoryUtils.insertIntoInventory(stack, this, slots = Option(slots)) InventoryUtils.insertIntoInventory(stack, InventoryUtils.asItemHandler(this), slots = Option(slots))
} }
override def canHeldItemHarvest(block: Block): Boolean = block.getMaterial.isToolNotRequired || (getCurrentItem != null && getCurrentItem.canHarvestBlock(block)) override def canHeldItemHarvest(block: Block): Boolean = block.getMaterial.isToolNotRequired || (getCurrentItem != null && getCurrentItem.canHarvestBlock(block))

View File

@ -14,7 +14,8 @@ import li.cil.oc.api.network.SidedEnvironment
import li.cil.oc.api.network.Visibility import li.cil.oc.api.network.Visibility
import li.cil.oc.api.prefab import li.cil.oc.api.prefab
import li.cil.oc.api.prefab.AbstractValue import li.cil.oc.api.prefab.AbstractValue
import li.cil.oc.server.component.DebugCard.{AccessContext, CommandSender} import li.cil.oc.server.component.DebugCard.AccessContext
import li.cil.oc.server.component.DebugCard.CommandSender
import li.cil.oc.util.BlockPosition import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
@ -541,11 +542,11 @@ object DebugCard {
val tag = if (Strings.isNullOrEmpty(tagJson)) null else JsonToNBT.getTagFromJson(tagJson) val tag = if (Strings.isNullOrEmpty(tagJson)) null else JsonToNBT.getTagFromJson(tagJson)
val position = BlockPosition(args.checkDouble(4), args.checkDouble(5), args.checkDouble(6), world) val position = BlockPosition(args.checkDouble(4), args.checkDouble(5), args.checkDouble(6), world)
val side = args.checkSideAny(7) val side = args.checkSideAny(7)
InventoryUtils.inventoryAt(position) match { InventoryUtils.inventoryAt(position, side) match {
case Some(inventory) => case Some(inventory) =>
val stack = new ItemStack(item, count, damage) val stack = new ItemStack(item, count, damage)
stack.setTagCompound(tag) stack.setTagCompound(tag)
result(InventoryUtils.insertIntoInventory(stack, inventory, Option(side))) result(InventoryUtils.insertIntoInventory(stack, inventory))
case _ => result(Unit, "no inventory") case _ => result(Unit, "no inventory")
} }
} }
@ -554,11 +555,11 @@ object DebugCard {
def removeItem(context: Context, args: Arguments): Array[AnyRef] = { def removeItem(context: Context, args: Arguments): Array[AnyRef] = {
checkAccess() checkAccess()
val position = BlockPosition(args.checkDouble(0), args.checkDouble(1), args.checkDouble(2), world) val position = BlockPosition(args.checkDouble(0), args.checkDouble(1), args.checkDouble(2), world)
InventoryUtils.inventoryAt(position) match { InventoryUtils.anyInventoryAt(position) match {
case Some(inventory) => case Some(inventory) =>
val slot = args.checkSlot(inventory, 3) val slot = args.checkSlot(inventory, 3)
val count = args.optInteger(4, inventory.getInventoryStackLimit) val count = args.optInteger(4, 64)
val removed = inventory.decrStackSize(slot, count) val removed = inventory.extractItem(slot, count, false)
if (removed == null) result(0) if (removed == null) result(0)
else result(removed.stackSize) else result(removed.stackSize)
case _ => result(Unit, "no inventory") case _ => result(Unit, "no inventory")

View File

@ -3,11 +3,11 @@ package li.cil.oc.server.component
import java.util import java.util
import li.cil.oc.Constants import li.cil.oc.Constants
import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
import li.cil.oc.api.driver.DeviceInfo.DeviceClass
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api.Network import li.cil.oc.api.Network
import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.driver.DeviceInfo
import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
import li.cil.oc.api.driver.DeviceInfo.DeviceClass
import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context import li.cil.oc.api.machine.Context
@ -22,8 +22,6 @@ import net.minecraft.util.EnumFacing
import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsJava._
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
import scala.collection.convert.WrapAsJava._
class Drone(val agent: entity.Drone) extends prefab.ManagedEnvironment with Agent with DeviceInfo { class Drone(val agent: entity.Drone) extends prefab.ManagedEnvironment with Agent with DeviceInfo {
override val node = Network.newNode(this, Visibility.Network). override val node = Network.newNode(this, Visibility.Network).
withComponent("drone"). withComponent("drone").
@ -46,7 +44,7 @@ class Drone(val agent: entity.Drone) extends prefab.ManagedEnvironment with Agen
override protected def suckableItems(side: EnumFacing) = entitiesInBlock(classOf[EntityItem], position) ++ super.suckableItems(side) override protected def suckableItems(side: EnumFacing) = entitiesInBlock(classOf[EntityItem], position) ++ super.suckableItems(side)
override protected def onSuckCollect(entity: EntityItem) = { override protected def onSuckCollect(entity: EntityItem) = {
if (InventoryUtils.insertIntoInventory(entity.getEntityItem, inventory, slots = Option(insertionSlots))) { if (InventoryUtils.insertIntoInventory(entity.getEntityItem, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots))) {
world.playSoundAtEntity(agent, "random.pop", 0.2f, ((world.rand.nextFloat - world.rand.nextFloat) * 0.7f + 1) * 2) world.playSoundAtEntity(agent, "random.pop", 0.2f, ((world.rand.nextFloat - world.rand.nextFloat) * 0.7f + 1) * 2)
} }
} }

View File

@ -77,7 +77,7 @@ class Trade(val info: TradeInfo) extends AbstractValue {
InventoryUtils.extractFromInventory(stack, inventory, null, simulate = true).stackSize == 0 InventoryUtils.extractFromInventory(stack, inventory, null, simulate = true).stackSize == 0
def hasRoomForItemStack(stack: ItemStack) = { def hasRoomForItemStack(stack: ItemStack) = {
val remainder = stack.copy() val remainder = stack.copy()
InventoryUtils.insertIntoInventory(remainder, inventory, None, remainder.stackSize, simulate = true) InventoryUtils.insertIntoInventory(remainder, InventoryUtils.asItemHandler(inventory), remainder.stackSize, simulate = true)
remainder.stackSize == 0 remainder.stackSize == 0
} }
@ -87,9 +87,9 @@ class Trade(val info: TradeInfo) extends AbstractValue {
val outputStack = recipe.getItemToSell.copy() val outputStack = recipe.getItemToSell.copy()
if (hasRoomForItemStack(outputStack)) { if (hasRoomForItemStack(outputStack)) {
// We established that out inventory allows to perform the trade, now actually do the trade. // We established that out inventory allows to perform the trade, now actually do the trade.
InventoryUtils.extractFromInventory(firstInputStack, inventory, null) InventoryUtils.extractFromInventory(firstInputStack, InventoryUtils.asItemHandler(inventory))
secondInputStack.map(InventoryUtils.extractFromInventory(_, inventory, null)) secondInputStack.map(InventoryUtils.extractFromInventory(_, InventoryUtils.asItemHandler(inventory)))
InventoryUtils.insertIntoInventory(outputStack, inventory, None, outputStack.stackSize) InventoryUtils.insertIntoInventory(outputStack, InventoryUtils.asItemHandler(inventory), outputStack.stackSize)
// Tell the merchant we used the recipe, so MC can disable it and/or enable more recipes. // Tell the merchant we used the recipe, so MC can disable it and/or enable more recipes.
info.merchant.get.orNull.useRecipe(recipe) info.merchant.get.orNull.useRecipe(recipe)

View File

@ -25,8 +25,8 @@ trait InventoryTransfer extends traits.WorldAware with traits.SideRestricted {
result(Unit, reason) result(Unit, reason)
case _ => case _ =>
if (args.count > 3) { if (args.count > 3) {
val sourceSlot = args.checkSlot(InventoryUtils.inventoryAt(sourcePos).getOrElse(throw new IllegalArgumentException("no inventory")), 3) val sourceSlot = args.checkSlot(InventoryUtils.inventoryAt(sourcePos, sourceSide).getOrElse(throw new IllegalArgumentException("no inventory")), 3)
val sinkSlot = args.optSlot(InventoryUtils.inventoryAt(sinkPos).getOrElse(throw new IllegalArgumentException("no inventory")), 4, -1) val sinkSlot = args.optSlot(InventoryUtils.inventoryAt(sinkPos, sinkSide).getOrElse(throw new IllegalArgumentException("no inventory")), 4, -1)
result(InventoryUtils.transferBetweenInventoriesSlotsAt(sourcePos, sourceSide.getOpposite, sourceSlot, sinkPos, Option(sinkSide.getOpposite), if (sinkSlot < 0) None else Option(sinkSlot), count)) result(InventoryUtils.transferBetweenInventoriesSlotsAt(sourcePos, sourceSide.getOpposite, sourceSlot, sinkPos, Option(sinkSide.getOpposite), if (sinkSlot < 0) None else Option(sinkSlot), count))
} }

View File

@ -39,9 +39,9 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val stack = inventory.getStackInSlot(selectedSlot) val stack = inventory.getStackInSlot(selectedSlot)
if (stack != null && stack.stackSize > 0) { if (stack != null && stack.stackSize > 0) {
val blockPos = position.offset(facing) val blockPos = position.offset(facing)
InventoryUtils.inventoryAt(blockPos) match { InventoryUtils.inventoryAt(blockPos, facing.getOpposite) match {
case Some(inv) if inv.isUseableByPlayer(fakePlayer) && mayInteract(blockPos, facing.getOpposite) => case Some(inv) if mayInteract(blockPos, facing.getOpposite, inv) =>
if (!InventoryUtils.insertIntoInventory(stack, inv, Option(facing.getOpposite), count)) { if (!InventoryUtils.insertIntoInventory(stack, inv, count)) {
// Cannot drop into that inventory. // Cannot drop into that inventory.
return result(false, "inventory full") return result(false, "inventory full")
} }
@ -74,8 +74,8 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val count = args.optItemCount(1) val count = args.optItemCount(1)
val blockPos = position.offset(facing) val blockPos = position.offset(facing)
if (InventoryUtils.inventoryAt(blockPos).exists(inventory => { if (InventoryUtils.inventoryAt(blockPos, facing.getOpposite).exists(inventory => {
inventory.isUseableByPlayer(fakePlayer) && mayInteract(blockPos, facing.getOpposite) && InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, facing.getOpposite, count) mayInteract(blockPos, facing.getOpposite) && InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, InventoryUtils.asItemHandler(this.inventory), slots = Option(insertionSlots)), inventory, count)
})) { })) {
context.pause(Settings.get.suckDelay) context.pause(Settings.get.suckDelay)
result(true) result(true)

View File

@ -8,8 +8,8 @@ import li.cil.oc.server.component.result
import li.cil.oc.util.BlockPosition import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.InventoryUtils import li.cil.oc.util.InventoryUtils
import net.minecraft.inventory.IInventory
import net.minecraft.util.EnumFacing import net.minecraft.util.EnumFacing
import net.minecraftforge.items.IItemHandler
trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideRestricted { trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideRestricted {
@Callback(doc = """function(facing:number, slot:number[, count:number[, fromSide:number]]):boolean -- Drops the selected item stack into the specified slot of an inventory.""") @Callback(doc = """function(facing:number, slot:number[, count:number[, fromSide:number]]):boolean -- Drops the selected item stack into the specified slot of an inventory.""")
@ -21,7 +21,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR
if (stack != null && stack.stackSize > 0) { if (stack != null && stack.stackSize > 0) {
withInventory(position.offset(facing), fromSide, inventory => { withInventory(position.offset(facing), fromSide, inventory => {
val slot = args.checkSlot(inventory, 1) val slot = args.checkSlot(inventory, 1)
if (!InventoryUtils.insertIntoInventorySlot(stack, inventory, Option(fromSide), slot, count)) { if (!InventoryUtils.insertIntoInventorySlot(stack, inventory, slot, count)) {
// Cannot drop into that inventory. // Cannot drop into that inventory.
return result(false, "inventory full/invalid slot") return result(false, "inventory full/invalid slot")
} }
@ -49,7 +49,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR
val fromSide = args.optSideAny(3, facing.getOpposite) val fromSide = args.optSideAny(3, facing.getOpposite)
withInventory(position.offset(facing), fromSide, inventory => { withInventory(position.offset(facing), fromSide, inventory => {
val slot = args.checkSlot(inventory, 1) val slot = args.checkSlot(inventory, 1)
if (InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, fromSide, slot, count)) { if (InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, InventoryUtils.asItemHandler(this.inventory), slots = Option(insertionSlots)), inventory, slot, count)) {
context.pause(Settings.get.suckDelay) context.pause(Settings.get.suckDelay)
result(true) result(true)
} }
@ -57,9 +57,9 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR
}) })
} }
private def withInventory(blockPos: BlockPosition, fromSide: EnumFacing, f: IInventory => Array[AnyRef]) = private def withInventory(blockPos: BlockPosition, fromSide: EnumFacing, f: IItemHandler => Array[AnyRef]) =
InventoryUtils.inventoryAt(blockPos) match { InventoryUtils.inventoryAt(blockPos, fromSide) match {
case Some(inventory) if inventory.isUseableByPlayer(fakePlayer) && mayInteract(blockPos, fromSide) => f(inventory) case Some(inventory) if mayInteract(blockPos, fromSide) => f(inventory)
case _ => result(Unit, "no inventory") case _ => result(Unit, "no inventory")
} }
} }

View File

@ -7,21 +7,21 @@ import li.cil.oc.api.machine.Context
import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.InventoryUtils import li.cil.oc.util.InventoryUtils
import li.cil.oc.util.ResultWrapper.result import li.cil.oc.util.ResultWrapper.result
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraftforge.items.IItemHandler
trait ItemInventoryControl extends InventoryAware { trait ItemInventoryControl extends InventoryAware {
@Callback(doc = "function(slot:number):number -- The size of an item inventory in the specified slot.") @Callback(doc = "function(slot:number):number -- The size of an item inventory in the specified slot.")
def getItemInventorySize(context: Context, args: Arguments): Array[AnyRef] = { def getItemInventorySize(context: Context, args: Arguments): Array[AnyRef] = {
withItemInventory(args.checkSlot(inventory, 0), itemInventory => result(itemInventory.getSizeInventory)) withItemInventory(args.checkSlot(inventory, 0), itemInventory => result(itemInventory.getSlots))
} }
@Callback(doc = "function(inventorySlot:number, slot:number[, count:number=64]):number -- Drops an item into the specified slot in the item inventory.") @Callback(doc = "function(inventorySlot:number, slot:number[, count:number=64]):number -- Drops an item from the selected slot into the specified slot in the item inventory.")
def dropIntoItemInventory(context: Context, args: Arguments): Array[AnyRef] = { def dropIntoItemInventory(context: Context, args: Arguments): Array[AnyRef] = {
withItemInventory(args.checkSlot(inventory, 0), itemInventory => { withItemInventory(args.checkSlot(inventory, 0), itemInventory => {
val slot = args.checkSlot(itemInventory, 1) val slot = args.checkSlot(itemInventory, 1)
val count = args.optItemCount(2) val count = args.optItemCount(2)
result(InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventorySlot(_, itemInventory, None, slot), inventory, null, count)) result(InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventorySlot(_, itemInventory, slot), inventory, null, selectedSlot, count))
}) })
} }
@ -30,14 +30,14 @@ trait ItemInventoryControl extends InventoryAware {
withItemInventory(args.checkSlot(inventory, 0), itemInventory => { withItemInventory(args.checkSlot(inventory, 0), itemInventory => {
val slot = args.checkSlot(itemInventory, 1) val slot = args.checkSlot(itemInventory, 1)
val count = args.optItemCount(2) val count = args.optItemCount(2)
result(InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, inventory, slots = Option(insertionSlots)), itemInventory, null, slot, count)) result(InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots)), itemInventory, slot, count))
}) })
} }
private def withItemInventory(slot: Int, f: IInventory => Array[AnyRef]): Array[AnyRef] = { private def withItemInventory(slot: Int, f: IItemHandler => Array[AnyRef]): Array[AnyRef] = {
inventory.getStackInSlot(slot) match { inventory.getStackInSlot(slot) match {
case stack: ItemStack => api.Driver.inventoryFor(stack, fakePlayer) match { case stack: ItemStack => api.Driver.itemHandlerFor(stack, fakePlayer) match {
case inventory: IInventory => f(inventory) case inventory: IItemHandler => f(inventory)
case _ => result(0, "no item inventory") case _ => result(0, "no item inventory")
} }
case _ => result(0, "no item inventory") case _ => result(0, "no item inventory")

View File

@ -51,7 +51,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware
else { else {
into.fill(contents, true) into.fill(contents, true)
inventory.decrStackSize(selectedSlot, 1) inventory.decrStackSize(selectedSlot, 1)
InventoryUtils.insertIntoInventory(container, inventory, slots = Option(insertionSlots)) InventoryUtils.insertIntoInventory(container, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots))
if (container.stackSize > 0) { if (container.stackSize > 0) {
InventoryUtils.spawnStackInWorld(position, container) InventoryUtils.spawnStackInWorld(position, container)
} }
@ -91,7 +91,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware
val amount = FluidContainerRegistry.getFluidForFilledItem(filled).amount val amount = FluidContainerRegistry.getFluidForFilledItem(filled).amount
from.drain(amount, true) from.drain(amount, true)
inventory.decrStackSize(selectedSlot, 1) inventory.decrStackSize(selectedSlot, 1)
InventoryUtils.insertIntoInventory(filled, inventory, slots = Option(insertionSlots)) InventoryUtils.insertIntoInventory(filled, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots))
if (filled.stackSize > 0) { if (filled.stackSize > 0) {
InventoryUtils.spawnStackInWorld(position, filled) InventoryUtils.spawnStackInWorld(position, filled)
} }
@ -128,6 +128,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware
case _ => None case _ => None
} }
} }
inventory.getStackInSlot(slot) match { inventory.getStackInSlot(slot) match {
case stack: ItemStack => fluidInfo(stack) match { case stack: ItemStack => fluidInfo(stack) match {
case Some((fluid, capacity)) => f(fluid, capacity) case Some((fluid, capacity)) => f(fluid, capacity)

View File

@ -19,6 +19,7 @@ import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action
import net.minecraftforge.event.world.BlockEvent import net.minecraftforge.event.world.BlockEvent
import net.minecraftforge.fluids.FluidRegistry import net.minecraftforge.fluids.FluidRegistry
import net.minecraftforge.fml.common.eventhandler.Event.Result import net.minecraftforge.fml.common.eventhandler.Event.Result
import net.minecraftforge.items.IItemHandler
trait WorldAware { trait WorldAware {
def position: BlockPosition def position: BlockPosition
@ -44,6 +45,8 @@ trait WorldAware {
} }
} }
def mayInteract(blockPos: BlockPosition, side: EnumFacing, inventory: IItemHandler): Boolean = mayInteract(blockPos, side) // This uses the inventory object in 1.9+
def entitiesInBounds[Type <: Entity](clazz: Class[Type], bounds: AxisAlignedBB) = { def entitiesInBounds[Type <: Entity](clazz: Class[Type], bounds: AxisAlignedBB) = {
world.getEntitiesWithinAABB(clazz, bounds) world.getEntitiesWithinAABB(clazz, bounds)
} }

View File

@ -8,16 +8,16 @@ import li.cil.oc.server.component.result
import li.cil.oc.util.DatabaseAccess import li.cil.oc.util.DatabaseAccess
import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.InventoryUtils import li.cil.oc.util.InventoryUtils
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.util.EnumFacing import net.minecraft.util.EnumFacing
import net.minecraftforge.items.IItemHandler
import net.minecraftforge.oredict.OreDictionary import net.minecraftforge.oredict.OreDictionary
trait WorldInventoryAnalytics extends WorldAware with SideRestricted with NetworkAware { trait WorldInventoryAnalytics extends WorldAware with SideRestricted with NetworkAware {
@Callback(doc = """function(side:number):number -- Get the number of slots in the inventory on the specified side of the device.""") @Callback(doc = """function(side:number):number -- Get the number of slots in the inventory on the specified side of the device.""")
def getInventorySize(context: Context, args: Arguments): Array[AnyRef] = { def getInventorySize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0) val facing = checkSideForAction(args, 0)
withInventory(facing, inventory => result(inventory.getSizeInventory)) withInventory(facing, inventory => result(inventory.getSlots))
} }
@Callback(doc = """function(side:number, slot:number):number -- Get number of items in the specified slot of the inventory on the specified side of the device.""") @Callback(doc = """function(side:number, slot:number):number -- Get number of items in the specified slot of the inventory on the specified side of the device.""")
@ -89,9 +89,9 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ
withInventory(facing, inventory => store(inventory.getStackInSlot(args.checkSlot(inventory, 1)))) withInventory(facing, inventory => store(inventory.getStackInSlot(args.checkSlot(inventory, 1))))
} }
private def withInventory(side: EnumFacing, f: IInventory => Array[AnyRef]) = private def withInventory(side: EnumFacing, f: IItemHandler => Array[AnyRef]) =
InventoryUtils.inventoryAt(position.offset(side)) match { InventoryUtils.inventoryAt(position.offset(side), side.getOpposite) match {
case Some(inventory) if inventory.isUseableByPlayer(fakePlayer) && mayInteract(position.offset(side), side.getOpposite) => f(inventory) case Some(inventory) if mayInteract(position.offset(side), side.getOpposite, inventory) => f(inventory)
case _ => result(Unit, "no inventory") case _ => result(Unit, "no inventory")
} }
} }

View File

@ -11,12 +11,15 @@ import li.cil.oc.api.driver.item.HostAware
import li.cil.oc.api.machine.Value import li.cil.oc.api.machine.Value
import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.api.network.ManagedEnvironment import li.cil.oc.api.network.ManagedEnvironment
import li.cil.oc.util.InventoryUtils
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.util.BlockPos import net.minecraft.util.BlockPos
import net.minecraft.util.EnumFacing import net.minecraft.util.EnumFacing
import net.minecraft.world.World import net.minecraft.world.World
import net.minecraftforge.items.CapabilityItemHandler
import net.minecraftforge.items.IItemHandler
import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsJava._
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
@ -143,10 +146,20 @@ private[oc] object Registry extends api.detail.DriverAPI {
}.orNull }.orNull
} }
@Deprecated
override def inventoryFor(stack: ItemStack, player: EntityPlayer):IInventory = { override def inventoryFor(stack: ItemStack, player: EntityPlayer):IInventory = {
OpenComputers.log.warn("A mod is using the deprecated method li.cil.oc.api.Driver.inventoryFor; use itemHandlerFor instead.")
null
}
override def itemHandlerFor(stack: ItemStack, player: EntityPlayer): IItemHandler = {
inventoryProviders.find(provider => provider.worksWith(stack, player)). inventoryProviders.find(provider => provider.worksWith(stack, player)).
map(provider => provider.getInventory(stack, player)). map(provider => InventoryUtils.asItemHandler(provider.getInventory(stack, player))).
orNull getOrElse {
if(stack.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null))
stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)
else null
}
} }
override def blockDrivers = blocks.toSeq override def blockDrivers = blocks.toSeq

View File

@ -4,6 +4,7 @@ import li.cil.oc.api.internal.MultiTank
import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Arguments
import net.minecraft.inventory.IInventory import net.minecraft.inventory.IInventory
import net.minecraft.util.EnumFacing import net.minecraft.util.EnumFacing
import net.minecraftforge.items.IItemHandler
import net.minecraftforge.fluids.FluidContainerRegistry import net.minecraftforge.fluids.FluidContainerRegistry
import scala.language.implicitConversions import scala.language.implicitConversions
@ -21,19 +22,23 @@ object ExtendedArguments {
if (!isDefined(index) || !hasValue(index)) default if (!isDefined(index) || !hasValue(index)) default
else math.max(0, args.checkInteger(index)) else math.max(0, args.checkInteger(index))
def checkSlot(inventory: IInventory, n: Int) = { def checkSlot(inventory: IItemHandler, n: Int): Int = {
val slot = args.checkInteger(n) - 1 val slot = args.checkInteger(n) - 1
if (slot < 0 || slot >= inventory.getSizeInventory) { if (slot < 0 || slot >= inventory.getSlots) {
throw new IllegalArgumentException("invalid slot") throw new IllegalArgumentException("invalid slot")
} }
slot slot
} }
def optSlot(inventory: IInventory, index: Int, default: Int) = { def optSlot(inventory: IItemHandler, index: Int, default: Int): Int = {
if (!isDefined(index)) default if (!isDefined(index)) default
else checkSlot(inventory, index) else checkSlot(inventory, index)
} }
def checkSlot(inventory: IInventory, n: Int): Int = checkSlot(InventoryUtils.asItemHandler(inventory), n)
def optSlot(inventory: IInventory, index: Int, default: Int): Int = optSlot(InventoryUtils.asItemHandler(inventory), index, default)
def checkTank(multi: MultiTank, n: Int) = { def checkTank(multi: MultiTank, n: Int) = {
val tank = args.checkInteger(n) - 1 val tank = args.checkInteger(n) - 1
if (tank < 0 || tank >= multi.tankCount) { if (tank < 0 || tank >= multi.tankCount) {

View File

@ -1,25 +1,38 @@
package li.cil.oc.util package li.cil.oc.util
import li.cil.oc.OpenComputers
import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ExtendedWorld._
import net.minecraft.block.BlockChest import net.minecraft.entity.Entity
import net.minecraft.entity.item.EntityItem import net.minecraft.entity.item.EntityItem
import net.minecraft.entity.item.EntityMinecartContainer
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory import net.minecraft.inventory.IInventory
import net.minecraft.inventory.ISidedInventory import net.minecraft.inventory.ISidedInventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntityChest import net.minecraft.tileentity.TileEntity
import net.minecraft.util.EnumFacing import net.minecraft.util.EnumFacing
import net.minecraftforge.items.CapabilityItemHandler
import net.minecraftforge.items.IItemHandler
import net.minecraftforge.items.IItemHandlerModifiable
import net.minecraftforge.items.wrapper.InvWrapper
import net.minecraftforge.items.wrapper.SidedInvWrapper
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
object InventoryUtils { object InventoryUtils {
def asItemHandler(inventory: IInventory, side: EnumFacing): IItemHandlerModifiable = inventory match {
case inv: ISidedInventory if side != null => new SidedInvWrapper(inv, side)
case _ => new InvWrapper(inventory)
}
def asItemHandler(inventory: IInventory): IItemHandlerModifiable = asItemHandler(inventory, null)
/** /**
* Check if two item stacks are of equal type, ignoring the stack size. * Check if two item stacks are of equal type, ignoring the stack size.
* <p/> * <p/>
* Optionally check for equality in NBT data. * Optionally check for equality in NBT data.
*/ */
def haveSameItemType(stackA: ItemStack, stackB: ItemStack, checkNBT: Boolean = false) = def haveSameItemType(stackA: ItemStack, stackB: ItemStack, checkNBT: Boolean = false): Boolean =
stackA != null && stackB != null && stackA != null && stackB != null &&
stackA.getItem == stackB.getItem && stackA.getItem == stackB.getItem &&
(!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage) && (!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage) &&
@ -31,15 +44,28 @@ object InventoryUtils {
* This performs special handling for (double-)chests and also checks for * This performs special handling for (double-)chests and also checks for
* mine carts with chests. * mine carts with chests.
*/ */
def inventoryAt(position: BlockPosition): Option[IInventory] = position.world match { def inventoryAt(position: BlockPosition, side: EnumFacing): Option[IItemHandler] = position.world match {
case Some(world) if world.blockExists(position) => (world.getBlock(position), world.getTileEntity(position)) match { case Some(world) if world.blockExists(position) => world.getTileEntity(position) match {
case (block: BlockChest, chest: TileEntityChest) => Option(block.getLockableContainer(world, chest.getPos)) case tile: TileEntity if tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side) => Option(tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side))
case (_, inventory: IInventory) => Some(inventory) case tile: IInventory => Option(asItemHandler(tile))
case _ => world.getEntitiesWithinAABB(classOf[EntityMinecartContainer], position.bounds).find(!_.isDead) case _ => world.getEntitiesWithinAABB(classOf[Entity], position.bounds)
.filter(e => !e.isDead && e.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side))
.map(_.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side))
.find(_ != null)
} }
case _ => None case _ => None
} }
def anyInventoryAt(position: BlockPosition): Option[IItemHandler] = {
for(side <- null :: EnumFacing.VALUES.toList) {
inventoryAt(position, side) match {
case inv: Some[IItemHandler] => return inv
case _ =>
}
}
None
}
/** /**
* Inserts a stack into an inventory. * Inserts a stack into an inventory.
* <p/> * <p/>
@ -61,40 +87,31 @@ object InventoryUtils {
* The number of items inserted can be limited, to avoid unnecessary * The number of items inserted can be limited, to avoid unnecessary
* changes to the inventory the stack may come from, for example. * changes to the inventory the stack may come from, for example.
*/ */
def insertIntoInventorySlot(stack: ItemStack, inventory: IInventory, side: Option[EnumFacing], slot: Int, limit: Int = 64, simulate: Boolean = false) = def insertIntoInventorySlot(stack: ItemStack, inventory: IItemHandler, slot: Int, limit: Int = 64, simulate: Boolean = false): Boolean =
(stack != null && limit > 0) && { (stack != null && limit > 0 && stack.stackSize > 0) && {
val isSideValidForSlot = (inventory, side) match { val amount = math.min(stack.stackSize, limit)
case (inventory: ISidedInventory, Some(s)) => inventory.canInsertItem(slot, stack, s) if (simulate) {
val toInsert = stack.copy()
toInsert.stackSize = amount
inventory.insertItem(slot, toInsert, simulate) match {
case remaining: ItemStack => remaining.stackSize < amount
case _ => true case _ => true
} }
(stack.stackSize > 0 && inventory.isItemValidForSlot(slot, stack) && isSideValidForSlot) && { } else {
val maxStackSize = math.min(inventory.getInventoryStackLimit, stack.getMaxStackSize) val toInsert = stack.splitStack(amount)
val existing = inventory.getStackInSlot(slot) inventory.insertItem(slot, toInsert, simulate) match {
val shouldMerge = existing != null && existing.stackSize < maxStackSize && case remaining: ItemStack =>
existing.isItemEqual(stack) && ItemStack.areItemStackTagsEqual(existing, stack) val result = remaining.stackSize < amount
if (shouldMerge) { stack.stackSize = remaining.stackSize
val space = maxStackSize - existing.stackSize result
val amount = math.min(space, math.min(stack.stackSize, limit)) case _ => true
stack.stackSize -= amount
if (simulate) amount > 0
else {
existing.stackSize += amount
inventory.markDirty()
true
}
}
else (existing == null) && {
val amount = math.min(maxStackSize, math.min(stack.stackSize, limit))
val inserted = stack.splitStack(amount)
if (simulate) amount > 0
else {
inventory.setInventorySlotContents(slot, inserted)
true
}
} }
} }
} }
def insertIntoInventorySlot(stack: ItemStack, inventory: IInventory, side: Option[EnumFacing], slot: Int, limit: Int, simulate: Boolean): Boolean =
insertIntoInventorySlot(stack, asItemHandler(inventory, side.orNull), slot, limit, simulate)
/** /**
* Extracts a stack from an inventory. * Extracts a stack from an inventory.
* <p/> * <p/>
@ -120,31 +137,30 @@ object InventoryUtils {
* also be achieved by a check in the consumer, but it saves some unnecessary * also be achieved by a check in the consumer, but it saves some unnecessary
* code repetition this way. * code repetition this way.
*/ */
def extractFromInventorySlot(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, slot: Int, limit: Int = 64) = { def extractFromInventorySlot(consumer: (ItemStack) => Unit, inventory: IItemHandler, slot: Int, limit: Int = 64): Boolean = {
val stack = inventory.getStackInSlot(slot) val stack = inventory.getStackInSlot(slot)
(stack != null && limit > 0) && { (stack != null && limit > 0 && stack.stackSize > 0) && {
val isSideValidForSlot = inventory match { var amount = math.min(stack.getMaxStackSize, math.min(stack.stackSize, limit))
case inventory: ISidedInventory => inventory.canExtractItem(slot, stack, side) inventory.extractItem(slot, amount, true) match {
case _ => true case extracted: ItemStack =>
} amount = extracted.stackSize
(stack.stackSize > 0 && isSideValidForSlot) && {
val maxStackSize = math.min(inventory.getInventoryStackLimit, stack.getMaxStackSize)
val amount = math.min(maxStackSize, math.min(stack.stackSize, limit))
val extracted = stack.splitStack(amount)
consumer(extracted) consumer(extracted)
val success = extracted.stackSize < amount if(extracted.stackSize >= amount) return false
stack.stackSize += extracted.stackSize inventory.extractItem(slot, amount - extracted.stackSize, false) match {
if (stack.stackSize == 0) { case realExtracted: ItemStack if realExtracted.stackSize == amount - extracted.stackSize => true
inventory.setInventorySlotContents(slot, null) case _ =>
OpenComputers.log.warn("Items may have been duplicated during inventory extraction. This means an IItemHandler instance acted differently between simulated and non-simulated extraction. Offender: " + inventory)
true
} }
else if (success) { case _ => false
inventory.markDirty()
}
success
} }
} }
} }
def extractFromInventorySlot(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, slot: Int, limit: Int): Boolean = {
extractFromInventorySlot(consumer, asItemHandler(inventory, side), slot, limit)
}
/** /**
* Inserts a stack into an inventory. * Inserts a stack into an inventory.
* <p/> * <p/>
@ -160,14 +176,11 @@ object InventoryUtils {
* item stack will be adjusted to reflect the number items inserted, by * item stack will be adjusted to reflect the number items inserted, by
* having its size decremented accordingly. * having its size decremented accordingly.
*/ */
def insertIntoInventory(stack: ItemStack, inventory: IInventory, side: Option[EnumFacing] = None, limit: Int = 64, simulate: Boolean = false, slots: Option[Iterable[Int]] = None) = def insertIntoInventory(stack: ItemStack, inventory: IItemHandler, limit: Int = 64, simulate: Boolean = false, slots: Option[Iterable[Int]] = None): Boolean =
(stack != null && limit > 0) && { (stack != null && limit > 0 && stack.stackSize > 0) && {
var success = false var success = false
var remaining = limit var remaining = limit
val range = slots.getOrElse(inventory match { val range = slots.getOrElse(0 until inventory.getSlots)
case sided: ISidedInventory => sided.getSlotsForFace(side.orNull).toIterable
case _ => 0 until inventory.getSizeInventory
})
if (range.nonEmpty) { if (range.nonEmpty) {
// This is a special case for inserting with an explicit ordering, // This is a special case for inserting with an explicit ordering,
@ -176,26 +189,15 @@ object InventoryUtils {
// slot, if at all possible, over merging. // slot, if at all possible, over merging.
if (slots.isDefined) { if (slots.isDefined) {
val stackSize = stack.stackSize val stackSize = stack.stackSize
if ((inventory.getStackInSlot(range.head) == null) && insertIntoInventorySlot(stack, inventory, side, range.head, remaining, simulate)) { if (insertIntoInventorySlot(stack, inventory, range.head, remaining, simulate)) {
remaining -= stackSize - stack.stackSize remaining -= stackSize - stack.stackSize
success = true success = true
} }
} }
val shouldTryMerge = !stack.isItemStackDamageable && stack.getMaxStackSize > 1 && inventory.getInventoryStackLimit > 1
if (shouldTryMerge) {
for (slot <- range) { for (slot <- range) {
val stackSize = stack.stackSize val stackSize = stack.stackSize
if ((inventory.getStackInSlot(slot) != null) && insertIntoInventorySlot(stack, inventory, side, slot, remaining, simulate)) { if (insertIntoInventorySlot(stack, inventory, slot, remaining, simulate)) {
remaining -= stackSize - stack.stackSize
success = true
}
}
}
for (slot <- range) {
val stackSize = stack.stackSize
if ((inventory.getStackInSlot(slot) == null) && insertIntoInventorySlot(stack, inventory, side, slot, remaining, simulate)) {
remaining -= stackSize - stack.stackSize remaining -= stackSize - stack.stackSize
success = true success = true
} }
@ -205,6 +207,9 @@ object InventoryUtils {
success success
} }
def insertIntoInventory(stack: ItemStack, inventory: IInventory, side: Option[EnumFacing], limit: Int, simulate: Boolean, slots: Option[Iterable[Int]]): Boolean =
insertIntoInventory(stack, asItemHandler(inventory, side.orNull), limit, simulate, slots)
/** /**
* Extracts a slot from an inventory. * Extracts a slot from an inventory.
* <p/> * <p/>
@ -216,13 +221,11 @@ object InventoryUtils {
* <p/> * <p/>
* This returns <tt>true</tt> if at least one item was extracted. * This returns <tt>true</tt> if at least one item was extracted.
*/ */
def extractAnyFromInventory(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, limit: Int = 64) = { def extractAnyFromInventory(consumer: (ItemStack) => Unit, inventory: IItemHandler, limit: Int = 64): Boolean =
val range = inventory match { (0 until inventory.getSlots).exists(slot => extractFromInventorySlot(consumer, inventory, slot, limit))
case sided: ISidedInventory => sided.getSlotsForFace(side).toIterable
case _ => 0 until inventory.getSizeInventory def extractAnyFromInventory(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, limit: Int): Boolean =
} extractAnyFromInventory(consumer, asItemHandler(inventory, side), limit)
range.exists(slot => extractFromInventorySlot(consumer, inventory, side, slot, limit))
}
/** /**
* Extracts an item stack from an inventory. * Extracts an item stack from an inventory.
@ -233,13 +236,9 @@ object InventoryUtils {
* This uses the <tt>extractFromInventorySlot</tt> method, and therefore * This uses the <tt>extractFromInventorySlot</tt> method, and therefore
* handles special cases such as sided inventories and stack size limits. * handles special cases such as sided inventories and stack size limits.
*/ */
def extractFromInventory(stack: ItemStack, inventory: IInventory, side: EnumFacing, simulate: Boolean = false) = { def extractFromInventory(stack: ItemStack, inventory: IItemHandler, simulate: Boolean = false): ItemStack = {
val range = inventory match {
case sided: ISidedInventory => sided.getSlotsForFace(side).toIterable
case _ => 0 until inventory.getSizeInventory
}
val remaining = stack.copy() val remaining = stack.copy()
for (slot <- range if remaining.stackSize > 0) { for (slot <- 0 until inventory.getSlots if remaining.stackSize > 0) {
extractFromInventorySlot(stack => { extractFromInventorySlot(stack => {
if (haveSameItemType(remaining, stack, checkNBT = true)) { if (haveSameItemType(remaining, stack, checkNBT = true)) {
val transferred = stack.stackSize min remaining.stackSize val transferred = stack.stackSize min remaining.stackSize
@ -248,24 +247,27 @@ object InventoryUtils {
stack.stackSize -= transferred stack.stackSize -= transferred
} }
} }
}, inventory, side, slot, remaining.stackSize) }, inventory, slot, limit = remaining.stackSize)
} }
remaining remaining
} }
def extractFromInventory(stack: ItemStack, inventory: IInventory, side: EnumFacing, simulate: Boolean): ItemStack =
extractFromInventory(stack, asItemHandler(inventory, side), simulate)
/** /**
* Utility method for calling <tt>insertIntoInventory</tt> on an inventory * Utility method for calling <tt>insertIntoInventory</tt> on an inventory
* in the world. * in the world.
*/ */
def insertIntoInventoryAt(stack: ItemStack, position: BlockPosition, side: Option[EnumFacing] = None, limit: Int = 64, simulate: Boolean = false): Boolean = def insertIntoInventoryAt(stack: ItemStack, position: BlockPosition, side: Option[EnumFacing] = None, limit: Int = 64, simulate: Boolean = false): Boolean =
inventoryAt(position).exists(insertIntoInventory(stack, _, side, limit, simulate)) inventoryAt(position, side.orNull).exists(insertIntoInventory(stack, _, limit, simulate))
/** /**
* Utility method for calling <tt>extractFromInventory</tt> on an inventory * Utility method for calling <tt>extractFromInventory</tt> on an inventory
* in the world. * in the world.
*/ */
def extractFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64) = def extractFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64): Boolean =
inventoryAt(position).exists(extractAnyFromInventory(consumer, _, side, limit)) inventoryAt(position, side).exists(extractAnyFromInventory(consumer, _, limit))
/** /**
* Transfers some items between two inventories. * Transfers some items between two inventories.
@ -280,46 +282,52 @@ object InventoryUtils {
* <p/> * <p/>
* This returns <tt>true</tt> if at least one item was transferred. * This returns <tt>true</tt> if at least one item was transferred.
*/ */
def transferBetweenInventories(source: IInventory, sourceSide: EnumFacing, sink: IInventory, sinkSide: Option[EnumFacing], limit: Int = 64) = def transferBetweenInventories(source: IItemHandler, sink: IItemHandler, limit: Int = 64): Boolean =
extractAnyFromInventory( extractAnyFromInventory(
insertIntoInventory(_, sink, sinkSide, limit), source, sourceSide, limit) insertIntoInventory(_, sink, limit), source, limit = limit)
def transferBetweenInventories(source: IInventory, sourceSide: EnumFacing, sink: IInventory, sinkSide: Option[EnumFacing], limit: Int): Boolean =
transferBetweenInventories(asItemHandler(source, sourceSide), asItemHandler(sink, sinkSide.orNull), limit)
/** /**
* Like <tt>transferBetweenInventories</tt> but moving between specific slots. * Like <tt>transferBetweenInventories</tt> but moving between specific slots.
*/ */
def transferBetweenInventoriesSlots(source: IInventory, sourceSide: EnumFacing, sourceSlot: Int, sink: IInventory, sinkSide: Option[EnumFacing], sinkSlot: Option[Int], limit: Int = 64) = def transferBetweenInventoriesSlots(source: IItemHandler, sourceSlot: Int, sink: IItemHandler, sinkSlot: Option[Int], limit: Int = 64): Boolean =
sinkSlot match { sinkSlot match {
case Some(explicitSinkSlot) => case Some(explicitSinkSlot) =>
extractFromInventorySlot( extractFromInventorySlot(
insertIntoInventorySlot(_, sink, sinkSide, explicitSinkSlot, limit), source, sourceSide, sourceSlot, limit) insertIntoInventorySlot(_, sink, explicitSinkSlot, limit), source, sourceSlot, limit = limit)
case _ => case _ =>
extractFromInventorySlot( extractFromInventorySlot(
insertIntoInventory(_, sink, sinkSide, limit), source, sourceSide, sourceSlot, limit) insertIntoInventory(_, sink, limit), source, sourceSlot, limit = limit)
} }
def transferBetweenInventoriesSlots(source: IInventory, sourceSide: EnumFacing, sourceSlot: Int, sink: IInventory, sinkSide: Option[EnumFacing], sinkSlot: Option[Int], limit: Int): Boolean =
transferBetweenInventoriesSlots(asItemHandler(source, sourceSide), sourceSlot, asItemHandler(sink, sinkSide.orNull), sinkSlot, limit)
/** /**
* Utility method for calling <tt>transferBetweenInventories</tt> on inventories * Utility method for calling <tt>transferBetweenInventories</tt> on inventories
* in the world. * in the world.
*/ */
def transferBetweenInventoriesAt(source: BlockPosition, sourceSide: EnumFacing, sink: BlockPosition, sinkSide: Option[EnumFacing], limit: Int = 64) = def transferBetweenInventoriesAt(source: BlockPosition, sourceSide: EnumFacing, sink: BlockPosition, sinkSide: Option[EnumFacing], limit: Int = 64): Boolean =
inventoryAt(source).exists(sourceInventory => inventoryAt(source, sourceSide).exists(sourceInventory =>
inventoryAt(sink).exists(sinkInventory => inventoryAt(sink, sinkSide.orNull).exists(sinkInventory =>
transferBetweenInventories(sourceInventory, sourceSide, sinkInventory, sinkSide, limit))) transferBetweenInventories(sourceInventory, sinkInventory, limit)))
/** /**
* Utility method for calling <tt>transferBetweenInventoriesSlots</tt> on inventories * Utility method for calling <tt>transferBetweenInventoriesSlots</tt> on inventories
* in the world. * in the world.
*/ */
def transferBetweenInventoriesSlotsAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sourceSlot: Int, sinkPos: BlockPosition, sinkSide: Option[EnumFacing], sinkSlot: Option[Int], limit: Int = 64) = def transferBetweenInventoriesSlotsAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sourceSlot: Int, sinkPos: BlockPosition, sinkSide: Option[EnumFacing], sinkSlot: Option[Int], limit: Int = 64): Boolean =
inventoryAt(sourcePos).exists(sourceInventory => inventoryAt(sourcePos, sourceSide).exists(sourceInventory =>
inventoryAt(sinkPos).exists(sinkInventory => inventoryAt(sinkPos, sinkSide.orNull).exists(sinkInventory =>
transferBetweenInventoriesSlots(sourceInventory, sourceSide, sourceSlot, sinkInventory, sinkSide, sinkSlot, limit))) transferBetweenInventoriesSlots(sourceInventory, sourceSlot, sinkInventory, sinkSlot, limit)))
/** /**
* Utility method for dropping contents from a single inventory slot into * Utility method for dropping contents from a single inventory slot into
* the world. * the world.
*/ */
def dropSlot(position: BlockPosition, inventory: IInventory, slot: Int, count: Int, direction: Option[EnumFacing] = None) = { def dropSlot(position: BlockPosition, inventory: IInventory, slot: Int, count: Int, direction: Option[EnumFacing] = None): Boolean = {
Option(inventory.decrStackSize(slot, count)) match { Option(inventory.decrStackSize(slot, count)) match {
case Some(stack) if stack.stackSize > 0 => spawnStackInWorld(position, stack, direction); true case Some(stack) if stack.stackSize > 0 => spawnStackInWorld(position, stack, direction); true
case _ => false case _ => false