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
-----------------------------------------------
-- 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(
-- Task 1: Background inventory scanner
resilient("Scanner", function()
@@ -630,14 +635,36 @@ local function main()
end
end),
-- Task 13: Network order/command listener
resilient("Network-listener", function()
-- Task 13a: Network message capture (fast — never yields to peripheral calls)
-- 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
while true do sleep(3600) end
end
while true do
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
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
log.debug("NET", "Duplicate command skipped: %s", tostring(message.commandId))
else
@@ -888,7 +915,7 @@ local function main()
log.error("NET", "Handler error: %s", tostring(handlerErr))
end
end -- idempotency else
end
end -- queue loop
end
end)
)