CC: Tweaked

CC: Tweaked

57M Downloads

Add inventory swapItems() method as a workaround for lack of atomic list()+pushItems()

umnikos opened this issue ยท 5 comments

commented

Say you want to transfer only diamonds from an ender storage chest on top to a regular chest on the bottom. Simple enough:

local chest = peripheral.wrap("top")
local slot = find_diamonds(chest.list())
if slot then
  chest.pushItems("bottom",slot)
end

Exceeept that doesn't always work. There is a small amount of time between the .list() call and the .pushItems() call, so if the diamonds were replaced with dirt in the meantime, that would now mean there's dirt in the bottom chest, which would be bad. Thankfully we can use an intermediate storage device to act as a buffer; Get an item, look at what you've got and if it's not what you wanted return it back. Simple enough:

local chest = peripheral.wrap("top")
local slot = find_diamonds(chest.list())
if slot then
  chest.pushItems("right",slot)
  local buffer = peripheral.wrap("right")
  local is_diamonds = find_diamonds(buffer.list())
  if is_diamonds then
    -- let the diamonds through
    buffer.pushItems("bottom",is_diamonds)
  else 
    -- return junk back as to not clog the buffer
    buffer.pushItems("top",is_diamonds)
  end
end

Problem is, this doesn't work either! The place where this can fail is in the "return junk back" step, as the chest we took it from might now be full and thus there's no space for it. We could keep it in the buffer until the top chest is not full, but if multiple of these failures compound eventually the buffer would be completely full as well and the system will deadlock. Here's the proposed fix for this conundrum: a .swapItems() method:

local chest = peripheral.wrap("top")
local slot = find_diamonds(chest.list())
if slot then
  -- will always succeed even if the buffer is full
  chest.swapItems("right",slot,1) -- "limit" parameter doesn't make sense as it's just swapping the contents of the slots
  local buffer = peripheral.wrap("right")
  local is_diamonds = find_diamonds(buffer.list())
  if is_diamonds then
    -- let the diamonds through
    buffer.pushItems("bottom",is_diamonds)
  else 
    -- no need to return anything
  end
end

Now this will finally guarantee only diamonds get into the bottom chest and that the system will not deadlock itself.

An alternative fix to the same problem would be to implement some mutex method for storages so that .list() and .pushItems() can be done atomically, but this seems simpler to implement and more fun to use.

commented

Thank you for the detailed writeup here!

Looking at your last example, I'm not entirely convinced that swapItems function is the right solution there though. This means your buffer chest holds onto that non-diamond item until the next swap, which will not be ideal in a lot of cases!

I feel a better solution here is to have a way to filter what item pullItems/pushItems will move - enabling something like compare and swap.

That said, in your specific case, you can just borrow a trick from vanilla item filters - keep one diamond in the bottom chest, and make pushItems target that one slot. That guarantees that only diamonds can be moved.

commented

Buffer unnecessarily keeping items can be fixed by just returning whenever possible

local chest = peripheral.wrap("top")
local slot = find_diamonds(chest.list())
if slot then
  chest.swapItems("right",slot,1)
  local buffer = peripheral.wrap("right")
  local is_diamonds = find_diamonds(buffer.list())
  if is_diamonds then
    buffer.pushItems("bottom",is_diamonds)
  else 
    -- return the item if possible
    buffer.pushItems("top",1)
  end
end

The vanilla item sorter trick only works on stackable items which is not all items you might want to sort in a storage system.

I can't dispute the usefulness of a dedicated filter parameter on pushItems/pullItems but I worry that depending on the way it gets implemented it might be inflexible; if it accepts an item name then sorting shulker boxes through nbt data is not possible. It's probably way more user friendly than my proposed solution, though.

commented

Another thing that is possible is to implement storage API similar to fluid_storage logic, rather that slot-based :)

commented

@SquidDev Sorry for the nudge; Has any work been done on filter parameters for pushItems/pullItems? After some thinking I have realized that every practical use case is covered by being able to filter on item name and nbt hash.

commented

Has any work been done on filter parameters for pushItems/pullItems?

Not yet, nope.