CC: Tweaked

CC: Tweaked

57M Downloads

Inventory methods seem to destroy all the peripheral events in the queue

Vap0r1ze opened this issue · 4 comments

commented

Minecraft Version

1.19.x

Version

1.103.1

Details

The situation

I have a computer connected to a wired modem with multiple chests next to them like this:

and I have a program that listens to the peripheral event to see when new inventories are attached

function is_inv(name)
  local types = {peripheral.getType(name)}
  for _, type in ipairs(types) do
    if type == "inventory" then
      return true
    end
  end
  return false
end

while true do
  evt, name = os.pullEvent("peripheral")
  if is_inv(name) then
    print(name .. " is an inv")
    -- local items = peripheral.wrap(name).list()
  else
    print(name .. " is not an env")
  end
end

The issue

Turn the modem off and then back on again.

If you were to run that code as-is, you would see nothing wrong (three chests attached logged like expected). But if you uncomment the items line, only the first event in the burst will be logged. You can turn the modem on and off multiple times, and each you will only see the first inventory printed.

This only happens when you call one of the methods on the inventory, like .list() or .getItemLimit(i). The scope of the issue may be broader than I described it in the title this issue. It may be that it destroys all events in the queue, or that any peripheral method will have this affect.

commented

ohh timers are so true, i can cancel them too to debounce. thank uu ^^

commented

The problem here is that some peripheral methods (and several other functions, such as rednet.receive) also need to interact with the event queue1. This means that they will consume any events which are queued while that function is running.

The easiest way to avoid this is to run several functions in parallel: one to call list and another to wait for new peripherals. There's a recent example here which does something similar (albeit with rednet messages), which might be a good reference.

Footnotes

  1. In the case of peripheral methods, this is because we need to run some code which interacts with the world, which means we need to wait for the next server tick.

commented

This makes sense, I think it'd be great if I could push my own event like container_attach, do you recommend a good way to debounce that, tick-wise?

commented

Hrmr, it depends on how you want to do debouncing really. I assume you'd still want to gather the names of all attached peripherals?

For simple debouncing, you can do something simple with os.startTimer, I guess maybe something like this:

local timer = nil

while true do
  local evt, arg = os.pullEvent()
  if evt == "timer" and arg == timer then
    os.queueEvent("container_attach") -- Or something else!
  elseif evt == "peripheral" and peripheral.hasType(arg, "inventory") then
    -- ^ forgot to mention this, but peripheral.hasType does the same job as your is_inv function.

    if not timer then timer = os.startTimer(0.5) end -- Assuming a debounce of 0.5

    -- Possibly also add the peripheral to the list of changed ones? Depends on what you want to do!
  end
end