From 654b1507bd24facc55a95156789d8879005d49dc Mon Sep 17 00:00:00 2001 From: bibo38 Date: Fri, 27 Mar 2020 19:27:24 +0100 Subject: [PATCH] Simulating the insertion instead of inserting directly Previously the extraction was simulated and based on the simulated extraction the extracted items were inserted. This was problematic, as it relyed on the fact, that the simulation returns the same result as the actual extraction. This caused problems, as extracting a component only saves the changes to the component, when it is not simulated. Furthermore it could cause item deduplication, if the actual extraction returned something different. To solve these issues, the insertion is also simulated on the simulated extraction and the actual insertion is done on the actual extracted items. --- .../traits/InventoryWorldControl.scala | 2 +- .../traits/InventoryWorldControlMk2.scala | 2 +- .../traits/ItemInventoryControl.scala | 4 +-- .../scala/li/cil/oc/util/InventoryUtils.scala | 36 ++++++++++--------- 4 files changed, 23 insertions(+), 21 deletions(-) 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 28efa6716..c1c7f8efd 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 @@ -105,7 +105,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest val blockPos = position.offset(facing) var extracted: Int = InventoryUtils.inventoryAt(blockPos, facing.getOpposite) match { case Some(inventory) => mayInteract(blockPos, facing.getOpposite) - InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, InventoryUtils.asItemHandler(this.inventory), slots = Option(insertionSlots)), inventory, count) + InventoryUtils.extractAnyFromInventory((is, sim) => InventoryUtils.insertIntoInventory(is, InventoryUtils.asItemHandler(this.inventory), slots = Option(insertionSlots), simulate = sim), inventory, count) case _ => 0 } if (extracted <= 0) { diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala index 60914b32f..5df2854a9 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala @@ -50,7 +50,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR val fromSide = args.optSideAny(3, facing.getOpposite) withInventory(position.offset(facing), fromSide, inventory => { val slot = args.checkSlot(inventory, 1) - val extracted = InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, InventoryUtils.asItemHandler(this.inventory), slots = Option(insertionSlots)), inventory, slot, count) + val extracted = InventoryUtils.extractFromInventorySlot((is, sim) => InventoryUtils.insertIntoInventory(is, InventoryUtils.asItemHandler(this.inventory), slots = Option(insertionSlots), simulate = sim), inventory, slot, count) if (extracted > 0) { context.pause(Settings.get.suckDelay) result(extracted) diff --git a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala index 42026cd36..61f078575 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/ItemInventoryControl.scala @@ -21,7 +21,7 @@ trait ItemInventoryControl extends InventoryAware { withItemInventory(args.checkSlot(inventory, 0), itemInventory => { val slot = args.checkSlot(itemInventory, 1) val count = args.optItemCount(2) - result(InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventorySlot(_, itemInventory, slot), inventory, null, selectedSlot, count)) + result(InventoryUtils.extractFromInventorySlot((is, sim) => InventoryUtils.insertIntoInventorySlot(is, itemInventory, slot, simulate = sim), inventory, null, selectedSlot, count)) }) } @@ -30,7 +30,7 @@ trait ItemInventoryControl extends InventoryAware { withItemInventory(args.checkSlot(inventory, 0), itemInventory => { val slot = args.checkSlot(itemInventory, 1) val count = args.optItemCount(2) - result(InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots)), itemInventory, slot, count)) + result(InventoryUtils.extractFromInventorySlot((is, sim) => InventoryUtils.insertIntoInventory(is, InventoryUtils.asItemHandler(inventory), slots = Option(insertionSlots), simulate = sim), itemInventory, slot, count)) }) } diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala index 6b9595a13..cfb4efa6e 100644 --- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala +++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala @@ -110,9 +110,9 @@ object InventoryUtils { *

* Only tries to extract from the specified slot. This can be used * to empty a slot. It will extract items using the specified consumer method - * which is called with the extracted stack before the stack in the inventory - * that we extract from is cleared from. This allows placing back excess - * items with as few inventory updates as possible. + * which is called with the extracted stack and a simulation flag before the + * stack in the inventory that we extract from is cleared from. This allows + * placing back excess items with as few inventory updates as possible. *

* The consumer is the only way to retrieve the actually extracted stack. It * is called with a separate stack instance, so it does not have to be copied @@ -129,7 +129,7 @@ object InventoryUtils { * also be achieved by a check in the consumer, but it saves some unnecessary * code repetition this way. */ - def extractFromInventorySlot(consumer: ItemStack => Unit, inventory: IItemHandler, slot: Int, limit: Int = 64): Int = { + def extractFromInventorySlot(consumer: (ItemStack, Boolean) => Unit, inventory: IItemHandler, slot: Int, limit: Int = 64): Int = { val stack = inventory.getStackInSlot(slot) if (stack.isEmpty || limit <= 0 || stack.getCount <= 0) return 0 @@ -138,19 +138,19 @@ object InventoryUtils { case simExtracted: ItemStack => val extracted = simExtracted.copy amount = extracted.getCount - consumer(extracted) + consumer(extracted, true) val count = (amount - extracted.getCount) max 0 if (count > 0) inventory.extractItem(slot, count, false) match { - case realExtracted: ItemStack if realExtracted.getCount == count => + case realExtracted: ItemStack if realExtracted.getCount == count => consumer(realExtracted, false) case _ => - OpenComputers.log.warn("Items may have been duplicated during inventory extraction. This means an IItemHandler instance acted differently between simulated and non-simulated extraction. Offender: " + inventory) + OpenComputers.log.warn("An IItemHandler instance acted differently between simulated and non-simulated extraction. Offender: " + inventory) } count case _ => 0 } } - def extractFromInventorySlot(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, slot: Int, limit: Int): Int = + def extractFromInventorySlot(consumer: (ItemStack, Boolean) => Unit, inventory: IInventory, side: EnumFacing, slot: Int, limit: Int): Int = extractFromInventorySlot(consumer, asItemHandler(inventory, side), slot, limit) /** @@ -200,7 +200,7 @@ object InventoryUtils { *

* This returns true if at least one item was extracted. */ - def extractAnyFromInventory(consumer: ItemStack => Unit, inventory: IItemHandler, limit: Int = 64): Int = { + def extractAnyFromInventory(consumer: (ItemStack, Boolean) => Unit, inventory: IItemHandler, limit: Int = 64): Int = { for (slot <- 0 until inventory.getSlots) { val extracted = extractFromInventorySlot(consumer, inventory, slot, limit) if (extracted > 0) @@ -209,7 +209,7 @@ object InventoryUtils { 0 } - def extractAnyFromInventory(consumer: ItemStack => Unit, inventory: IInventory, side: EnumFacing, limit: Int): Int = + def extractAnyFromInventory(consumer: (ItemStack, Boolean) => Unit, inventory: IInventory, side: EnumFacing, limit: Int): Int = extractAnyFromInventory(consumer, asItemHandler(inventory, side), limit) /** @@ -225,11 +225,13 @@ object InventoryUtils { def extractFromInventory(stack: ItemStack, inventory: IItemHandler, simulate: Boolean = false, exact: Boolean = true): ItemStack = { val remaining = stack.copy() for (slot <- 0 until inventory.getSlots if remaining.getCount > 0) { - extractFromInventorySlot(stackInInv => { + extractFromInventorySlot((stackInInv, simulateInsert) => { if (stackInInv != null && remaining.getItem == stackInInv.getItem && (!exact || haveSameItemType(remaining, stackInInv, checkNBT = true))) { val transferred = stackInInv.getCount min remaining.getCount - remaining.shrink(transferred) - if (!simulate) { + if(!simulateInsert) { + remaining.shrink(transferred) + } + if (simulateInsert || !simulate) { stackInInv.shrink(transferred) } } @@ -254,7 +256,7 @@ object InventoryUtils { * Utility method for calling extractFromInventory on an inventory * in the world. */ - def getExtractorFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64): Extractor = + def getExtractorFromInventoryAt(consumer: (ItemStack, Boolean) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64): Extractor = inventoryAt(position, side) match { case Some(inventory) => () => extractAnyFromInventory(consumer, inventory, limit) case _ => null @@ -275,7 +277,7 @@ object InventoryUtils { */ def transferBetweenInventories(source: IItemHandler, sink: IItemHandler, limit: Int = 64): Int = extractAnyFromInventory( - insertIntoInventory(_, sink, limit), source, limit = limit) + insertIntoInventory(_, sink, limit, _), source, limit = limit) def transferBetweenInventories(source: IInventory, sourceSide: EnumFacing, sink: IInventory, sinkSide: Option[EnumFacing], limit: Int): Int = transferBetweenInventories(asItemHandler(source, sourceSide), asItemHandler(sink, sinkSide.orNull), limit) @@ -287,10 +289,10 @@ object InventoryUtils { sinkSlot match { case Some(explicitSinkSlot) => extractFromInventorySlot( - insertIntoInventorySlot(_, sink, explicitSinkSlot, limit), source, sourceSlot, limit = limit) + insertIntoInventorySlot(_, sink, explicitSinkSlot, limit, _), source, sourceSlot, limit = limit) case _ => extractFromInventorySlot( - insertIntoInventory(_, sink, limit), source, sourceSlot, limit = limit) + insertIntoInventory(_, sink, limit, _), source, sourceSlot, limit = limit) } def transferBetweenInventoriesSlots(source: IInventory, sourceSide: EnumFacing, sourceSlot: Int, sink: IInventory, sinkSide: Option[EnumFacing], sinkSlot: Option[Int], limit: Int): Int =