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.
This commit is contained in:
bibo38 2020-03-27 19:27:24 +01:00
parent a7eb1db087
commit 654b1507bd
4 changed files with 23 additions and 21 deletions

View File

@ -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) {

View File

@ -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)

View File

@ -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))
})
}

View File

@ -110,9 +110,9 @@ object InventoryUtils {
* <p/>
* Only tries to extract from the specified slot. This <em>can</em> 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.
* <p/>
* 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 {
* <p/>
* This returns <tt>true</tt> 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 <tt>extractFromInventory</tt> 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 =