fix: split Network-listener into capture/processor to prevent modem_message loss

CC:Tweaked's parallel.waitForAny drops events when a coroutine's
filter doesn't match. The old Network-listener processed commands
inline, causing it to yield with filter 'task_complete' during
peripheral calls (pushItems, list, etc). Any modem_message arriving
during that window was consumed by the scheduler and lost.

Split into two coroutines:
- Network-capture: only ever yields for 'modem_message', inserts
  into a shared networkQueue table, queues 'network_queued' event
- Network-processor: drains the queue and runs all handler logic,
  safe to yield for peripheral calls without losing messages

This fixes the ~80% message loss rate on dispense requests.
This commit is contained in:
MayaTheShy
2026-03-28 23:01:14 -04:00
parent badde91336
commit 36612ecc9f

View File

@@ -383,6 +383,11 @@ local function main()
-- Parallel tasks -- Parallel tasks
----------------------------------------------- -----------------------------------------------
-- Shared queue: capture task writes here, processor task reads.
-- This ensures modem_message events are never lost while the
-- processor yields for peripheral calls (pushItems, list, etc).
local networkQueue = {}
parallel.waitForAny( parallel.waitForAny(
-- Task 1: Background inventory scanner -- Task 1: Background inventory scanner
resilient("Scanner", function() resilient("Scanner", function()
@@ -630,14 +635,36 @@ local function main()
end end
end), end),
-- Task 13: Network order/command listener -- Task 13a: Network message capture (fast — never yields to peripheral calls)
resilient("Network-listener", function() -- This coroutine's filter is ALWAYS "modem_message", so it can never
-- miss events while other tasks yield for "task_complete" etc.
resilient("Network-capture", function()
if not ctx.networkModem then if not ctx.networkModem then
while true do sleep(3600) end while true do sleep(3600) end
end end
while true do while true do
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message") local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
if channel == cfg.ORDER_CHANNEL and type(message) == "table" then if channel == cfg.ORDER_CHANNEL and type(message) == "table" then
table.insert(networkQueue, { replyChannel = replyChannel, message = message })
os.queueEvent("network_queued")
end
end
end),
-- Task 13b: Network message processor (drains queue — safe to yield)
resilient("Network-processor", function()
if not ctx.networkModem then
while true do sleep(3600) end
end
while true do
if #networkQueue == 0 then
os.pullEvent("network_queued")
end
while #networkQueue > 0 do
local entry = table.remove(networkQueue, 1)
local message = entry.message
local replyChannel = entry.replyChannel
if isCommandDuplicate(message.commandId) then if isCommandDuplicate(message.commandId) then
log.debug("NET", "Duplicate command skipped: %s", tostring(message.commandId)) log.debug("NET", "Duplicate command skipped: %s", tostring(message.commandId))
else else
@@ -888,7 +915,7 @@ local function main()
log.error("NET", "Handler error: %s", tostring(handlerErr)) log.error("NET", "Handler error: %s", tostring(handlerErr))
end end
end -- idempotency else end -- idempotency else
end end -- queue loop
end end
end) end)
) )