From cbaa08ef138a50d219b71a9e3391f7109c9ea1da Mon Sep 17 00:00:00 2001 From: Vexatos Date: Sat, 10 Dec 2016 21:49:55 +0100 Subject: [PATCH 1/4] Rewrote InventoryUtils to use IItemHandlers. --- src/main/java/li/cil/oc/api/Driver.java | 12 +- .../java/li/cil/oc/api/detail/DriverAPI.java | 10 +- .../cil/oc/api/driver/InventoryProvider.java | 9 +- .../InventoryProviderDatabase.scala | 7 +- .../InventoryProviderServer.scala | 7 +- .../li/cil/oc/server/agent/Inventory.scala | 6 +- .../cil/oc/server/component/DebugCard.scala | 13 +- .../li/cil/oc/server/component/Drone.scala | 8 +- .../li/cil/oc/server/component/Trade.scala | 8 +- .../component/traits/InventoryTransfer.scala | 4 +- .../traits/InventoryWorldControl.scala | 10 +- .../traits/InventoryWorldControlMk2.scala | 12 +- .../traits/ItemInventoryControl.scala | 10 +- .../traits/TankInventoryControl.scala | 5 +- .../server/component/traits/WorldAware.scala | 3 + .../traits/WorldInventoryAnalytics.scala | 10 +- .../li/cil/oc/server/driver/Registry.scala | 13 +- .../li/cil/oc/util/ExtendedArguments.scala | 11 +- .../scala/li/cil/oc/util/InventoryUtils.scala | 222 +++++++++--------- 19 files changed, 204 insertions(+), 176 deletions(-) diff --git a/src/main/java/li/cil/oc/api/Driver.java b/src/main/java/li/cil/oc/api/Driver.java index 4d220b1c5..c24435a1a 100644 --- a/src/main/java/li/cil/oc/api/Driver.java +++ b/src/main/java/li/cil/oc/api/Driver.java @@ -8,11 +8,11 @@ import li.cil.oc.api.driver.Item; import li.cil.oc.api.driver.SidedBlock; import li.cil.oc.api.network.EnvironmentHost; 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.IItemHandler; import java.util.Collection; @@ -224,10 +224,10 @@ public final class Driver { } /** - * Get an inventory implementation providing access to an item inventory. + * Get an IItemHandler implementation providing access to an item inventory. *

* 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 null. *

* Note that the specified player may be null, but will usually @@ -235,9 +235,9 @@ public final class Driver { * * @param stack the item stack to get the inventory access for. * @param player the player holding the item. May be null. - * @return the inventory implementation interfacing the stack, or null. + * @return the IItemHandler implementation interfacing the stack, or null. */ - public static IInventory inventoryFor(ItemStack stack, EntityPlayer player) { + public static IItemHandler inventoryFor(ItemStack stack, EntityPlayer player) { if (API.driver != null) return API.driver.inventoryFor(stack, player); return null; @@ -279,4 +279,4 @@ public final class Driver { private Driver() { } -} \ No newline at end of file +} diff --git a/src/main/java/li/cil/oc/api/detail/DriverAPI.java b/src/main/java/li/cil/oc/api/detail/DriverAPI.java index 5bd02475a..97d218d9a 100644 --- a/src/main/java/li/cil/oc/api/detail/DriverAPI.java +++ b/src/main/java/li/cil/oc/api/detail/DriverAPI.java @@ -8,11 +8,11 @@ import li.cil.oc.api.driver.Item; import li.cil.oc.api.driver.SidedBlock; import li.cil.oc.api.network.EnvironmentHost; 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.IItemHandler; import java.util.Collection; @@ -170,10 +170,10 @@ public interface DriverAPI { Class environmentFor(ItemStack stack); /** - * Get an inventory implementation providing access to an item inventory. + * Get an IItemHandler implementation providing access to an item inventory. *

* 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 null. *

* Note that the specified player may be null, but will usually @@ -181,9 +181,9 @@ public interface DriverAPI { * * @param stack the item stack to get the inventory access for. * @param player the player holding the item. May be null. - * @return the inventory implementation interfacing the stack, or null. + * @return the IItemHandler implementation interfacing the stack, or null. */ - IInventory inventoryFor(ItemStack stack, EntityPlayer player); + IItemHandler inventoryFor(ItemStack stack, EntityPlayer player); /** * Get a list of all registered block drivers. diff --git a/src/main/java/li/cil/oc/api/driver/InventoryProvider.java b/src/main/java/li/cil/oc/api/driver/InventoryProvider.java index 4308b454a..4c77d093b 100644 --- a/src/main/java/li/cil/oc/api/driver/InventoryProvider.java +++ b/src/main/java/li/cil/oc/api/driver/InventoryProvider.java @@ -3,6 +3,7 @@ package li.cil.oc.api.driver; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; +import net.minecraftforge.items.IItemHandler; /** * Inventory providers are used to access contents of item inventories. @@ -12,7 +13,7 @@ import net.minecraft.item.ItemStack; * allow agents (robots, drones) to interact with such inventories using * the inventory controller upgrade, for example. *

- * Implementations returned by {@link #getInventory} should save changes + * Implementations returned by {@link #getItemHandler} should save changes * back to the item stack when {@link IInventory#markDirty()} is called. * Return null if the specified stack is not supported. */ @@ -27,7 +28,7 @@ public interface InventoryProvider { boolean worksWith(ItemStack stack, EntityPlayer player); /** - * Get an inventory implementation that allows interfacing with the + * Get an IItemHandler implementation that allows interfacing with the * item inventory represented by the specified item stack. *

* Note that the specified player may be null, but will @@ -36,7 +37,7 @@ public interface InventoryProvider { * * @param stack the item stack to get the inventory for. * @param player the player holding the item, may be null. - * @return the inventory representing the contents, or null. + * @return the IItemHandler representing the contents, or null. */ - IInventory getInventory(ItemStack stack, EntityPlayer player); + IItemHandler getItemHandler(ItemStack stack, EntityPlayer player); } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala index 54ec36265..197e60170 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala @@ -2,16 +2,17 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api.driver.InventoryProvider import li.cil.oc.common.inventory.DatabaseInventory +import li.cil.oc.util.InventoryUtils import net.minecraft.entity.player.EntityPlayer -import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack +import net.minecraftforge.items.IItemHandler object InventoryProviderDatabase extends InventoryProvider { override def worksWith(stack: ItemStack, player: EntityPlayer): Boolean = DriverUpgradeDatabase.worksWith(stack) - override def getInventory(stack: ItemStack, player: EntityPlayer): IInventory = new DatabaseInventory { + override def getItemHandler(stack: ItemStack, player: EntityPlayer): IItemHandler = InventoryUtils.asItemHandler(new DatabaseInventory { override def container: ItemStack = stack override def isUseableByPlayer(player: EntityPlayer): Boolean = player == player - } + }) } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala index d7e30f1cd..ef19b5c2f 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala @@ -2,14 +2,15 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api.driver.InventoryProvider import li.cil.oc.common.inventory.ServerInventory +import li.cil.oc.util.InventoryUtils import net.minecraft.entity.player.EntityPlayer -import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack +import net.minecraftforge.items.IItemHandler object InventoryProviderServer extends InventoryProvider { override def worksWith(stack: ItemStack, player: EntityPlayer): Boolean = DriverServer.worksWith(stack) - override def getInventory(stack: ItemStack, player: EntityPlayer): IInventory = new ServerInventory { + override def getItemHandler(stack: ItemStack, player: EntityPlayer): IItemHandler = InventoryUtils.asItemHandler(new ServerInventory { override def container: ItemStack = stack - } + }) } diff --git a/src/main/scala/li/cil/oc/server/agent/Inventory.scala b/src/main/scala/li/cil/oc/server/agent/Inventory.scala index ab7965aca..543c850ca 100644 --- a/src/main/scala/li/cil/oc/server/agent/Inventory.scala +++ b/src/main/scala/li/cil/oc/server/agent/Inventory.scala @@ -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,12 @@ 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._ +import net.minecraftforge.items.IItemHandlerModifiable +import net.minecraftforge.items.wrapper.PlayerInvWrapper class Inventory(val agent: internal.Agent) extends InventoryPlayer(null) { + protected implicit def toItemHandler(inv: Inventory):IItemHandlerModifiable = new PlayerInvWrapper(inv) + def selectedItemStack = agent.mainInventory.getStackInSlot(agent.selectedSlot) def inventorySlots = (agent.selectedSlot until getSizeInventory) ++ (0 until agent.selectedSlot) diff --git a/src/main/scala/li/cil/oc/server/component/DebugCard.scala b/src/main/scala/li/cil/oc/server/component/DebugCard.scala index f79e3ba6e..4e2013985 100644 --- a/src/main/scala/li/cil/oc/server/component/DebugCard.scala +++ b/src/main/scala/li/cil/oc/server/component/DebugCard.scala @@ -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") diff --git a/src/main/scala/li/cil/oc/server/component/Drone.scala b/src/main/scala/li/cil/oc/server/component/Drone.scala index 48a5994d1..70c7844d8 100644 --- a/src/main/scala/li/cil/oc/server/component/Drone.scala +++ b/src/main/scala/li/cil/oc/server/component/Drone.scala @@ -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) } } diff --git a/src/main/scala/li/cil/oc/server/component/Trade.scala b/src/main/scala/li/cil/oc/server/component/Trade.scala index 1b2d5904d..3efd8d0f8 100644 --- a/src/main/scala/li/cil/oc/server/component/Trade.scala +++ b/src/main/scala/li/cil/oc/server/component/Trade.scala @@ -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) diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryTransfer.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryTransfer.scala index 704ddcdbc..6150b80ec 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryTransfer.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryTransfer.scala @@ -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)) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala index 4e6aadd96..2892899fa 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala @@ -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) diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala index 5c799a7c4..5f14788c4 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala @@ -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") } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala index 6443eb6f5..4b3253e04 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala @@ -7,13 +7,13 @@ 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 -- The size of an item inventory in the specified slot.") @@ -28,14 +28,14 @@ trait ItemInventoryControl extends InventoryAware { def suckFromItemInventory(context: Context, args: Arguments): Array[AnyRef] = { withItemInventory(args.checkSlot(inventory, 0), itemInventory => { val count = args.optItemCount(1) - result(InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, inventory, slots = Option(insertionSlots)), itemInventory, null, count)) + result(InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots)), itemInventory, 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 inventory: IItemHandler => f(inventory) case _ => result(0, "no item inventory") } case _ => result(0, "no item inventory") diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala index b54ca7e91..5e31644ce 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala @@ -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) diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala index 5bb80df03..308a93b45 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala @@ -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) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala index 04a37ca7c..66c78847d 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala @@ -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") } } diff --git a/src/main/scala/li/cil/oc/server/driver/Registry.scala b/src/main/scala/li/cil/oc/server/driver/Registry.scala index 0a30f2383..76f7bba99 100644 --- a/src/main/scala/li/cil/oc/server/driver/Registry.scala +++ b/src/main/scala/li/cil/oc/server/driver/Registry.scala @@ -12,11 +12,12 @@ import li.cil.oc.api.machine.Value import li.cil.oc.api.network.EnvironmentHost import li.cil.oc.api.network.ManagedEnvironment 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 +144,14 @@ private[oc] object Registry extends api.detail.DriverAPI { }.orNull } - override def inventoryFor(stack: ItemStack, player: EntityPlayer): IInventory = { + override def inventoryFor(stack: ItemStack, player: EntityPlayer): IItemHandler = { inventoryProviders.find(provider => provider.worksWith(stack, player)). - map(provider => provider.getInventory(stack, player)). - orNull + map(provider => provider.getItemHandler(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 diff --git a/src/main/scala/li/cil/oc/util/ExtendedArguments.scala b/src/main/scala/li/cil/oc/util/ExtendedArguments.scala index 8ae09768a..d33725164 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedArguments.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedArguments.scala @@ -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) { diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala index 56b6fba79..2f39822fd 100644 --- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala +++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala @@ -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. *

* 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. *

@@ -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) = + def insertIntoInventorySlot(stack: ItemStack, inventory: IItemHandler, slot: Int, limit: Int = 64, simulate: Boolean = false): Boolean = (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 - } + 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 < stack.stackSize + 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 < stack.stackSize + 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. *

@@ -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 + 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. *

* 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) = + def insertIntoInventory(stack: ItemStack, inventory: IItemHandler, limit: Int = 64, simulate: Boolean = false, slots: Option[Iterable[Int]] = None): Boolean = (stack != null && limit > 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. *

@@ -216,13 +221,11 @@ object InventoryUtils { *

* This returns true 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 extractFromInventorySlot 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 insertIntoInventory 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 extractFromInventory 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 { *

* This returns true 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 transferBetweenInventories 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 transferBetweenInventories 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 transferBetweenInventoriesSlots 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 From f237ceb7a7afe8490c396228294c8bdf6107d7e3 Mon Sep 17 00:00:00 2001 From: Vexatos Date: Sun, 11 Dec 2016 15:51:47 +0100 Subject: [PATCH 2/4] Added some stack size checks and fixed agent.Inventory. --- src/main/scala/li/cil/oc/server/agent/Inventory.scala | 5 +---- src/main/scala/li/cil/oc/util/InventoryUtils.scala | 10 +++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main/scala/li/cil/oc/server/agent/Inventory.scala b/src/main/scala/li/cil/oc/server/agent/Inventory.scala index 543c850ca..e5c676b47 100644 --- a/src/main/scala/li/cil/oc/server/agent/Inventory.scala +++ b/src/main/scala/li/cil/oc/server/agent/Inventory.scala @@ -10,11 +10,8 @@ import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagList -import net.minecraftforge.items.IItemHandlerModifiable -import net.minecraftforge.items.wrapper.PlayerInvWrapper class Inventory(val agent: internal.Agent) extends InventoryPlayer(null) { - protected implicit def toItemHandler(inv: Inventory):IItemHandlerModifiable = new PlayerInvWrapper(inv) def selectedItemStack = agent.mainInventory.getStackInSlot(agent.selectedSlot) @@ -57,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)) diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala index 2f39822fd..e8c191bed 100644 --- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala +++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala @@ -88,20 +88,20 @@ object InventoryUtils { * changes to the inventory the stack may come from, for example. */ 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 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 < stack.stackSize + case remaining: ItemStack => remaining.stackSize < amount case _ => true } } else { val toInsert = stack.splitStack(amount) inventory.insertItem(slot, toInsert, simulate) match { case remaining: ItemStack => - val result = remaining.stackSize < stack.stackSize + val result = remaining.stackSize < amount stack.stackSize = remaining.stackSize result case _ => true @@ -139,7 +139,7 @@ object InventoryUtils { */ def extractFromInventorySlot(consumer: (ItemStack) => Unit, inventory: IItemHandler, slot: Int, limit: Int = 64): Boolean = { val stack = inventory.getStackInSlot(slot) - (stack != null && limit > 0) && { + (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 => @@ -177,7 +177,7 @@ object InventoryUtils { * having its size decremented accordingly. */ 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 remaining = limit val range = slots.getOrElse(0 until inventory.getSlots) From 5db3b72cc93a0403551be7b7da212bb3da773cbc Mon Sep 17 00:00:00 2001 From: Vexatos Date: Sun, 11 Dec 2016 16:35:52 +0100 Subject: [PATCH 3/4] Reverted changes to InventoryProvider and renamed inventoryFor to itemHandlerFor. --- src/main/java/li/cil/oc/api/Driver.java | 15 +++++++++++++-- src/main/java/li/cil/oc/api/detail/DriverAPI.java | 9 ++++++++- .../li/cil/oc/api/driver/InventoryProvider.java | 9 ++++----- .../opencomputers/InventoryProviderDatabase.scala | 7 +++---- .../opencomputers/InventoryProviderServer.scala | 7 +++---- .../component/traits/ItemInventoryControl.scala | 2 +- .../scala/li/cil/oc/server/driver/Registry.scala | 14 +++++++++++--- 7 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/main/java/li/cil/oc/api/Driver.java b/src/main/java/li/cil/oc/api/Driver.java index c24435a1a..6051afd4f 100644 --- a/src/main/java/li/cil/oc/api/Driver.java +++ b/src/main/java/li/cil/oc/api/Driver.java @@ -8,6 +8,7 @@ import li.cil.oc.api.driver.Item; import li.cil.oc.api.driver.SidedBlock; import li.cil.oc.api.network.EnvironmentHost; 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; @@ -223,6 +224,16 @@ public final class Driver { return null; } + /** + * @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. *

@@ -237,9 +248,9 @@ public final class Driver { * @param player the player holding the item. May be null. * @return the IItemHandler implementation interfacing the stack, or null. */ - public static IItemHandler 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; } diff --git a/src/main/java/li/cil/oc/api/detail/DriverAPI.java b/src/main/java/li/cil/oc/api/detail/DriverAPI.java index 97d218d9a..13103d648 100644 --- a/src/main/java/li/cil/oc/api/detail/DriverAPI.java +++ b/src/main/java/li/cil/oc/api/detail/DriverAPI.java @@ -8,6 +8,7 @@ import li.cil.oc.api.driver.Item; import li.cil.oc.api.driver.SidedBlock; import li.cil.oc.api.network.EnvironmentHost; 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; @@ -169,6 +170,12 @@ public interface DriverAPI { */ Class environmentFor(ItemStack stack); + /** + * @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. *

@@ -183,7 +190,7 @@ public interface DriverAPI { * @param player the player holding the item. May be null. * @return the IItemHandler implementation interfacing the stack, or null. */ - IItemHandler inventoryFor(ItemStack stack, EntityPlayer player); + IItemHandler itemHandlerFor(ItemStack stack, EntityPlayer player); /** * Get a list of all registered block drivers. diff --git a/src/main/java/li/cil/oc/api/driver/InventoryProvider.java b/src/main/java/li/cil/oc/api/driver/InventoryProvider.java index 4c77d093b..4308b454a 100644 --- a/src/main/java/li/cil/oc/api/driver/InventoryProvider.java +++ b/src/main/java/li/cil/oc/api/driver/InventoryProvider.java @@ -3,7 +3,6 @@ package li.cil.oc.api.driver; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; -import net.minecraftforge.items.IItemHandler; /** * Inventory providers are used to access contents of item inventories. @@ -13,7 +12,7 @@ import net.minecraftforge.items.IItemHandler; * allow agents (robots, drones) to interact with such inventories using * the inventory controller upgrade, for example. *

- * Implementations returned by {@link #getItemHandler} should save changes + * Implementations returned by {@link #getInventory} should save changes * back to the item stack when {@link IInventory#markDirty()} is called. * Return null if the specified stack is not supported. */ @@ -28,7 +27,7 @@ public interface InventoryProvider { boolean worksWith(ItemStack stack, EntityPlayer player); /** - * Get an IItemHandler implementation that allows interfacing with the + * Get an inventory implementation that allows interfacing with the * item inventory represented by the specified item stack. *

* Note that the specified player may be null, but will @@ -37,7 +36,7 @@ public interface InventoryProvider { * * @param stack the item stack to get the inventory for. * @param player the player holding the item, may be null. - * @return the IItemHandler representing the contents, or null. + * @return the inventory representing the contents, or null. */ - IItemHandler getItemHandler(ItemStack stack, EntityPlayer player); + IInventory getInventory(ItemStack stack, EntityPlayer player); } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala index 197e60170..54ec36265 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderDatabase.scala @@ -2,17 +2,16 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api.driver.InventoryProvider import li.cil.oc.common.inventory.DatabaseInventory -import li.cil.oc.util.InventoryUtils import net.minecraft.entity.player.EntityPlayer +import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import net.minecraftforge.items.IItemHandler object InventoryProviderDatabase extends InventoryProvider { override def worksWith(stack: ItemStack, player: EntityPlayer): Boolean = DriverUpgradeDatabase.worksWith(stack) - override def getItemHandler(stack: ItemStack, player: EntityPlayer): IItemHandler = InventoryUtils.asItemHandler(new DatabaseInventory { + override def getInventory(stack: ItemStack, player: EntityPlayer): IInventory = new DatabaseInventory { override def container: ItemStack = stack override def isUseableByPlayer(player: EntityPlayer): Boolean = player == player - }) + } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala index ef19b5c2f..d7e30f1cd 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/InventoryProviderServer.scala @@ -2,15 +2,14 @@ package li.cil.oc.integration.opencomputers import li.cil.oc.api.driver.InventoryProvider import li.cil.oc.common.inventory.ServerInventory -import li.cil.oc.util.InventoryUtils import net.minecraft.entity.player.EntityPlayer +import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack -import net.minecraftforge.items.IItemHandler object InventoryProviderServer extends InventoryProvider { override def worksWith(stack: ItemStack, player: EntityPlayer): Boolean = DriverServer.worksWith(stack) - override def getItemHandler(stack: ItemStack, player: EntityPlayer): IItemHandler = InventoryUtils.asItemHandler(new ServerInventory { + override def getInventory(stack: ItemStack, player: EntityPlayer): IInventory = new ServerInventory { override def container: ItemStack = stack - }) + } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala index 4b3253e04..527cf02a2 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala @@ -34,7 +34,7 @@ trait ItemInventoryControl extends InventoryAware { 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 stack: ItemStack => api.Driver.itemHandlerFor(stack, fakePlayer) match { case inventory: IItemHandler => f(inventory) case _ => result(0, "no item inventory") } diff --git a/src/main/scala/li/cil/oc/server/driver/Registry.scala b/src/main/scala/li/cil/oc/server/driver/Registry.scala index 76f7bba99..db847adda 100644 --- a/src/main/scala/li/cil/oc/server/driver/Registry.scala +++ b/src/main/scala/li/cil/oc/server/driver/Registry.scala @@ -11,7 +11,9 @@ 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 @@ -144,13 +146,19 @@ private[oc] object Registry extends api.detail.DriverAPI { }.orNull } - override def inventoryFor(stack: ItemStack, player: EntityPlayer): IItemHandler = { + @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.getItemHandler(stack, player)). + 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 + else null } } From 761875d9ce7770ffa3846297da1e4235ca4799a5 Mon Sep 17 00:00:00 2001 From: Vexatos Date: Sat, 17 Dec 2016 14:30:50 +0100 Subject: [PATCH 4/4] Made dropIntoItemInventory more specific. --- .../cil/oc/server/component/traits/ItemInventoryControl.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala index d7c47aaea..42026cd36 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala @@ -16,12 +16,12 @@ trait ItemInventoryControl extends InventoryAware { 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, slot), inventory, null, count)) + result(InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventorySlot(_, itemInventory, slot), inventory, null, selectedSlot, count)) }) }