Inventory methods seem to destroy all the peripheral events in the queue
Vap0r1ze opened this issue · 4 comments
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.
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
-
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. ↩
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?
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