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 e82090706..94c67f978 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 @@ -6,12 +6,12 @@ import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.api.prefab.ItemStackArrayValue import li.cil.oc.server.component.result -import li.cil.oc.util.{BlockPosition, DatabaseAccess, InventoryUtils} +import li.cil.oc.util.{BlockInventorySource, BlockPosition, DatabaseAccess, EntityInventorySource, InventorySource, InventoryUtils} import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ExtendedArguments._ -import li.cil.oc.util.InventoryUtils import net.minecraft.inventory.IInventory import net.minecraft.block.Block +import net.minecraft.entity.EntityList import net.minecraft.item.ItemStack import net.minecraftforge.common.util.ForgeDirection import net.minecraftforge.oredict.OreDictionary @@ -102,8 +102,12 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ } case _ => None } - withInventory(facing, inventory => blockAt(position.offset(facing)) match { - case Some(block) => result(block.getUnlocalizedName) + withInventorySource(facing, { + case BlockInventorySource(position, _) => blockAt(position) match { + case Some(block) => result(block.getUnlocalizedName) + case _ => result(Unit, "Unknown") + } + case EntityInventorySource(entity, _) => result(EntityList.getEntityString(entity)) case _ => result(Unit, "Unknown") }) } @@ -122,9 +126,20 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ withInventory(facing, inventory => store(inventory.getStackInSlot(args.checkSlot(inventory, 1)))) } - private def withInventory(side: ForgeDirection, 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 mayInteract(side: ForgeDirection, f: InventorySource): Boolean = { + if (!f.inventory.isUseableByPlayer(fakePlayer)) false + else f match { + case BlockInventorySource(pos, _) => mayInteract(pos, side.getOpposite) + case _ => true + } + } + + private def withInventorySource(side: ForgeDirection, f: InventorySource => Array[AnyRef]) = + InventoryUtils.inventorySourceAt(position.offset(side)) match { + case Some(is) if mayInteract(side, is) => f(is) case _ => result(Unit, "no inventory") } + + private def withInventory(side: ForgeDirection, f: IInventory => Array[AnyRef]) = + withInventorySource(side, is => f(is.inventory)) } diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala index cc0a07a4a..e54030264 100644 --- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala +++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala @@ -2,6 +2,7 @@ package li.cil.oc.util 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 @@ -25,22 +26,34 @@ object InventoryUtils { (!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage) && (!checkNBT || ItemStack.areItemStackTagsEqual(stackA, stackB)) + /** + * Retrieves an actual inventory implementation for a specified world coordinate, + * complete with a reference to the source of said implementation. + *
+ * This performs special handling for (double-)chests and also checks for + * mine carts with chests. + */ + def inventorySourceAt(position: BlockPosition): Option[InventorySource] = position.world match { + case Some(world) if world.blockExists(position) => (world.getBlock(position), world.getTileEntity(position)) match { + case (block: BlockChest, chest: TileEntityChest) => Option(block.func_149951_m(world, chest.xCoord, chest.yCoord, chest.zCoord)). + map(a => BlockInventorySource(position, a)) + case (_, inventory: IInventory) => Some(BlockInventorySource(position, inventory)) + case _ => world.getEntitiesWithinAABB(classOf[EntityMinecartContainer], position.bounds). + map(_.asInstanceOf[EntityMinecartContainer]). + find(!_.isDead). + map(a => EntityInventorySource(a, a)) + } + case _ => None + } + /** * Retrieves an actual inventory implementation for a specified world coordinate. *
* 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.func_149951_m(world, chest.xCoord, chest.yCoord, chest.zCoord)) - case (_, inventory: IInventory) => Some(inventory) - case _ => world.getEntitiesWithinAABB(classOf[EntityMinecartContainer], position.bounds). - map(_.asInstanceOf[EntityMinecartContainer]). - find(!_.isDead) - } - case _ => None - } + def inventoryAt(position: BlockPosition): Option[IInventory] = inventorySourceAt(position). + map(a => a.inventory) /** * Inserts a stack into an inventory. @@ -408,3 +421,9 @@ object InventoryUtils { case _ => null } } + +sealed trait InventorySource { + def inventory: IInventory +} +final case class BlockInventorySource(position: BlockPosition, inventory: IInventory) extends InventorySource +final case class EntityInventorySource(entity: Entity, inventory: IInventory) extends InventorySource