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.EnumFacing;
import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandler;
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/>
* 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>.
* <p/>
* 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 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)
return API.driver.inventoryFor(stack, player);
return API.driver.itemHandlerFor(stack, player);
return null;
}
@ -279,4 +290,4 @@ public final class Driver {
private Driver() {
}
}
}

View File

@ -13,6 +13,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandler;
import java.util.Collection;
@ -170,10 +171,16 @@ public interface DriverAPI {
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/>
* 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>.
* <p/>
* 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 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.

View File

@ -1,6 +1,7 @@
package li.cil.oc.server.agent
import li.cil.oc.api.internal
import li.cil.oc.util.ExtendedInventory._
import li.cil.oc.util.InventoryUtils
import net.minecraft.block.Block
import net.minecraft.entity.player.EntityPlayer
@ -9,9 +10,9 @@ import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.nbt.NBTTagList
import li.cil.oc.util.ExtendedInventory._
class Inventory(val agent: internal.Agent) extends InventoryPlayer(null) {
def selectedItemStack = agent.mainInventory.getStackInSlot(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) = {
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))

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.prefab
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.ExtendedArguments._
import li.cil.oc.util.ExtendedNBT._
@ -541,11 +542,11 @@ object DebugCard {
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 side = args.checkSideAny(7)
InventoryUtils.inventoryAt(position) match {
InventoryUtils.inventoryAt(position, side) match {
case Some(inventory) =>
val stack = new ItemStack(item, count, damage)
stack.setTagCompound(tag)
result(InventoryUtils.insertIntoInventory(stack, inventory, Option(side)))
result(InventoryUtils.insertIntoInventory(stack, inventory))
case _ => result(Unit, "no inventory")
}
}
@ -554,11 +555,11 @@ object DebugCard {
def removeItem(context: Context, args: Arguments): Array[AnyRef] = {
checkAccess()
val position = BlockPosition(args.checkDouble(0), args.checkDouble(1), args.checkDouble(2), world)
InventoryUtils.inventoryAt(position) match {
InventoryUtils.anyInventoryAt(position) match {
case Some(inventory) =>
val slot = args.checkSlot(inventory, 3)
val count = args.optInteger(4, inventory.getInventoryStackLimit)
val removed = inventory.decrStackSize(slot, count)
val count = args.optInteger(4, 64)
val removed = inventory.extractItem(slot, count, false)
if (removed == null) result(0)
else result(removed.stackSize)
case _ => result(Unit, "no inventory")

View File

@ -3,11 +3,11 @@ package li.cil.oc.server.component
import java.util
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.api.Network
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.Callback
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.WrapAsScala._
import scala.collection.convert.WrapAsJava._
class Drone(val agent: entity.Drone) extends prefab.ManagedEnvironment with Agent with DeviceInfo {
override val node = Network.newNode(this, Visibility.Network).
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 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)
}
}

View File

@ -77,7 +77,7 @@ class Trade(val info: TradeInfo) extends AbstractValue {
InventoryUtils.extractFromInventory(stack, inventory, null, simulate = true).stackSize == 0
def hasRoomForItemStack(stack: ItemStack) = {
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
}
@ -87,9 +87,9 @@ class Trade(val info: TradeInfo) extends AbstractValue {
val outputStack = recipe.getItemToSell.copy()
if (hasRoomForItemStack(outputStack)) {
// We established that out inventory allows to perform the trade, now actually do the trade.
InventoryUtils.extractFromInventory(firstInputStack, inventory, null)
secondInputStack.map(InventoryUtils.extractFromInventory(_, inventory, null))
InventoryUtils.insertIntoInventory(outputStack, inventory, None, outputStack.stackSize)
InventoryUtils.extractFromInventory(firstInputStack, InventoryUtils.asItemHandler(inventory))
secondInputStack.map(InventoryUtils.extractFromInventory(_, InventoryUtils.asItemHandler(inventory)))
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.
info.merchant.get.orNull.useRecipe(recipe)

View File

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

View File

@ -39,9 +39,9 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val stack = inventory.getStackInSlot(selectedSlot)
if (stack != null && stack.stackSize > 0) {
val blockPos = position.offset(facing)
InventoryUtils.inventoryAt(blockPos) match {
case Some(inv) if inv.isUseableByPlayer(fakePlayer) && mayInteract(blockPos, facing.getOpposite) =>
if (!InventoryUtils.insertIntoInventory(stack, inv, Option(facing.getOpposite), count)) {
InventoryUtils.inventoryAt(blockPos, facing.getOpposite) match {
case Some(inv) if mayInteract(blockPos, facing.getOpposite, inv) =>
if (!InventoryUtils.insertIntoInventory(stack, inv, count)) {
// Cannot drop into that inventory.
return result(false, "inventory full")
}
@ -74,8 +74,8 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val count = args.optItemCount(1)
val blockPos = position.offset(facing)
if (InventoryUtils.inventoryAt(blockPos).exists(inventory => {
inventory.isUseableByPlayer(fakePlayer) && mayInteract(blockPos, facing.getOpposite) && InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, facing.getOpposite, count)
if (InventoryUtils.inventoryAt(blockPos, facing.getOpposite).exists(inventory => {
mayInteract(blockPos, facing.getOpposite) && InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, InventoryUtils.asItemHandler(this.inventory), slots = Option(insertionSlots)), inventory, count)
})) {
context.pause(Settings.get.suckDelay)
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.ExtendedArguments._
import li.cil.oc.util.InventoryUtils
import net.minecraft.inventory.IInventory
import net.minecraft.util.EnumFacing
import net.minecraftforge.items.IItemHandler
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.""")
@ -21,7 +21,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR
if (stack != null && stack.stackSize > 0) {
withInventory(position.offset(facing), fromSide, inventory => {
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.
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)
withInventory(position.offset(facing), fromSide, inventory => {
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)
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]) =
InventoryUtils.inventoryAt(blockPos) match {
case Some(inventory) if inventory.isUseableByPlayer(fakePlayer) && mayInteract(blockPos, fromSide) => f(inventory)
private def withInventory(blockPos: BlockPosition, fromSide: EnumFacing, f: IItemHandler => Array[AnyRef]) =
InventoryUtils.inventoryAt(blockPos, fromSide) match {
case Some(inventory) if mayInteract(blockPos, fromSide) => f(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.InventoryUtils
import li.cil.oc.util.ResultWrapper.result
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
import net.minecraftforge.items.IItemHandler
trait ItemInventoryControl extends InventoryAware {
@Callback(doc = "function(slot:number):number -- The size of an item inventory in the specified slot.")
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] = {
withItemInventory(args.checkSlot(inventory, 0), itemInventory => {
val slot = args.checkSlot(itemInventory, 1)
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 => {
val slot = args.checkSlot(itemInventory, 1)
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 {
case stack: ItemStack => api.Driver.inventoryFor(stack, fakePlayer) match {
case inventory: IInventory => f(inventory)
case stack: ItemStack => api.Driver.itemHandlerFor(stack, fakePlayer) match {
case inventory: IItemHandler => f(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 {
into.fill(contents, true)
inventory.decrStackSize(selectedSlot, 1)
InventoryUtils.insertIntoInventory(container, inventory, slots = Option(insertionSlots))
InventoryUtils.insertIntoInventory(container, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots))
if (container.stackSize > 0) {
InventoryUtils.spawnStackInWorld(position, container)
}
@ -91,7 +91,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware
val amount = FluidContainerRegistry.getFluidForFilledItem(filled).amount
from.drain(amount, true)
inventory.decrStackSize(selectedSlot, 1)
InventoryUtils.insertIntoInventory(filled, inventory, slots = Option(insertionSlots))
InventoryUtils.insertIntoInventory(filled, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots))
if (filled.stackSize > 0) {
InventoryUtils.spawnStackInWorld(position, filled)
}
@ -128,6 +128,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware
case _ => None
}
}
inventory.getStackInSlot(slot) match {
case stack: ItemStack => fluidInfo(stack) match {
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.fluids.FluidRegistry
import net.minecraftforge.fml.common.eventhandler.Event.Result
import net.minecraftforge.items.IItemHandler
trait WorldAware {
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) = {
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.ExtendedArguments._
import li.cil.oc.util.InventoryUtils
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
import net.minecraft.util.EnumFacing
import net.minecraftforge.items.IItemHandler
import net.minecraftforge.oredict.OreDictionary
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.""")
def getInventorySize(context: Context, args: Arguments): Array[AnyRef] = {
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.""")
@ -89,9 +89,9 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ
withInventory(facing, inventory => store(inventory.getStackInSlot(args.checkSlot(inventory, 1))))
}
private def withInventory(side: EnumFacing, f: IInventory => Array[AnyRef]) =
InventoryUtils.inventoryAt(position.offset(side)) match {
case Some(inventory) if inventory.isUseableByPlayer(fakePlayer) && mayInteract(position.offset(side), side.getOpposite) => f(inventory)
private def withInventory(side: EnumFacing, f: IItemHandler => Array[AnyRef]) =
InventoryUtils.inventoryAt(position.offset(side), side.getOpposite) match {
case Some(inventory) if mayInteract(position.offset(side), side.getOpposite, inventory) => f(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.network.EnvironmentHost
import li.cil.oc.api.network.ManagedEnvironment
import li.cil.oc.util.InventoryUtils
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
import net.minecraft.util.BlockPos
import net.minecraft.util.EnumFacing
import net.minecraft.world.World
import net.minecraftforge.items.CapabilityItemHandler
import net.minecraftforge.items.IItemHandler
import scala.collection.convert.WrapAsJava._
import scala.collection.convert.WrapAsScala._
@ -143,10 +146,20 @@ private[oc] object Registry extends api.detail.DriverAPI {
}.orNull
}
override def inventoryFor(stack: ItemStack, player: EntityPlayer): IInventory = {
@Deprecated
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)).
map(provider => provider.getInventory(stack, player)).
orNull
map(provider => InventoryUtils.asItemHandler(provider.getInventory(stack, player))).
getOrElse {
if(stack.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null))
stack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)
else null
}
}
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 net.minecraft.inventory.IInventory
import net.minecraft.util.EnumFacing
import net.minecraftforge.items.IItemHandler
import net.minecraftforge.fluids.FluidContainerRegistry
import scala.language.implicitConversions
@ -21,19 +22,23 @@ object ExtendedArguments {
if (!isDefined(index) || !hasValue(index)) default
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
if (slot < 0 || slot >= inventory.getSizeInventory) {
if (slot < 0 || slot >= inventory.getSlots) {
throw new IllegalArgumentException("invalid slot")
}
slot
}
def optSlot(inventory: IInventory, index: Int, default: Int) = {
def optSlot(inventory: IItemHandler, index: Int, default: Int): Int = {
if (!isDefined(index)) default
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) = {
val tank = args.checkInteger(n) - 1
if (tank < 0 || tank >= multi.tankCount) {

View File

@ -1,25 +1,38 @@
package li.cil.oc.util
import li.cil.oc.OpenComputers
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.EntityMinecartContainer
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory
import net.minecraft.inventory.ISidedInventory
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntityChest
import net.minecraft.tileentity.TileEntity
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._
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.
* <p/>
* 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.getItem == stackB.getItem &&
(!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage) &&
@ -31,15 +44,28 @@ object InventoryUtils {
* This performs special handling for (double-)chests and also checks for
* mine carts with chests.
*/
def inventoryAt(position: BlockPosition): Option[IInventory] = position.world match {
case Some(world) if world.blockExists(position) => (world.getBlock(position), world.getTileEntity(position)) match {
case (block: BlockChest, chest: TileEntityChest) => Option(block.getLockableContainer(world, chest.getPos))
case (_, inventory: IInventory) => Some(inventory)
case _ => world.getEntitiesWithinAABB(classOf[EntityMinecartContainer], position.bounds).find(!_.isDead)
def inventoryAt(position: BlockPosition, side: EnumFacing): Option[IItemHandler] = position.world match {
case Some(world) if world.blockExists(position) => world.getTileEntity(position) match {
case tile: TileEntity if tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side) => Option(tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side))
case tile: IInventory => Option(asItemHandler(tile))
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
}
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.
* <p/>
@ -61,40 +87,31 @@ object InventoryUtils {
* The number of items inserted can be limited, to avoid unnecessary
* 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) =
(stack != null && limit > 0) && {
val isSideValidForSlot = (inventory, side) match {
case (inventory: ISidedInventory, Some(s)) => inventory.canInsertItem(slot, stack, s)
case _ => true
}
(stack.stackSize > 0 && inventory.isItemValidForSlot(slot, stack) && isSideValidForSlot) && {
val maxStackSize = math.min(inventory.getInventoryStackLimit, stack.getMaxStackSize)
val existing = inventory.getStackInSlot(slot)
val shouldMerge = existing != null && existing.stackSize < maxStackSize &&
existing.isItemEqual(stack) && ItemStack.areItemStackTagsEqual(existing, stack)
if (shouldMerge) {
val space = maxStackSize - existing.stackSize
val amount = math.min(space, math.min(stack.stackSize, limit))
stack.stackSize -= amount
if (simulate) amount > 0
else {
existing.stackSize += amount
inventory.markDirty()
true
}
def insertIntoInventorySlot(stack: ItemStack, inventory: IItemHandler, slot: Int, limit: Int = 64, simulate: Boolean = false): Boolean =
(stack != null && limit > 0 && stack.stackSize > 0) && {
val amount = math.min(stack.stackSize, limit)
if (simulate) {
val toInsert = stack.copy()
toInsert.stackSize = amount
inventory.insertItem(slot, toInsert, simulate) match {
case remaining: ItemStack => remaining.stackSize < amount
case _ => 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
}
} else {
val toInsert = stack.splitStack(amount)
inventory.insertItem(slot, toInsert, simulate) match {
case remaining: ItemStack =>
val result = remaining.stackSize < amount
stack.stackSize = remaining.stackSize
result
case _ => 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.
* <p/>
@ -120,32 +137,31 @@ object InventoryUtils {
* also be achieved by a check in the consumer, but it saves some unnecessary
* 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)
(stack != null && limit > 0) && {
val isSideValidForSlot = inventory match {
case inventory: ISidedInventory => inventory.canExtractItem(slot, stack, side)
case _ => true
}
(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)
val success = extracted.stackSize < amount
stack.stackSize += extracted.stackSize
if (stack.stackSize == 0) {
inventory.setInventorySlotContents(slot, null)
}
else if (success) {
inventory.markDirty()
}
success
(stack != null && limit > 0 && stack.stackSize > 0) && {
var amount = math.min(stack.getMaxStackSize, math.min(stack.stackSize, limit))
inventory.extractItem(slot, amount, true) match {
case extracted: ItemStack =>
amount = extracted.stackSize
consumer(extracted)
if(extracted.stackSize >= amount) return false
inventory.extractItem(slot, amount - extracted.stackSize, false) match {
case realExtracted: ItemStack if realExtracted.stackSize == amount - extracted.stackSize => true
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
}
case _ => false
}
}
}
/**
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.
* <p/>
* This will try to fit the stack in any and as many as necessary slots in
@ -160,14 +176,11 @@ object InventoryUtils {
* item stack will be adjusted to reflect the number items inserted, by
* 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) =
(stack != null && limit > 0) && {
def insertIntoInventory(stack: ItemStack, inventory: IItemHandler, limit: Int = 64, simulate: Boolean = false, slots: Option[Iterable[Int]] = None): Boolean =
(stack != null && limit > 0 && stack.stackSize > 0) && {
var success = false
var remaining = limit
val range = slots.getOrElse(inventory match {
case sided: ISidedInventory => sided.getSlotsForFace(side.orNull).toIterable
case _ => 0 until inventory.getSizeInventory
})
val range = slots.getOrElse(0 until inventory.getSlots)
if (range.nonEmpty) {
// This is a special case for inserting with an explicit ordering,
@ -176,26 +189,15 @@ object InventoryUtils {
// slot, if at all possible, over merging.
if (slots.isDefined) {
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
success = true
}
}
val shouldTryMerge = !stack.isItemStackDamageable && stack.getMaxStackSize > 1 && inventory.getInventoryStackLimit > 1
if (shouldTryMerge) {
for (slot <- range) {
val stackSize = stack.stackSize
if ((inventory.getStackInSlot(slot) != null) && insertIntoInventorySlot(stack, inventory, side, 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)) {
if (insertIntoInventorySlot(stack, inventory, slot, remaining, simulate)) {
remaining -= stackSize - stack.stackSize
success = true
}
@ -205,6 +207,9 @@ object InventoryUtils {
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.
* <p/>
@ -216,13 +221,11 @@ object InventoryUtils {
* <p/>
* This returns <tt>true</tt> if at least one item was extracted.
*/
def extractAnyFromInventory(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, limit: Int = 64) = {
val range = inventory match {
case sided: ISidedInventory => sided.getSlotsForFace(side).toIterable
case _ => 0 until inventory.getSizeInventory
}
range.exists(slot => extractFromInventorySlot(consumer, inventory, side, slot, limit))
}
def extractAnyFromInventory(consumer: (ItemStack) => Unit, inventory: IItemHandler, limit: Int = 64): Boolean =
(0 until inventory.getSlots).exists(slot => extractFromInventorySlot(consumer, inventory, slot, limit))
def extractAnyFromInventory(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, limit: Int): Boolean =
extractAnyFromInventory(consumer, asItemHandler(inventory, side), limit)
/**
* Extracts an item stack from an inventory.
@ -233,13 +236,9 @@ object InventoryUtils {
* This uses the <tt>extractFromInventorySlot</tt> method, and therefore
* handles special cases such as sided inventories and stack size limits.
*/
def extractFromInventory(stack: ItemStack, inventory: IInventory, side: EnumFacing, simulate: Boolean = false) = {
val range = inventory match {
case sided: ISidedInventory => sided.getSlotsForFace(side).toIterable
case _ => 0 until inventory.getSizeInventory
}
def extractFromInventory(stack: ItemStack, inventory: IItemHandler, simulate: Boolean = false): ItemStack = {
val remaining = stack.copy()
for (slot <- range if remaining.stackSize > 0) {
for (slot <- 0 until inventory.getSlots if remaining.stackSize > 0) {
extractFromInventorySlot(stack => {
if (haveSameItemType(remaining, stack, checkNBT = true)) {
val transferred = stack.stackSize min remaining.stackSize
@ -248,24 +247,27 @@ object InventoryUtils {
stack.stackSize -= transferred
}
}
}, inventory, side, slot, remaining.stackSize)
}, inventory, slot, limit = remaining.stackSize)
}
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
* in the world.
*/
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
* in the world.
*/
def extractFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64) =
inventoryAt(position).exists(extractAnyFromInventory(consumer, _, side, limit))
def extractFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64): Boolean =
inventoryAt(position, side).exists(extractAnyFromInventory(consumer, _, limit))
/**
* Transfers some items between two inventories.
@ -280,46 +282,52 @@ object InventoryUtils {
* <p/>
* 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(
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.
*/
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 {
case Some(explicitSinkSlot) =>
extractFromInventorySlot(
insertIntoInventorySlot(_, sink, sinkSide, explicitSinkSlot, limit), source, sourceSide, sourceSlot, limit)
insertIntoInventorySlot(_, sink, explicitSinkSlot, limit), source, sourceSlot, limit = limit)
case _ =>
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
* in the world.
*/
def transferBetweenInventoriesAt(source: BlockPosition, sourceSide: EnumFacing, sink: BlockPosition, sinkSide: Option[EnumFacing], limit: Int = 64) =
inventoryAt(source).exists(sourceInventory =>
inventoryAt(sink).exists(sinkInventory =>
transferBetweenInventories(sourceInventory, sourceSide, sinkInventory, sinkSide, limit)))
def transferBetweenInventoriesAt(source: BlockPosition, sourceSide: EnumFacing, sink: BlockPosition, sinkSide: Option[EnumFacing], limit: Int = 64): Boolean =
inventoryAt(source, sourceSide).exists(sourceInventory =>
inventoryAt(sink, sinkSide.orNull).exists(sinkInventory =>
transferBetweenInventories(sourceInventory, sinkInventory, limit)))
/**
* Utility method for calling <tt>transferBetweenInventoriesSlots</tt> on inventories
* in the world.
*/
def transferBetweenInventoriesSlotsAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sourceSlot: Int, sinkPos: BlockPosition, sinkSide: Option[EnumFacing], sinkSlot: Option[Int], limit: Int = 64) =
inventoryAt(sourcePos).exists(sourceInventory =>
inventoryAt(sinkPos).exists(sinkInventory =>
transferBetweenInventoriesSlots(sourceInventory, sourceSide, sourceSlot, sinkInventory, sinkSide, sinkSlot, limit)))
def transferBetweenInventoriesSlotsAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sourceSlot: Int, sinkPos: BlockPosition, sinkSide: Option[EnumFacing], sinkSlot: Option[Int], limit: Int = 64): Boolean =
inventoryAt(sourcePos, sourceSide).exists(sourceInventory =>
inventoryAt(sinkPos, sinkSide.orNull).exists(sinkInventory =>
transferBetweenInventoriesSlots(sourceInventory, sourceSlot, sinkInventory, sinkSlot, limit)))
/**
* Utility method for dropping contents from a single inventory slot into
* 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 {
case Some(stack) if stack.stackSize > 0 => spawnStackInWorld(position, stack, direction); true
case _ => false