From 30e3e129c8a619c7240e2e9320e10ad7f3e91bc3 Mon Sep 17 00:00:00 2001 From: Vexatos Date: Sat, 22 Apr 2017 20:16:34 +0200 Subject: [PATCH] I don't trust ItemStack Options. Let's get rid of them. --- .../oc/client/gui/DynamicGuiContainer.scala | 7 +- .../scala/li/cil/oc/common/Achievement.scala | 13 +-- .../scala/li/cil/oc/common/EventHandler.scala | 15 +-- .../cil/oc/common/block/Microcontroller.scala | 3 +- .../container/DynamicComponentSlot.scala | 3 +- .../cil/oc/common/inventory/Inventory.scala | 3 +- .../provider/DisintegrationProvider.scala | 12 ++- .../cil/oc/common/recipe/ColorizeRecipe.scala | 9 +- .../oc/common/recipe/DecolorizeRecipe.scala | 9 +- .../common/recipe/LootDiskCyclingRecipe.scala | 5 +- .../cil/oc/common/tileentity/Assembler.scala | 18 ++-- .../common/tileentity/Microcontroller.scala | 8 +- .../li/cil/oc/common/tileentity/Printer.scala | 12 ++- .../traits/ComponentInventory.scala | 34 ++++--- .../jei/ModPluginOpenComputers.scala | 5 +- .../cil/oc/integration/util/ItemSearch.scala | 11 ++- .../li/cil/oc/server/component/Robot.scala | 6 +- .../oc/server/component/UpgradeDatabase.scala | 3 +- .../server/component/UpgradeGenerator.scala | 28 +++--- .../component/traits/InventoryAnalytics.scala | 5 +- .../component/traits/InventoryAware.scala | 3 +- .../component/traits/InventoryControl.scala | 13 +-- .../traits/InventoryWorldControl.scala | 3 +- .../traits/WorldInventoryAnalytics.scala | 5 +- .../scala/li/cil/oc/util/InventoryUtils.scala | 9 +- .../scala/li/cil/oc/util/StackOption.scala | 97 +++++++++++++++++++ 26 files changed, 234 insertions(+), 105 deletions(-) create mode 100644 src/main/scala/li/cil/oc/util/StackOption.scala diff --git a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala index 90ee35b77..c6d88b14f 100644 --- a/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala +++ b/src/main/scala/li/cil/oc/client/gui/DynamicGuiContainer.scala @@ -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 } } diff --git a/src/main/scala/li/cil/oc/common/Achievement.scala b/src/main/scala/li/cil/oc/common/Achievement.scala index ce8649c6e..078e8f625 100644 --- a/src/main/scala/li/cil/oc/common/Achievement.scala +++ b/src/main/scala/li/cil/oc/common/Achievement.scala @@ -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))) } -} \ No newline at end of file +} diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index 8b0fba099..74ee667ac 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -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) diff --git a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala index d7efa81ca..c81e2d7f9 100644 --- a/src/main/scala/li/cil/oc/common/block/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/block/Microcontroller.scala @@ -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 _ => } } diff --git a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala index 09f1a8277..612a0e5a7 100644 --- a/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala +++ b/src/main/scala/li/cil/oc/common/container/DynamicComponentSlot.scala @@ -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) } } diff --git a/src/main/scala/li/cil/oc/common/inventory/Inventory.scala b/src/main/scala/li/cil/oc/common/inventory/Inventory.scala index ca25dda28..74fd0e8bd 100644 --- a/src/main/scala/li/cil/oc/common/inventory/Inventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/Inventory.scala @@ -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 // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala b/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala index 775e0f0fb..f347c5826 100644 --- a/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala +++ b/src/main/scala/li/cil/oc/common/nanomachines/provider/DisintegrationProvider.scala @@ -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 } } diff --git a/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala index 9105a9e8c..16701c544 100644 --- a/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala +++ b/src/main/scala/li/cil/oc/common/recipe/ColorizeRecipe.scala @@ -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 diff --git a/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala index b067f1b5e..8f28eaeab 100644 --- a/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala +++ b/src/main/scala/li/cil/oc/common/recipe/DecolorizeRecipe.scala @@ -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 } } diff --git a/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala b/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala index 7922ff44b..2712e3081 100644 --- a/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala +++ b/src/main/scala/li/cil/oc/common/recipe/LootDiskCyclingRecipe.scala @@ -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 diff --git a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala index 69da1b082..c65c8a164 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Assembler.scala @@ -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) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala index 382fd512e..67df23449 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala @@ -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 } } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala index 366b85778..f60e2e2d4 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala @@ -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) diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/ComponentInventory.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/ComponentInventory.scala index a61d12e5c..af21bd494 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/ComponentInventory.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/ComponentInventory.scala @@ -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() } } diff --git a/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala b/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala index 1ccc851ac..af321394f 100644 --- a/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/jei/ModPluginOpenComputers.scala @@ -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) } diff --git a/src/main/scala/li/cil/oc/integration/util/ItemSearch.scala b/src/main/scala/li/cil/oc/integration/util/ItemSearch.scala index a551f642c..8980cf262 100644 --- a/src/main/scala/li/cil/oc/integration/util/ItemSearch.scala +++ b/src/main/scala/li/cil/oc/integration/util/ItemSearch.scala @@ -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 } } diff --git a/src/main/scala/li/cil/oc/server/component/Robot.scala b/src/main/scala/li/cil/oc/server/component/Robot.scala index 2c38af741..0dced123b 100644 --- a/src/main/scala/li/cil/oc/server/component/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/Robot.scala @@ -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") diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala b/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala index 187f22e4e..13d86017d 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeDatabase.scala @@ -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) diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala index c3fa28a10..da4b15bae 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala @@ -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) { diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala index 867ac725d..c8aae3d38 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryAnalytics.scala @@ -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 }) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala index e977d1015..37dc8a43d 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala @@ -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)) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala index 04ecbe938..3a083fbe0 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala @@ -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 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 54c42d59f..5c2055695 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 @@ -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) 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 f7ec2c32a..4767921f7 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,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.""") diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala index 22a1f4279..ce1b2d5c6 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.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. diff --git a/src/main/scala/li/cil/oc/util/StackOption.scala b/src/main/scala/li/cil/oc/util/StackOption.scala new file mode 100644 index 000000000..f2f28b2de --- /dev/null +++ b/src/main/scala/li/cil/oc/util/StackOption.scala @@ -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) +}