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
+164 -70
View File
@@ -1,10 +1,13 @@
local DEFAULT_SERVER_BASE = "https://ticket.fse-media.group"
local DEFAULT_SERVER_PATH = "/api/tickets/check"
local GATE_OPEN_SECONDS = 2
local VERSION = "v1.5.29"
local VERSION = "v1.5.30"
local VERSION_CHECK_INTERVAL = 5
local PERIPHERAL_WAIT_INTERVAL = 0.5
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
@@ -118,11 +121,22 @@ end
local function waitForPeripherals(requireInspection)
local attempts = 0
local lastDrawMsg = nil
local handshakeDone = false
while attempts < PERIPHERAL_WAIT_MAX_RETRIES do
local hadChange = false
if not monitor then
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
if not speaker then
local s, _ = safeFindPeripheral("speaker")
@@ -135,12 +149,17 @@ local function waitForPeripherals(requireInspection)
local ok = true
if not monitor 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
if not monitor and requireInspection and not inspection then
msg = "Waiting for monitor + gate..."
elseif not monitor then
if not monitor then
msg = "Waiting for monitor..."
elseif not handshakeDone then
msg = "Waking monitor..."
elseif requireInspection and not inspection then
msg = "Waiting for gate peripheral..."
end
@@ -171,7 +190,7 @@ local function waitForPeripherals(requireInspection)
sleep(PERIPHERAL_WAIT_INTERVAL)
attempts = attempts + 1
end
return false
return not not monitor
end
pcall(function() waitForPeripherals(false) end)
@@ -248,33 +267,81 @@ local function jsonDecode(str)
if ok then return res else return nil end
end
local function setServerConnected(ok)
if serverConnected == ok then return end
serverConnected = ok
serverLastChangeTs = os.epoch("utc")
end
local termDev = nativeTerm
if peripheral and type(peripheral.getName) == "function" and monitor then
pcall(function() monitorSide = peripheral.getName(monitor) 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 nextMonitor = nil
local nextMonitorSide = monitorSide
if peripheral then
if nextMonitorSide and type(peripheral.isPresent) == "function" and peripheral.isPresent(nextMonitorSide) and type(peripheral.wrap) == "function" then
local okWrap, wrapped = pcall(peripheral.wrap, nextMonitorSide)
if okWrap and wrapped then
nextMonitor = wrapped
if nextMonitorSide and type(peripheral.isPresent) == "function" then
local okPres, pres = pcall(peripheral.isPresent, nextMonitorSide)
if okPres and pres and type(peripheral.wrap) == "function" then
local okWrap, wrapped = pcall(peripheral.wrap, nextMonitorSide)
if okWrap and wrapped then nextMonitor = wrapped end
end
end
if not nextMonitor and type(peripheral.find) == "function" then
local foundName = nil
local found = peripheral.find("monitor", function(name, wrapped)
foundName = name
nextMonitor = wrapped
return true
local found = nil
pcall(function()
found = peripheral.find("monitor", function(name, wrapped)
foundName = name
return true
end)
end)
if found then
nextMonitor = found
@@ -284,10 +351,18 @@ local function refreshDisplayTarget()
end
end
end
monitor = nextMonitor
monitorSide = nextMonitorSide
if nextMonitor and nextMonitor ~= monitor then
monitor = nextMonitor
monitorSide = nextMonitorSide
pcall(function() handshakeMonitor(nextMonitor) end)
elseif nextMonitor then
monitor = nextMonitor
monitorSide = nextMonitorSide
end
if monitor then
pcall(monitor.setTextScale, 0.5)
if not safeCallMonitor(monitor, "setTextScale", 0.5) then
pcall(function() handshakeMonitor(monitor) end)
end
termDev = monitor
else
termDev = nativeTerm or term
@@ -296,52 +371,48 @@ local function refreshDisplayTarget()
end
local function clear()
refreshDisplayTarget()
termDev.setBackgroundColor(colors.black)
termDev.setTextColor(colors.white)
termDev.clear()
termDev.setCursorPos(1, 1)
pcall(refreshDisplayTarget)
if not termDev then return end
safeCallTerm("setBackgroundColor", colors.black)
safeCallTerm("setTextColor", colors.white)
safeCallTerm("clear")
safeCallTerm("setCursorPos", 1, 1)
end
local function centerText(y, text, color)
local w = termDev.getSize()
termDev.setTextColor(color or colors.white)
local x = math.max(1, math.floor((w - #text) / 2) + 1)
termDev.setCursorPos(x, y)
termDev.write(text)
if not termDev then return end
local w = safeCallTerm("getSize")
w = tonumber(w) or 0
if w < 2 then return end
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
local function splitDisplayLines(text)
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 lastStartupRedrawAt = 0
local function drawServerStatusIndicator(w)
w = tonumber(w) or 0
if w < 2 then return end
local col = colors.yellow
if serverConnected == true then col = colors.lime
elseif serverConnected == false then col = colors.red end
termDev.setBackgroundColor(colors.black)
termDev.setTextColor(col)
termDev.setCursorPos(w - 1, 1)
termDev.write("S")
termDev.setCursorPos(w, 1)
termDev.write("*")
termDev.setTextColor(colors.white)
safeCallTerm("setBackgroundColor", colors.black)
safeCallTerm("setTextColor", col)
safeCallTerm("setCursorPos", w - 1, 1)
safeCallTerm("write", "S")
safeCallTerm("setCursorPos", w, 1)
safeCallTerm("write", "*")
safeCallTerm("setTextColor", colors.white)
end
local lastUpdateError = nil
local function drawVersionIndicator(w)
w = tonumber(w) or 0
local s = tostring(VERSION or "")
if #s == 0 then return end
if w < #s then return end
if #s == 0 or w < #s then return end
local markerColor = colors.lime
local markerText = "* "
if isUpdating then
@@ -351,25 +422,31 @@ local function drawVersionIndicator(w)
markerColor = colors.red
markerText = "* "
end
termDev.setBackgroundColor(colors.black)
termDev.setTextColor(colors.gray)
termDev.setCursorPos(1, 1)
termDev.write(s)
termDev.setTextColor(markerColor)
termDev.write(markerText)
safeCallTerm("setBackgroundColor", colors.black)
safeCallTerm("setTextColor", colors.gray)
safeCallTerm("setCursorPos", 1, 1)
safeCallTerm("write", s)
safeCallTerm("setTextColor", markerColor)
safeCallTerm("write", markerText)
if lastUpdateError then
termDev.setCursorPos(1, 2)
termDev.setTextColor(colors.red)
termDev.write(string.sub(tostring(lastUpdateError), 1, w))
safeCallTerm("setCursorPos", 1, 2)
safeCallTerm("setTextColor", colors.red)
safeCallTerm("write", string.sub(tostring(lastUpdateError), 1, w))
end
termDev.setTextColor(colors.white)
safeCallTerm("setTextColor", colors.white)
end
local _cachedDrawArgHash = nil
local function draw(statusLine1, statusLine2, statusColor)
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)
drawVersionIndicator(w)
drawServerStatusIndicator(w)
@@ -385,9 +462,9 @@ local function draw(statusLine1, statusLine2, statusColor)
centerText(y, line, statusColor or colors.white)
end
end
termDev.setCursorPos(1, h)
termDev.setTextColor(colors.gray)
termDev.write(string.rep(" ", w))
safeCallTerm("setCursorPos", 1, h)
safeCallTerm("setTextColor", colors.gray)
safeCallTerm("write", string.rep(" ", w))
end
local function pulseLeftRedstone(seconds)
@@ -1726,6 +1803,23 @@ local function mainLoop()
drawReadyScreen()
elseif ev[1] == "peripheral" or ev[1] == "peripheral_detach" or ev[1] == "monitor_resize" or ev[1] == "config_updated" then
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
@@ -1797,4 +1891,4 @@ local function backgroundSyncTask()
end
end
parallel.waitForAny(mainLoop, backgroundSyncTask)
parallel.waitForAny(mainLoop, startupRedrawTask, backgroundSyncTask)