I don't trust ItemStack Options. Let's get rid of them.

This commit is contained in:
Vexatos 2017-04-22 20:16:34 +02:00
parent 5c3b5634c4
commit 30e3e129c8
26 changed files with 234 additions and 105 deletions

View File

@ -9,13 +9,14 @@ import li.cil.oc.integration.Mods
import li.cil.oc.integration.jei.ModJEI
import li.cil.oc.integration.util.ItemSearch
import li.cil.oc.util.RenderState
import li.cil.oc.util.StackOption
import li.cil.oc.util.StackOption._
import net.minecraft.client.gui.Gui
import net.minecraft.client.renderer.GlStateManager
import net.minecraft.client.renderer.Tessellator
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
import net.minecraft.inventory.Container
import net.minecraft.inventory.Slot
import net.minecraft.item.ItemStack
import net.minecraftforge.fml.common.Optional
import org.lwjgl.opengl.GL11
@ -25,7 +26,7 @@ import scala.collection.convert.WrapAsScala._
abstract class DynamicGuiContainer[C <: Container](container: C) extends CustomGuiContainer(container) {
protected var hoveredSlot: Option[Slot] = None
protected var hoveredStackNEI: Option[ItemStack] = None
protected var hoveredStackNEI: StackOption = EmptyStack
protected def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) {
fontRenderer.drawString(
@ -127,7 +128,7 @@ abstract class DynamicGuiContainer[C <: Container](container: C) extends CustomG
((currentIsInPlayerInventory && slot.getHasStack && isSelectiveSlot(hovered) && hovered.isItemValid(slot.getStack)) ||
(hoveredIsInPlayerInventory && hovered.getHasStack && isSelectiveSlot(slot) && slot.isItemValid(hovered.getStack)))
case _ => hoveredStackNEI match {
case Some(stack) => !currentIsInPlayerInventory && isSelectiveSlot(slot) && slot.isItemValid(stack)
case SomeStack(stack) => !currentIsInPlayerInventory && isSelectiveSlot(slot) && slot.isItemValid(stack)
case _ => false
}
}

View File

@ -4,6 +4,7 @@ import li.cil.oc.Constants
import li.cil.oc.OpenComputers
import li.cil.oc.api.detail.ItemInfo
import li.cil.oc.common.init.Items
import li.cil.oc.util.StackOption
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.stats.StatBase
@ -241,7 +242,7 @@ object Achievement {
private class AchievementBuilder(val name: String) {
var x = 0
var y = 0
var stack: Option[ItemStack] = stackFromName(name)
var stack: StackOption = stackFromName(name)
var parent: Option[MCAchievement] = None
var crafting = mutable.Set.empty[String]
var customCrafting = mutable.Set.empty[ItemStack]
@ -254,7 +255,7 @@ object Achievement {
}
def withIconOf(stack: ItemStack): AchievementBuilder = {
this.stack = Option(stack)
this.stack = StackOption(stack)
this
}
@ -271,7 +272,7 @@ object Achievement {
def whenCrafting(stack: ItemStack): AchievementBuilder = {
customCrafting += stack
if (this.stack.isEmpty) this.stack = Option(stack)
if (this.stack.isEmpty) this.stack = StackOption(stack)
this
}
@ -282,7 +283,7 @@ object Achievement {
}
def add(): MCAchievement = {
val achievement = new MCAchievement("oc." + name, "oc." + name, x, y, stack.orNull, parent.orNull)
val achievement = new MCAchievement("oc." + name, "oc." + name, x, y, stack.orEmpty, parent.orNull)
if (parent.isEmpty) {
achievement.asInstanceOf[StatBase].initIndependentStat()
@ -312,7 +313,7 @@ object Achievement {
achievement
}
private def stackFromName(name: String) = Option(Items.get(name)).map(_.createItemStack(1))
private def stackFromName(name: String): StackOption = StackOption(Option(Items.get(name)).map(_.createItemStack(1)))
}
}
}

View File

@ -33,6 +33,7 @@ import li.cil.oc.server.machine.Machine
import li.cil.oc.server.machine.luac.LuaStateFactory
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.StackOption._
import li.cil.oc.util._
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
@ -293,29 +294,29 @@ object EventHandler {
didRecraft = recraft(e, navigationUpgrade, stack => {
// Restore the map currently used in the upgrade.
Option(api.Driver.driverFor(e.crafting)) match {
case Some(driver) => Option(new ItemStack(driver.dataTag(stack).getCompoundTag(Settings.namespace + "map")))
case _ => None
case Some(driver) => StackOption(new ItemStack(driver.dataTag(stack).getCompoundTag(Settings.namespace + "map")))
case _ => EmptyStack
}
}) || didRecraft
didRecraft = recraft(e, mcu, stack => {
// Restore EEPROM currently used in microcontroller.
new MicrocontrollerData(stack).components.find(api.Items.get(_) == eeprom)
new MicrocontrollerData(stack).components.find(api.Items.get(_) == eeprom).asStackOption
}) || didRecraft
didRecraft = recraft(e, drone, stack => {
// Restore EEPROM currently used in drone.
new MicrocontrollerData(stack).components.find(api.Items.get(_) == eeprom)
new MicrocontrollerData(stack).components.find(api.Items.get(_) == eeprom).asStackOption
}) || didRecraft
didRecraft = recraft(e, robot, stack => {
// Restore EEPROM currently used in robot.
new RobotData(stack).components.find(api.Items.get(_) == eeprom)
new RobotData(stack).components.find(api.Items.get(_) == eeprom).asStackOption
}) || didRecraft
didRecraft = recraft(e, tablet, stack => {
// Restore EEPROM currently used in tablet.
new TabletData(stack).items.collect { case item if !item.isEmpty => item }.find(api.Items.get(_) == eeprom)
new TabletData(stack).items.collect { case item if !item.isEmpty => item }.find(api.Items.get(_) == eeprom).asStackOption
}) || didRecraft
// Presents?
@ -367,7 +368,7 @@ object EventHandler {
month == Calendar.APRIL && dayOfMonth == 1
}
private def recraft(e: ItemCraftedEvent, item: ItemInfo, callback: ItemStack => Option[ItemStack]): Boolean = {
private def recraft(e: ItemCraftedEvent, item: ItemInfo, callback: ItemStack => StackOption): Boolean = {
if (api.Items.get(e.crafting) == item) {
for (slot <- 0 until e.craftMatrix.getSizeInventory) {
val stack = e.craftMatrix.getStackInSlot(slot)

View File

@ -14,6 +14,7 @@ import li.cil.oc.integration.util.ItemBlacklist
import li.cil.oc.integration.util.Wrench
import li.cil.oc.util.InventoryUtils
import li.cil.oc.util.Rarity
import li.cil.oc.util.StackOption._
import net.minecraft.block.Block
import net.minecraft.block.state.BlockStateContainer
import net.minecraft.block.state.IBlockState
@ -91,7 +92,7 @@ class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microc
case mcu: tileentity.Microcontroller =>
val newEeprom = player.inventory.decrStackSize(player.inventory.currentItem, 1)
mcu.changeEEPROM(newEeprom) match {
case Some(oldEeprom) => InventoryUtils.addToPlayerInventory(oldEeprom, player)
case SomeStack(oldEeprom) => InventoryUtils.addToPlayerInventory(oldEeprom, player)
case _ =>
}
}

View File

@ -7,6 +7,7 @@ import li.cil.oc.util.InventoryUtils
import li.cil.oc.util.SideTracker
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
import net.minecraft.util.ResourceLocation
class DynamicComponentSlot(val container: Player, inventory: IInventory, index: Int, x: Int, y: Int, val info: DynamicComponentSlot => InventorySlot, val containerTierGetter: () => Int) extends ComponentSlot(inventory, index, x, y) {
@ -38,7 +39,7 @@ class DynamicComponentSlot(val container: Player, inventory: IInventory, index:
override protected def clearIfInvalid(player: EntityPlayer) {
if (SideTracker.isServer && getHasStack && !isItemValid(getStack)) {
val stack = getStack
putStack(null)
putStack(ItemStack.EMPTY)
InventoryUtils.addToPlayerInventory(stack, player)
}
}

View File

@ -2,6 +2,7 @@ package li.cil.oc.common.inventory
import li.cil.oc.Settings
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.StackOption
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraftforge.common.util.Constants.NBT
@ -9,7 +10,7 @@ import net.minecraftforge.common.util.Constants.NBT
trait Inventory extends SimpleInventory {
def items: Array[ItemStack]
def updateItems(slot: Int, stack: ItemStack): Unit = items(slot) = stack
def updateItems(slot: Int, stack: ItemStack): Unit = items(slot) = StackOption(stack).orEmpty
// ----------------------------------------------------------------------- //

View File

@ -6,6 +6,8 @@ import li.cil.oc.api.nanomachines.DisableReason
import li.cil.oc.api.prefab.AbstractBehavior
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.StackOption
import li.cil.oc.util.StackOption._
import net.minecraft.block.Block
import net.minecraft.block.state.IBlockState
import net.minecraft.entity.player.EntityPlayer
@ -66,7 +68,7 @@ object DisintegrationProvider extends ScalaProvider("c4e7e3c2-8069-4fbb-b08e-74b
if (hardness > 0) {
val timeToBreak = (1 / hardness).toInt
if (timeToBreak < 20 * 30) {
val info = new SlowBreakInfo(now, now + timeToBreak, pos, Option(player.getHeldItemMainhand).map(_.copy()), blockState)
val info = new SlowBreakInfo(now, now + timeToBreak, pos, StackOption(player.getHeldItemMainhand).map(_.copy()), blockState)
world.destroyBlockInWorldPartially(pos.hashCode(), pos, 0)
breakingMapNew += pos -> info
}
@ -98,14 +100,14 @@ object DisintegrationProvider extends ScalaProvider("c4e7e3c2-8069-4fbb-b08e-74b
}
}
class SlowBreakInfo(val timeStarted: Long, val timeBroken: Long, val pos: BlockPosition, val originalTool: Option[ItemStack], val blockState: IBlockState) {
class SlowBreakInfo(val timeStarted: Long, val timeBroken: Long, val pos: BlockPosition, val originalTool: StackOption, val blockState: IBlockState) {
var lastDamageSent = 0
def checkTool(player: EntityPlayer): Boolean = {
val currentTool = Option(player.getHeldItemMainhand).map(_.copy())
val currentTool = StackOption(player.getHeldItemMainhand).map(_.copy())
(currentTool, originalTool) match {
case (Some(stackA), Some(stackB)) => stackA.getItem == stackB.getItem && (stackA.isItemStackDamageable || stackA.getItemDamage == stackB.getItemDamage)
case (None, None) => true
case (SomeStack(stackA), SomeStack(stackB)) => stackA.getItem == stackB.getItem && (stackA.isItemStackDamageable || stackA.getItemDamage == stackB.getItemDamage)
case (EmptyStack, EmptyStack) => true
case _ => false
}
}

View File

@ -2,6 +2,7 @@ package li.cil.oc.common.recipe
import li.cil.oc.util.Color
import li.cil.oc.util.ItemColorizer
import li.cil.oc.util.StackOption
import net.minecraft.block.Block
import net.minecraft.entity.passive.EntitySheep
import net.minecraft.inventory.InventoryCrafting
@ -20,9 +21,9 @@ class ColorizeRecipe(target: Item, source: Array[Item] = null) extends Container
val sourceItems: Array[Item] = if (source != null) source else Array(targetItem)
override def matches(crafting: InventoryCrafting, world: World): Boolean = {
val stacks = (0 until crafting.getSizeInventory).flatMap(i => Option(crafting.getStackInSlot(i)))
val stacks = (0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i)))
val targets = stacks.filter(stack => sourceItems.contains(stack.getItem) || stack.getItem == targetItem)
val other = stacks.filterNot(stack => stack.isEmpty || targets.contains(stack))
val other = stacks.filterNot(targets.contains(_))
targets.size == 1 && other.nonEmpty && other.forall(Color.isDye)
}
@ -32,12 +33,12 @@ class ColorizeRecipe(target: Item, source: Array[Item] = null) extends Container
var colorCount = 0
var maximum = 0
(0 until crafting.getSizeInventory).flatMap(i => Option(crafting.getStackInSlot(i))).foreach { stack =>
(0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i))).foreach { stack =>
if (sourceItems.contains(stack.getItem)
|| stack.getItem == targetItem) {
targetStack = stack.copy()
targetStack.setCount(1)
} else if(!stack.isEmpty) {
} else {
val dye = Color.findDye(stack)
if (dye.isEmpty)
return ItemStack.EMPTY

View File

@ -1,6 +1,7 @@
package li.cil.oc.common.recipe
import li.cil.oc.util.ItemColorizer
import li.cil.oc.util.StackOption
import net.minecraft.block.Block
import net.minecraft.init.Items
import net.minecraft.inventory.InventoryCrafting
@ -17,20 +18,20 @@ class DecolorizeRecipe(target: Item) extends ContainerItemAwareRecipe {
val targetItem: Item = target
override def matches(crafting: InventoryCrafting, world: World): Boolean = {
val stacks = (0 until crafting.getSizeInventory).flatMap(i => Option(crafting.getStackInSlot(i)))
val stacks = (0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i)))
val targets = stacks.filter(stack => stack.getItem == targetItem)
val other = stacks.filterNot(stack => stack.isEmpty || targets.contains(stack))
val other = stacks.filterNot(targets.contains)
targets.size == 1 && other.size == 1 && other.forall(_.getItem == Items.WATER_BUCKET)
}
override def getCraftingResult(crafting: InventoryCrafting): ItemStack = {
var targetStack: ItemStack = ItemStack.EMPTY
(0 until crafting.getSizeInventory).flatMap(i => Option(crafting.getStackInSlot(i))).foreach { stack =>
(0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i))).foreach { stack =>
if (stack.getItem == targetItem) {
targetStack = stack.copy()
targetStack.setCount(1)
} else if (!stack.isEmpty && stack.getItem != Items.WATER_BUCKET) {
} else if (stack.getItem != Items.WATER_BUCKET) {
return ItemStack.EMPTY
}
}

View File

@ -3,6 +3,7 @@ package li.cil.oc.common.recipe
import li.cil.oc.Settings
import li.cil.oc.common.Loot
import li.cil.oc.integration.util.Wrench
import li.cil.oc.util.StackOption
import net.minecraft.inventory.InventoryCrafting
import net.minecraft.item.ItemStack
import net.minecraft.item.crafting.IRecipe
@ -14,7 +15,7 @@ import scala.collection.immutable
class LootDiskCyclingRecipe extends IRecipe {
override def matches(crafting: InventoryCrafting, world: World): Boolean = {
val stacks = collectStacks(crafting).toArray
stacks.count(!_.isEmpty) == 2 && stacks.exists(Loot.isLootDisk) && stacks.exists(Wrench.isWrench)
stacks.length == 2 && stacks.exists(Loot.isLootDisk) && stacks.exists(Wrench.isWrench)
}
override def getCraftingResult(crafting: InventoryCrafting): ItemStack = {
@ -31,7 +32,7 @@ class LootDiskCyclingRecipe extends IRecipe {
def getLootFactoryName(stack: ItemStack): String = stack.getTagCompound.getString(Settings.namespace + "lootFactory")
def collectStacks(crafting: InventoryCrafting): immutable.IndexedSeq[ItemStack] = (0 until crafting.getSizeInventory).flatMap(i => Option(crafting.getStackInSlot(i)))
def collectStacks(crafting: InventoryCrafting): immutable.IndexedSeq[ItemStack] = (0 until crafting.getSizeInventory).flatMap(i => StackOption(crafting.getStackInSlot(i)))
override def getRecipeSize: Int = 2

View File

@ -15,6 +15,8 @@ import li.cil.oc.api.network._
import li.cil.oc.common.template.AssemblerTemplates
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.StackOption
import li.cil.oc.util.StackOption._
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.EnumFacing
@ -29,7 +31,7 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits
withConnector(Settings.get.bufferConverter).
create()
var output: Option[ItemStack] = None
var output: StackOption = EmptyStack
var totalRequiredEnergy = 0.0
@ -87,7 +89,7 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits
if (!stack.isEmpty && !isItemValidForSlot(slot, stack)) return false
}
val (stack, energy) = template.assemble(this)
output = Some(stack)
output = StackOption(stack)
if (finishImmediately) {
totalRequiredEnergy = 0
}
@ -123,16 +125,16 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits
override def updateEntity() {
super.updateEntity()
if (output.isDefined && getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) {
if (!output.isEmpty && getWorld.getTotalWorldTime % Settings.get.tickFrequency == 0) {
val want = math.max(1, math.min(requiredEnergy, Settings.get.assemblerTickAmount * Settings.get.tickFrequency))
val have = want + (if (Settings.get.ignorePower) 0 else node.changeBuffer(-want))
requiredEnergy -= have
if (requiredEnergy <= 0) {
setInventorySlotContents(0, output.get)
output = None
output = EmptyStack
requiredEnergy = 0
}
ServerPacketSender.sendRobotAssembling(this, have > 0.5 && output.isDefined)
ServerPacketSender.sendRobotAssembling(this, have > 0.5 && !output.isEmpty)
}
}
@ -146,10 +148,10 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits
override def readFromNBTForServer(nbt: NBTTagCompound) {
super.readFromNBTForServer(nbt)
if (nbt.hasKey(OutputTag)) {
output = Option(new ItemStack(nbt.getCompoundTag(OutputTag)))
output = StackOption(new ItemStack(nbt.getCompoundTag(OutputTag)))
}
else if (nbt.hasKey(OutputTagCompat)) {
output = Option(new ItemStack(nbt.getCompoundTag(OutputTagCompat)))
output = StackOption(new ItemStack(nbt.getCompoundTag(OutputTagCompat)))
}
totalRequiredEnergy = nbt.getDouble(TotalTag)
requiredEnergy = nbt.getDouble(RemainingTag)
@ -157,7 +159,7 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits
override def writeToNBTForServer(nbt: NBTTagCompound) {
super.writeToNBTForServer(nbt)
output.foreach(stack => nbt.setNewCompoundTag(OutputTag, stack.writeToNBT))
nbt.setNewCompoundTag(OutputTag, output.get.writeToNBT)
nbt.setDouble(TotalTag, totalRequiredEnergy)
nbt.setDouble(RemainingTag, requiredEnergy)
}

View File

@ -17,6 +17,8 @@ import li.cil.oc.common.Tier
import li.cil.oc.common.item.data.MicrocontrollerData
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.StackOption
import li.cil.oc.util.StackOption._
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
@ -260,17 +262,17 @@ class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.C
override def removeStackFromSlot(slot: Int) = ItemStack.EMPTY
// For hotswapping EEPROMs.
def changeEEPROM(newEeprom: ItemStack): Option[ItemStack] = {
def changeEEPROM(newEeprom: ItemStack): StackOption = {
val oldEepromIndex = info.components.indexWhere(api.Items.get(_) == api.Items.get(Constants.ItemName.EEPROM))
if (oldEepromIndex >= 0) {
val oldEeprom = info.components(oldEepromIndex)
super.setInventorySlotContents(oldEepromIndex, newEeprom)
Some(oldEeprom)
SomeStack(oldEeprom)
}
else {
assert(info.components(getSizeInventory - 1).isEmpty)
super.setInventorySlotContents(getSizeInventory - 1, newEeprom)
None
EmptyStack
}
}
}

View File

@ -16,6 +16,8 @@ import li.cil.oc.api.util.StateAware
import li.cil.oc.common.item.data.PrintData
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.StackOption
import li.cil.oc.util.StackOption._
import net.minecraft.inventory.ISidedInventory
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
@ -40,7 +42,7 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat
var data = new PrintData()
var isActive = false
var limit = 0
var output: Option[ItemStack] = None
var output: StackOption = EmptyStack
var totalRequiredEnergy = 0.0
var requiredEnergy = 0.0
@ -248,7 +250,7 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat
amountMaterial -= materialRequired
amountInk -= inkRequired
limit -= 1
output = Option(data.createItemStack())
output = StackOption(data.createItemStack())
if (limit < 1) isActive = false
ServerPacketSender.sendPrinting(this, printing = true)
}
@ -275,7 +277,7 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat
return
}
requiredEnergy = 0
output = None
output = EmptyStack
}
ServerPacketSender.sendPrinting(this, have > 0.5 && output.isDefined)
}
@ -319,10 +321,10 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat
isActive = nbt.getBoolean(IsActiveTag)
limit = nbt.getInteger(LimitTag)
if (nbt.hasKey(OutputTag)) {
output = Option(new ItemStack(nbt.getCompoundTag(OutputTag)))
output = StackOption(new ItemStack(nbt.getCompoundTag(OutputTag)))
}
else {
output = None
output = EmptyStack
}
totalRequiredEnergy = nbt.getDouble(TotalTag)
requiredEnergy = nbt.getDouble(RemainingTag)

View File

@ -6,6 +6,8 @@ import li.cil.oc.api.network.Node
import li.cil.oc.common.EventHandler
import li.cil.oc.common.inventory
import li.cil.oc.util.ExtendedInventory._
import li.cil.oc.util.StackOption
import li.cil.oc.util.StackOption._
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.EnumFacing
@ -24,8 +26,8 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo
// Cache changes to inventory slots on the client side to avoid recreating
// components when we don't have to and the slots are just cleared by MC
// temporarily.
private lazy val pendingRemovalsActual = mutable.ArrayBuffer.fill(getSizeInventory)(None: Option[ItemStack])
private lazy val pendingAddsActual = mutable.ArrayBuffer.fill(getSizeInventory)(None: Option[ItemStack])
private lazy val pendingRemovalsActual = mutable.ArrayBuffer.fill(getSizeInventory)(EmptyStack: StackOption)
private lazy val pendingAddsActual = mutable.ArrayBuffer.fill(getSizeInventory)(EmptyStack: StackOption)
private var updateScheduled = false
def pendingRemovals = {
adjustSize(pendingRemovalsActual)
@ -36,7 +38,7 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo
pendingAddsActual
}
private def adjustSize[T](buffer: mutable.ArrayBuffer[Option[T]]): Unit = {
private def adjustSize(buffer: mutable.ArrayBuffer[StackOption]): Unit = {
val delta = buffer.length - getSizeInventory
if (delta > 0) {
buffer.remove(buffer.length - delta, delta)
@ -44,7 +46,7 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo
else if (delta < 0) {
buffer.sizeHint(getSizeInventory)
for (i <- 0 until -delta) {
buffer += None
buffer += EmptyStack
}
}
}
@ -53,23 +55,23 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo
updateScheduled = false
for (slot <- this.indices) {
(pendingRemovals(slot), pendingAdds(slot)) match {
case (Some(removed), Some(added)) =>
case (SomeStack(removed), SomeStack(added)) =>
if (!removed.isItemEqual(added) || !ItemStack.areItemStackTagsEqual(removed, added)) {
super.onItemRemoved(slot, removed)
super.onItemAdded(slot, added)
markDirty()
} // else: No change, ignore.
case (Some(removed), None) =>
case (SomeStack(removed), EmptyStack) =>
super.onItemRemoved(slot, removed)
markDirty()
case (None, Some(added)) =>
case (EmptyStack, SomeStack(added)) =>
super.onItemAdded(slot, added)
markDirty()
case _ => // No change.
}
pendingRemovals(slot) = None
pendingAdds(slot) = None
pendingRemovals(slot) = EmptyStack
pendingAdds(slot) = EmptyStack
}
}
@ -84,13 +86,13 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo
if (isServer) super.onItemAdded(slot, stack)
else {
pendingRemovals(slot) match {
case Some(removed) if removed.isItemEqual(stack) && ItemStack.areItemStackTagsEqual(removed, stack) =>
case SomeStack(removed) if removed.isItemEqual(stack) && ItemStack.areItemStackTagsEqual(removed, stack) =>
// Reverted to original state.
pendingAdds(slot) = None
pendingRemovals(slot) = None
pendingAdds(slot) = EmptyStack
pendingRemovals(slot) = EmptyStack
case _ =>
// Got a removal and an add of *something else* in the same tick.
pendingAdds(slot) = Option(stack)
pendingAdds(slot) = StackOption(stack)
scheduleInventoryChange()
}
}
@ -100,15 +102,15 @@ trait ComponentInventory extends Environment with Inventory with inventory.Compo
if (isServer) super.onItemRemoved(slot, stack)
else {
pendingAdds(slot) match {
case Some(added) =>
case SomeStack(added) =>
// If we have a pending add and get a remove on a slot it is
// now either empty, or the previous remove is valid again.
pendingAdds(slot) = None
pendingAdds(slot) = EmptyStack
case _ =>
// If we have no pending add, only the first removal can be
// relevant (further ones should in fact be impossible).
if (pendingRemovals(slot).isEmpty) {
pendingRemovals(slot) = Option(stack)
pendingRemovals(slot) = StackOption(stack)
scheduleInventoryChange()
}
}

View File

@ -5,6 +5,7 @@ import li.cil.oc.Settings
import li.cil.oc.api.Items
import li.cil.oc.integration.util.ItemBlacklist
import li.cil.oc.integration.util.ItemSearch
import li.cil.oc.util.StackOption
import mezz.jei.api.IJeiRuntime
import mezz.jei.api.IModPlugin
import mezz.jei.api.IModRegistry
@ -42,13 +43,13 @@ class ModPluginOpenComputers extends IModPlugin {
ModJEI.ingredientRegistry = Option(registry.getIngredientRegistry)
}
private var stackUnderMouse: (GuiContainer, Int, Int) => Option[ItemStack] = _
private var stackUnderMouse: (GuiContainer, Int, Int) => StackOption = _
override def onRuntimeAvailable(jeiRuntime: IJeiRuntime) {
if (stackUnderMouse == null) {
ItemSearch.stackFocusing += ((container, mouseX, mouseY) => stackUnderMouse(container, mouseX, mouseY))
}
stackUnderMouse = (container, mouseX, mouseY) => Option(jeiRuntime.getItemListOverlay.getStackUnderMouse)
stackUnderMouse = (container, mouseX, mouseY) => StackOption(jeiRuntime.getItemListOverlay.getStackUnderMouse)
ModJEI.runtime = Option(jeiRuntime)
}

View File

@ -1,14 +1,15 @@
package li.cil.oc.integration.util
import li.cil.oc.util.StackOption
import li.cil.oc.util.StackOption._
import net.minecraft.client.gui.inventory.GuiContainer
import net.minecraft.item.ItemStack
import scala.collection.mutable
object ItemSearch {
val focusedInput = mutable.Set.empty[() => Boolean]
val stackFocusing = mutable.Set.empty[(GuiContainer, Int, Int) => Option[ItemStack]]
val stackFocusing = mutable.Set.empty[(GuiContainer, Int, Int) => StackOption]
def isInputFocused: Boolean = {
for (f <- focusedInput) {
@ -17,10 +18,10 @@ object ItemSearch {
false
}
def hoveredStack(container: GuiContainer, mouseX: Int, mouseY: Int): Option[ItemStack] = {
def hoveredStack(container: GuiContainer, mouseX: Int, mouseY: Int): StackOption = {
for (f <- stackFocusing) {
f(container, mouseX, mouseY).foreach(stack => return Option(stack))
f(container, mouseX, mouseY).foreach(stack => return StackOption(stack))
}
None
EmptyStack
}
}

View File

@ -21,6 +21,8 @@ import li.cil.oc.server.PacketSender
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.StackOption
import li.cil.oc.util.StackOption._
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.EnumFacing
import net.minecraft.util.EnumParticleTypes
@ -71,8 +73,8 @@ class Robot(val agent: tileentity.Robot) extends AbstractManagedEnvironment with
@Callback(doc = "function():number -- Get the durability of the currently equipped tool.")
def durability(context: Context, args: Arguments): Array[AnyRef] = {
Option(agent.equipmentInventory.getStackInSlot(0)) match {
case Some(item) =>
StackOption(agent.equipmentInventory.getStackInSlot(0)) match {
case SomeStack(item) =>
ToolDurabilityProviders.getDurability(item) match {
case Some(durability) => result(durability)
case _ => result(Unit, "tool cannot be damaged")

View File

@ -18,6 +18,7 @@ import li.cil.oc.api.prefab.AbstractManagedEnvironment
import li.cil.oc.util.DatabaseAccess
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ItemUtils
import li.cil.oc.util.StackOption
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
@ -40,7 +41,7 @@ class UpgradeDatabase(val data: IInventory) extends AbstractManagedEnvironment w
override def size = data.getSizeInventory
override def getStackInSlot(slot: Int) = Option(data.getStackInSlot(slot)).map(_.copy()).orNull
override def getStackInSlot(slot: Int) = StackOption(data.getStackInSlot(slot)).map(_.copy()).orEmpty
override def setStackInSlot(slot: Int, stack: ItemStack) = data.setInventorySlotContents(slot, stack)

View File

@ -17,6 +17,8 @@ import li.cil.oc.api.network._
import li.cil.oc.api.prefab
import li.cil.oc.api.prefab.AbstractManagedEnvironment
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.StackOption
import li.cil.oc.util.StackOption._
import net.minecraft.entity.item.EntityItem
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
@ -30,7 +32,7 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab
withConnector().
create()
var inventory: Option[ItemStack] = None
var inventory: StackOption = EmptyStack
var remainingTicks = 0
@ -55,7 +57,7 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab
return result(Unit, "selected slot does not contain fuel")
}
inventory match {
case Some(existingStack) =>
case SomeStack(existingStack) =>
if (!existingStack.isItemEqual(stack) ||
!ItemStack.areItemStackTagsEqual(existingStack, stack)) {
return result(Unit, "different fuel type already queued")
@ -68,7 +70,7 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab
existingStack.grow(moveCount)
stack.shrink(moveCount)
case _ =>
inventory = Some(stack.splitStack(math.min(stack.getCount, count)))
inventory = StackOption(stack.splitStack(math.min(stack.getCount, count)))
}
if (stack.getCount > 0) host.mainInventory.setInventorySlotContents(host.selectedSlot, stack)
else host.mainInventory.setInventorySlotContents(host.selectedSlot, ItemStack.EMPTY)
@ -78,7 +80,7 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab
@Callback(doc = """function():number -- Get the size of the item stack in the generator's queue.""")
def count(context: Context, args: Arguments): Array[AnyRef] = {
inventory match {
case Some(stack) => result(stack.getCount)
case SomeStack(stack) => result(stack.getCount)
case _ => result(0)
}
}
@ -87,12 +89,12 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab
def remove(context: Context, args: Arguments): Array[AnyRef] = {
val count = args.optInteger(0, Int.MaxValue)
inventory match {
case Some(stack) =>
case SomeStack(stack) =>
val removedStack = stack.splitStack(math.min(count, stack.getCount))
val success = host.player.inventory.addItemStackToInventory(removedStack)
stack.grow(removedStack.getCount)
if (success && stack.getCount <= 0) {
inventory = None
inventory = EmptyStack
}
result(success)
case _ => result(false)
@ -114,9 +116,9 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab
stack.shrink(1)
if (stack.getCount <= 0) {
if (stack.getItem.hasContainerItem(stack))
inventory = Option(stack.getItem.getContainerItem(stack))
inventory = StackOption(stack.getItem.getContainerItem(stack))
else
inventory = None
inventory = EmptyStack
}
}
}
@ -140,13 +142,13 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab
super.onDisconnect(node)
if (node == this.node) {
inventory match {
case Some(stack) =>
case SomeStack(stack) =>
val world = host.world
val entity = new EntityItem(world, host.xPosition, host.yPosition, host.zPosition, stack.copy())
entity.motionY = 0.04
entity.setPickupDelay(5)
world.spawnEntity(entity)
inventory = None
inventory = EmptyStack
case _ =>
}
remainingTicks = 0
@ -158,9 +160,9 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab
override def load(nbt: NBTTagCompound) {
super.load(nbt)
inventory = Option(new ItemStack(nbt.getCompoundTag("inventory")))
inventory = StackOption(new ItemStack(nbt.getCompoundTag("inventory")))
if (nbt.hasKey(InventoryTag)) {
inventory = Option(new ItemStack(nbt.getCompoundTag(InventoryTag)))
inventory = StackOption(new ItemStack(nbt.getCompoundTag(InventoryTag)))
}
remainingTicks = nbt.getInteger(RemainingTicksTag)
}
@ -168,7 +170,7 @@ class UpgradeGenerator(val host: EnvironmentHost with internal.Agent) extends Ab
override def save(nbt: NBTTagCompound) {
super.save(nbt)
inventory match {
case Some(stack) => nbt.setNewCompoundTag(InventoryTag, stack.writeToNBT)
case SomeStack(stack) => nbt.setNewCompoundTag(InventoryTag, stack.writeToNBT)
case _ =>
}
if (remainingTicks > 0) {

View File

@ -8,6 +8,7 @@ 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 li.cil.oc.util.StackOption._
import net.minecraft.item.ItemStack
import net.minecraftforge.oredict.OreDictionary
@ -23,8 +24,8 @@ trait InventoryAnalytics extends InventoryAware with NetworkAware {
def isEquivalentTo(context: Context, args: Arguments): Array[AnyRef] = {
val slot = args.checkSlot(inventory, 0)
result((stackInSlot(selectedSlot), stackInSlot(slot)) match {
case (Some(stackA), Some(stackB)) => OreDictionary.getOreIDs(stackA).intersect(OreDictionary.getOreIDs(stackB)).nonEmpty
case (None, None) => true
case (SomeStack(stackA), SomeStack(stackB)) => OreDictionary.getOreIDs(stackA).intersect(OreDictionary.getOreIDs(stackB)).nonEmpty
case (EmptyStack, EmptyStack) => true
case _ => false
})
}

View File

@ -2,6 +2,7 @@ package li.cil.oc.server.component.traits
import li.cil.oc.api.machine.Arguments
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.StackOption
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory
@ -24,5 +25,5 @@ trait InventoryAware {
if (args.count > 0 && args.checkAny(0) != null) args.checkSlot(inventory, 0)
else selectedSlot
protected def stackInSlot(slot: Int) = Option(inventory.getStackInSlot(slot))
protected def stackInSlot(slot: Int): StackOption = StackOption(inventory.getStackInSlot(slot))
}

View File

@ -6,6 +6,7 @@ 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 li.cil.oc.util.StackOption._
import net.minecraft.item.ItemStack
trait InventoryControl extends InventoryAware {
@ -25,7 +26,7 @@ trait InventoryControl extends InventoryAware {
def count(context: Context, args: Arguments): Array[AnyRef] = {
val slot = optSlot(args, 0)
result(stackInSlot(slot) match {
case Some(stack) => stack.getCount
case SomeStack(stack) => stack.getCount
case _ => 0
})
}
@ -34,7 +35,7 @@ trait InventoryControl extends InventoryAware {
def space(context: Context, args: Arguments): Array[AnyRef] = {
val slot = optSlot(args, 0)
result(stackInSlot(slot) match {
case Some(stack) => math.min(inventory.getInventoryStackLimit, stack.getMaxStackSize) - stack.getCount
case SomeStack(stack) => math.min(inventory.getInventoryStackLimit, stack.getMaxStackSize) - stack.getCount
case _ => inventory.getInventoryStackLimit
})
}
@ -43,8 +44,8 @@ trait InventoryControl extends InventoryAware {
def compareTo(context: Context, args: Arguments): Array[AnyRef] = {
val slot = args.checkSlot(inventory, 0)
result((stackInSlot(selectedSlot), stackInSlot(slot)) match {
case (Some(stackA), Some(stackB)) => InventoryUtils.haveSameItemType(stackA, stackB, args.optBoolean(1, false))
case (None, None) => true
case (SomeStack(stackA), SomeStack(stackB)) => InventoryUtils.haveSameItemType(stackA, stackB, args.optBoolean(1, false))
case (EmptyStack, EmptyStack) => true
case _ => false
})
}
@ -57,7 +58,7 @@ trait InventoryControl extends InventoryAware {
result(true)
}
else result((stackInSlot(selectedSlot), stackInSlot(slot)) match {
case (Some(from), Some(to)) =>
case (SomeStack(from), SomeStack(to)) =>
if (InventoryUtils.haveSameItemType(from, to, checkNBT = true)) {
val space = math.min(inventory.getInventoryStackLimit, to.getMaxStackSize) - to.getCount
val amount = math.min(count, math.min(space, from.getCount))
@ -79,7 +80,7 @@ trait InventoryControl extends InventoryAware {
true
}
else false
case (Some(from), None) =>
case (SomeStack(from), EmptyStack) =>
inventory.setInventorySlotContents(slot, inventory.decrStackSize(selectedSlot, count))
true
case _ => false

View File

@ -7,6 +7,7 @@ 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 li.cil.oc.util.StackOption._
import net.minecraft.entity.item.EntityItem
import net.minecraft.item.ItemBlock
import net.minecraft.item.ItemStack
@ -22,7 +23,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
def compare(context: Context, args: Arguments): Array[AnyRef] = {
val side = checkSideForAction(args, 0)
stackInSlot(selectedSlot) match {
case Some(stack) => Option(stack.getItem) match {
case SomeStack(stack) => Option(stack.getItem) match {
case Some(item: ItemBlock) =>
val blockPos = position.offset(side).toBlockPos
val state = world.getBlockState(blockPos)

View File

@ -8,6 +8,7 @@ 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 li.cil.oc.util.StackOption
import net.minecraft.item.ItemStack
import net.minecraft.util.EnumFacing
import net.minecraftforge.items.IItemHandler
@ -23,13 +24,13 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ
@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.""")
def getSlotStackSize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
withInventory(facing, inventory => result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.getCount)))
withInventory(facing, inventory => result(StackOption(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.getCount)))
}
@Callback(doc = """function(side:number, slot:number):number -- Get the maximum number of items in the specified slot of the inventory on the specified side of the device.""")
def getSlotMaxStackSize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
withInventory(facing, inventory => result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.getMaxStackSize)))
withInventory(facing, inventory => result(StackOption(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.getMaxStackSize)))
}
@Callback(doc = """function(side:number, slotA:number, slotB:number[, checkNBT:boolean=false]):boolean -- Get whether the items in the two specified slots of the inventory on the specified side of the device are of the same type.""")

View File

@ -2,6 +2,7 @@ package li.cil.oc.util
import li.cil.oc.OpenComputers
import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.StackOption._
import net.minecraft.entity.Entity
import net.minecraft.entity.item.EntityItem
import net.minecraft.entity.player.EntityPlayer
@ -328,8 +329,8 @@ object InventoryUtils {
* the world.
*/
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.getCount > 0 => spawnStackInWorld(position, stack, direction); true
StackOption(inventory.decrStackSize(slot, count)) match {
case SomeStack(stack) if stack.getCount > 0 => spawnStackInWorld(position, stack, direction); true
case _ => false
}
}
@ -339,8 +340,8 @@ object InventoryUtils {
*/
def dropAllSlots(position: BlockPosition, inventory: IInventory): Unit = {
for (slot <- 0 until inventory.getSizeInventory) {
Option(inventory.getStackInSlot(slot)) match {
case Some(stack) if stack.getCount > 0 =>
StackOption(inventory.getStackInSlot(slot)) match {
case SomeStack(stack) if stack.getCount > 0 =>
inventory.setInventorySlotContents(slot, ItemStack.EMPTY)
spawnStackInWorld(position, stack)
case _ => // Nothing.

View File

@ -0,0 +1,97 @@
package li.cil.oc.util
import li.cil.oc.util.StackOption._
import net.minecraft.item.ItemStack
object StackOption {
//implicit def extendedStack(stack: ItemStack): StackOption = if (stack.isEmpty) Empty else new StackOption(stack)
implicit def extendedOption(opt: Option[ItemStack]): ExtendedOption = ExtendedOption(opt)
// Mostly stolen from Option
implicit def stack2Iterable(so: StackOption): Iterable[ItemStack] = so.toList
def apply(stack: ItemStack): StackOption = if (stack == null || stack.isEmpty) EmptyStack else SomeStack(stack)
def apply(stack: Option[ItemStack]): StackOption = if (stack == null || stack.isEmpty || stack.get.isEmpty) EmptyStack else SomeStack(stack.get)
def empty: StackOption = EmptyStack
case object EmptyStack extends StackOption(ItemStack.EMPTY)
final case class SomeStack(stack: ItemStack) extends StackOption(stack)
}
final case class ExtendedOption(opt : Option[ItemStack]) {
def asStackOption = StackOption(opt)
}
sealed abstract class StackOption(stack: ItemStack) extends Product with Serializable {
self =>
def isEmpty: Boolean = stack == null || stack.isEmpty
def get: ItemStack = stack
def isDefined: Boolean = !isEmpty
def getOrElse(default: ItemStack): ItemStack = if (isEmpty) default else this.get
def orEmpty: ItemStack = if(isEmpty) EmptyStack.get else this.get
def map(f: ItemStack => ItemStack): StackOption = if (isEmpty) EmptyStack else SomeStack(f(this.get))
//def map[B](f: ItemStack => B): Option[B] = if (isEmpty) None else Some(f(this.get))
def fold[B](ifEmpty: => B)(f: ItemStack => B): B = if (isEmpty) ifEmpty else f(this.get)
def flatMap[B](f: ItemStack => Option[B]): Option[B] = if (isEmpty) None else f(this.get)
def flatten[B](implicit ev: ItemStack <:< Option[B]): Option[B] = if (isEmpty) None else ev(this.get)
def filter(p: ItemStack => Boolean): StackOption = if (isEmpty || p(this.get)) this else EmptyStack
def filterNot(p: ItemStack => Boolean): StackOption = if (isEmpty || !p(this.get)) this else EmptyStack
def nonEmpty: Boolean = isDefined
def withFilter(p: ItemStack => Boolean): WithFilter = new WithFilter(p)
class WithFilter(p: ItemStack => Boolean) {
//def map[B](f: ItemStack => B): Option[B] = self filter p map f
//def flatMap[B](f: ItemStack => Option[B]): Option[B] = self filter p flatMap f
def foreach[U](f: ItemStack => U): Unit = self filter p foreach f
def withFilter(q: ItemStack => Boolean): WithFilter = new WithFilter(x => p(x) && q(x))
}
def contains[A1 >: ItemStack](elem: A1): Boolean = !isEmpty && this.get == elem
def exists(p: ItemStack => Boolean): Boolean = !isEmpty && p(this.get)
def forall(p: ItemStack => Boolean): Boolean = isEmpty || p(this.get)
def foreach[U](f: ItemStack => U) {
if (!isEmpty) f(this.get)
}
def collect[B](pf: PartialFunction[ItemStack, B]): Option[B] = if (!isEmpty) pf.lift(this.get) else None
def orElse[B >: ItemStack](alternative: => StackOption): StackOption =
if (isEmpty) alternative else this
def iterator: Iterator[ItemStack] =
if (isEmpty) collection.Iterator.empty else collection.Iterator.single(this.get)
def toList: List[ItemStack] =
if (isEmpty) List() else new ::(this.get, Nil)
def toRight[X](left: => X): Either[X, ItemStack] =
if (isEmpty) Left(left) else Right(this.get)
def toLeft[X](right: => X): Either[ItemStack, X] =
if (isEmpty) Right(right) else Left(this.get)
}