fix(gate): 修复区块加载时检票机屏幕无显示问题 - 新增 monitor 握手阶段,所有 termDev 渲染调用加 pcall 保护,启动后 20 秒持续重绘热修复,修复 setServerConnected 重复定义丢失 queueEvent 的 bug (v1.5.30)

This commit is contained in:
2026-08-11 17:25:31 +08:00
parent e76d0188db
commit e18f732b38
+162 -68
View File
@@ -1,10 +1,13 @@
local DEFAULT_SERVER_BASE = "https://ticket.fse-media.group" local DEFAULT_SERVER_BASE = "https://ticket.fse-media.group"
local DEFAULT_SERVER_PATH = "/api/tickets/check" local DEFAULT_SERVER_PATH = "/api/tickets/check"
local GATE_OPEN_SECONDS = 2 local GATE_OPEN_SECONDS = 2
local VERSION = "v1.5.29" local VERSION = "v1.5.30"
local VERSION_CHECK_INTERVAL = 5 local VERSION_CHECK_INTERVAL = 5
local PERIPHERAL_WAIT_INTERVAL = 0.5 local PERIPHERAL_WAIT_INTERVAL = 0.5
local PERIPHERAL_WAIT_MAX_RETRIES = 40 local PERIPHERAL_WAIT_MAX_RETRIES = 40
local MONITOR_HANDSHAKE_RETRIES = 20
local STARTUP_REDRAW_DURATION = 20
local STARTUP_REDRAW_INTERVAL = 0.5
local serverURL = nil local serverURL = nil
@@ -118,11 +121,22 @@ end
local function waitForPeripherals(requireInspection) local function waitForPeripherals(requireInspection)
local attempts = 0 local attempts = 0
local lastDrawMsg = nil local lastDrawMsg = nil
local handshakeDone = false
while attempts < PERIPHERAL_WAIT_MAX_RETRIES do while attempts < PERIPHERAL_WAIT_MAX_RETRIES do
local hadChange = false local hadChange = false
if not monitor then if not monitor then
local m, ms = safeFindPeripheral("monitor", monitorSide) local m, ms = safeFindPeripheral("monitor", monitorSide)
if m then monitor = m; monitorSide = ms; hadChange = true end if m then
monitor = m
monitorSide = ms
hadChange = true
end
end
if monitor and (hadChange or not handshakeDone) then
termDev = monitor
local ok = false
pcall(function() ok = handshakeMonitor(monitor) end)
if ok then handshakeDone = true end
end end
if not speaker then if not speaker then
local s, _ = safeFindPeripheral("speaker") local s, _ = safeFindPeripheral("speaker")
@@ -135,12 +149,17 @@ local function waitForPeripherals(requireInspection)
local ok = true local ok = true
if not monitor then ok = false end if not monitor then ok = false end
if requireInspection and not inspection then ok = false end if requireInspection and not inspection then ok = false end
if ok then return true end if ok and handshakeDone then return true end
if ok and not handshakeDone then
local ok2 = false
pcall(function() ok2 = handshakeMonitor(monitor) end)
if ok2 then handshakeDone = true; return true end
end
local msg = nil local msg = nil
if not monitor and requireInspection and not inspection then if not monitor then
msg = "Waiting for monitor + gate..."
elseif not monitor then
msg = "Waiting for monitor..." msg = "Waiting for monitor..."
elseif not handshakeDone then
msg = "Waking monitor..."
elseif requireInspection and not inspection then elseif requireInspection and not inspection then
msg = "Waiting for gate peripheral..." msg = "Waiting for gate peripheral..."
end end
@@ -171,7 +190,7 @@ local function waitForPeripherals(requireInspection)
sleep(PERIPHERAL_WAIT_INTERVAL) sleep(PERIPHERAL_WAIT_INTERVAL)
attempts = attempts + 1 attempts = attempts + 1
end end
return false return not not monitor
end end
pcall(function() waitForPeripherals(false) end) pcall(function() waitForPeripherals(false) end)
@@ -248,33 +267,81 @@ local function jsonDecode(str)
if ok then return res else return nil end if ok then return res else return nil end
end end
local function setServerConnected(ok)
if serverConnected == ok then return end
serverConnected = ok
serverLastChangeTs = os.epoch("utc")
end
local termDev = nativeTerm local termDev = nativeTerm
if peripheral and type(peripheral.getName) == "function" and monitor then if peripheral and type(peripheral.getName) == "function" and monitor then
pcall(function() monitorSide = peripheral.getName(monitor) end) pcall(function() monitorSide = peripheral.getName(monitor) end)
end end
local function safeCallTerm(method, ...)
if not termDev or type(termDev[method]) ~= "function" then return nil end
local ok, r1, r2, r3 = pcall(termDev[method], ...)
if not ok then return nil end
return r1, r2, r3
end
local function safeCallMonitor(target, method, ...)
if not target or type(target[method]) ~= "function" then return false end
local ok, r1 = pcall(target[method], ...)
if not ok then return false end
return r1 ~= false
end
local function handshakeMonitor(target)
if not target then return false end
local scaleOk = false
local getSizeOk = false
local w, h = nil, nil
for _ = 1, MONITOR_HANDSHAKE_RETRIES do
if safeCallMonitor(target, "setTextScale", 0.5) then scaleOk = true end
w, h = safeCallTerm("getSize")
if type(w) == "number" and type(h) == "number" and w >= 2 and h >= 2 then
getSizeOk = true
end
if scaleOk and getSizeOk then
local cOk = safeCallMonitor(target, "setBackgroundColor", colors.black)
local c2Ok = safeCallMonitor(target, "setTextColor", colors.white)
local clearOk = safeCallMonitor(target, "clear")
if clearOk then return true end
end
sleep(PERIPHERAL_WAIT_INTERVAL)
local rewrapOk, rewrapped = pcall(function()
if monitorSide and peripheral and type(peripheral.isPresent) == "function" and peripheral.isPresent(monitorSide) and type(peripheral.wrap) == "function" then
local okW, wd = pcall(peripheral.wrap, monitorSide)
if okW and wd then return wd end
end
if peripheral and type(peripheral.find) == "function" then
return peripheral.find("monitor")
end
return nil
end)
if rewrapOk and rewrapped then
target = rewrapped
monitor = rewrapped
termDev = rewrapped
end
end
return scaleOk or getSizeOk
end
local function refreshDisplayTarget() local function refreshDisplayTarget()
local nextMonitor = nil local nextMonitor = nil
local nextMonitorSide = monitorSide local nextMonitorSide = monitorSide
if peripheral then if peripheral then
if nextMonitorSide and type(peripheral.isPresent) == "function" and peripheral.isPresent(nextMonitorSide) and type(peripheral.wrap) == "function" then if nextMonitorSide and type(peripheral.isPresent) == "function" then
local okWrap, wrapped = pcall(peripheral.wrap, nextMonitorSide) local okPres, pres = pcall(peripheral.isPresent, nextMonitorSide)
if okWrap and wrapped then if okPres and pres and type(peripheral.wrap) == "function" then
nextMonitor = wrapped local okWrap, wrapped = pcall(peripheral.wrap, nextMonitorSide)
if okWrap and wrapped then nextMonitor = wrapped end
end end
end end
if not nextMonitor and type(peripheral.find) == "function" then if not nextMonitor and type(peripheral.find) == "function" then
local foundName = nil local foundName = nil
local found = peripheral.find("monitor", function(name, wrapped) local found = nil
foundName = name pcall(function()
nextMonitor = wrapped found = peripheral.find("monitor", function(name, wrapped)
return true foundName = name
return true
end)
end) end)
if found then if found then
nextMonitor = found nextMonitor = found
@@ -284,10 +351,18 @@ local function refreshDisplayTarget()
end end
end end
end end
monitor = nextMonitor if nextMonitor and nextMonitor ~= monitor then
monitorSide = nextMonitorSide monitor = nextMonitor
monitorSide = nextMonitorSide
pcall(function() handshakeMonitor(nextMonitor) end)
elseif nextMonitor then
monitor = nextMonitor
monitorSide = nextMonitorSide
end
if monitor then if monitor then
pcall(monitor.setTextScale, 0.5) if not safeCallMonitor(monitor, "setTextScale", 0.5) then
pcall(function() handshakeMonitor(monitor) end)
end
termDev = monitor termDev = monitor
else else
termDev = nativeTerm or term termDev = nativeTerm or term
@@ -296,52 +371,48 @@ local function refreshDisplayTarget()
end end
local function clear() local function clear()
refreshDisplayTarget() pcall(refreshDisplayTarget)
termDev.setBackgroundColor(colors.black) if not termDev then return end
termDev.setTextColor(colors.white) safeCallTerm("setBackgroundColor", colors.black)
termDev.clear() safeCallTerm("setTextColor", colors.white)
termDev.setCursorPos(1, 1) safeCallTerm("clear")
safeCallTerm("setCursorPos", 1, 1)
end end
local function centerText(y, text, color) local function centerText(y, text, color)
local w = termDev.getSize() if not termDev then return end
termDev.setTextColor(color or colors.white) local w = safeCallTerm("getSize")
local x = math.max(1, math.floor((w - #text) / 2) + 1) w = tonumber(w) or 0
termDev.setCursorPos(x, y) if w < 2 then return end
termDev.write(text) safeCallTerm("setTextColor", color or colors.white)
local x = math.max(1, math.floor((w - #tostring(text or "")) / 2) + 1)
safeCallTerm("setCursorPos", x, y)
safeCallTerm("write", tostring(text or ""))
end end
local function splitDisplayLines(text) local lastStartupRedrawAt = 0
local out = {}
text = tostring(text or "")
if #text == 0 then return out end
for line in text:gmatch("([^\n]*)\n?") do
if line == "" and #out > 0 and text:sub(-1) ~= "\n" then break end
if #line > 0 then table.insert(out, line) end
end
return out
end
local function drawServerStatusIndicator(w) local function drawServerStatusIndicator(w)
w = tonumber(w) or 0
if w < 2 then return end if w < 2 then return end
local col = colors.yellow local col = colors.yellow
if serverConnected == true then col = colors.lime if serverConnected == true then col = colors.lime
elseif serverConnected == false then col = colors.red end elseif serverConnected == false then col = colors.red end
termDev.setBackgroundColor(colors.black) safeCallTerm("setBackgroundColor", colors.black)
termDev.setTextColor(col) safeCallTerm("setTextColor", col)
termDev.setCursorPos(w - 1, 1) safeCallTerm("setCursorPos", w - 1, 1)
termDev.write("S") safeCallTerm("write", "S")
termDev.setCursorPos(w, 1) safeCallTerm("setCursorPos", w, 1)
termDev.write("*") safeCallTerm("write", "*")
termDev.setTextColor(colors.white) safeCallTerm("setTextColor", colors.white)
end end
local lastUpdateError = nil local lastUpdateError = nil
local function drawVersionIndicator(w) local function drawVersionIndicator(w)
w = tonumber(w) or 0
local s = tostring(VERSION or "") local s = tostring(VERSION or "")
if #s == 0 then return end if #s == 0 or w < #s then return end
if w < #s then return end
local markerColor = colors.lime local markerColor = colors.lime
local markerText = "* " local markerText = "* "
if isUpdating then if isUpdating then
@@ -351,25 +422,31 @@ local function drawVersionIndicator(w)
markerColor = colors.red markerColor = colors.red
markerText = "* " markerText = "* "
end end
termDev.setBackgroundColor(colors.black) safeCallTerm("setBackgroundColor", colors.black)
termDev.setTextColor(colors.gray) safeCallTerm("setTextColor", colors.gray)
termDev.setCursorPos(1, 1) safeCallTerm("setCursorPos", 1, 1)
termDev.write(s) safeCallTerm("write", s)
termDev.setTextColor(markerColor) safeCallTerm("setTextColor", markerColor)
termDev.write(markerText) safeCallTerm("write", markerText)
if lastUpdateError then if lastUpdateError then
termDev.setCursorPos(1, 2) safeCallTerm("setCursorPos", 1, 2)
termDev.setTextColor(colors.red) safeCallTerm("setTextColor", colors.red)
termDev.write(string.sub(tostring(lastUpdateError), 1, w)) safeCallTerm("write", string.sub(tostring(lastUpdateError), 1, w))
end end
termDev.setTextColor(colors.white) safeCallTerm("setTextColor", colors.white)
end end
local _cachedDrawArgHash = nil
local function draw(statusLine1, statusLine2, statusColor) local function draw(statusLine1, statusLine2, statusColor)
clear() clear()
local w, h = termDev.getSize() if not termDev then return end
local w, h = safeCallTerm("getSize")
w = tonumber(w) or 0
h = tonumber(h) or 0
if w < 2 or h < 2 then return end
centerText(2, "GATE", colors.cyan) centerText(2, "GATE", colors.cyan)
drawVersionIndicator(w) drawVersionIndicator(w)
drawServerStatusIndicator(w) drawServerStatusIndicator(w)
@@ -385,9 +462,9 @@ local function draw(statusLine1, statusLine2, statusColor)
centerText(y, line, statusColor or colors.white) centerText(y, line, statusColor or colors.white)
end end
end end
termDev.setCursorPos(1, h) safeCallTerm("setCursorPos", 1, h)
termDev.setTextColor(colors.gray) safeCallTerm("setTextColor", colors.gray)
termDev.write(string.rep(" ", w)) safeCallTerm("write", string.rep(" ", w))
end end
local function pulseLeftRedstone(seconds) local function pulseLeftRedstone(seconds)
@@ -1726,6 +1803,23 @@ local function mainLoop()
drawReadyScreen() drawReadyScreen()
elseif ev[1] == "peripheral" or ev[1] == "peripheral_detach" or ev[1] == "monitor_resize" or ev[1] == "config_updated" then elseif ev[1] == "peripheral" or ev[1] == "peripheral_detach" or ev[1] == "monitor_resize" or ev[1] == "config_updated" then
pcall(drawReadyScreen) pcall(drawReadyScreen)
elseif ev[1] == "timer" then
pcall(drawReadyScreen)
end
end
end
local function startupRedrawTask()
local startTs = (os.epoch and os.epoch("utc")) or (os.time() * 3600000)
local endTs = startTs + STARTUP_REDRAW_DURATION * 1000
local nextRedraw = startTs
while true do
local now = (os.epoch and os.epoch("utc")) or (os.time() * 3600000)
if now <= endTs then
pcall(drawReadyScreen)
sleep(STARTUP_REDRAW_INTERVAL)
else
sleep(60)
end end
end end
end end
@@ -1797,4 +1891,4 @@ local function backgroundSyncTask()
end end
end end
parallel.waitForAny(mainLoop, backgroundSyncTask) parallel.waitForAny(mainLoop, startupRedrawTask, backgroundSyncTask)