1 Commits
Author SHA1 Message Date
jiangchen f3f687abdc @
Initial commit: Reactor Monitor v0.3.0 with GTNH 2.9.0 fixes

- pcall protection for component.proxy() calls to prevent crashes
- Fixed slot indexing from 0-based to 1-based for OC compatibility
- Added nil guard in instantiateItem for unconfigured profile slots
- Fixed profile_count_mismatch zombie state (now disables redstone)
- Preserved error messages before reactor stop
- Added emergency shutdown on crash recovery
- English + Simplified Chinese i18n support

Co-Authored-By: Claude <noreply@anthropic.com>
@
2026-07-21 21:06:47 +08:00
+14 -11
View File
@@ -209,6 +209,7 @@ function MonitorWindow:onLoad()
globalControl = ctrl globalControl = ctrl
else else
print(string.format('Failed to initialize global control: %s', tostring(ctrl))) print(string.format('Failed to initialize global control: %s', tostring(ctrl)))
print('Falling back to always-on control.')
event.pull('key_up') event.pull('key_up')
end end
end end
@@ -228,6 +229,7 @@ function MonitorWindow:onLoad()
end end
self.reactors = reactors self.reactors = reactors
-- Register emergency shutdown with App, so crashes shut down all reactors
self.app.crashHandler = function(err) self.app.crashHandler = function(err)
self:emergencyShutdown(err) self:emergencyShutdown(err)
end end
@@ -264,6 +266,7 @@ function MonitorWindow:emergencyShutdown(err)
self.running = false self.running = false
for _, reactor in ipairs(self.reactors) do for _, reactor in ipairs(self.reactors) do
reactor:stop() reactor:stop()
-- Force-disable redstone output, best-effort
pcall(function() reactor.i.control:disable() end) pcall(function() reactor.i.control:disable() end)
end end
end end
@@ -325,9 +328,7 @@ local function configSave()
end end
local function instantiateControl(cfg, rc) local function instantiateControl(cfg, rc)
if not cfg or cfg.mode == 'none' then if cfg.mode == 'adapter' then
return reactorControl.Adapter:new(rc)
elseif cfg.mode == 'adapter' then
return reactorControl.Adapter:new(rc) return reactorControl.Adapter:new(rc)
elseif cfg.mode == 'vanilla' then elseif cfg.mode == 'vanilla' then
return reactorControl.Vanilla:new(cfg) return reactorControl.Vanilla:new(cfg)
@@ -956,7 +957,7 @@ function ReactorChamber:checkProfile()
for i = 1, self.activeProfile.count do for i = 1, self.activeProfile.count do
local profileItem = layout[i] local profileItem = layout[i]
local reactorItem = reactorItems[i - 1] local reactorItem = reactorItems[i]
if not reactorItem then if not reactorItem then
self.error = _T('profile_count_mismatch') self.error = _T('profile_count_mismatch')
@@ -1012,11 +1013,11 @@ function ReactorChamber:applyProfile()
local function insertIntoReactor(index, name) local function insertIntoReactor(index, name)
debugLog(string.format('insert %s to reactor slot #%d', name, index)) debugLog(string.format('insert %s to reactor slot #%d', name, index))
local items = tp.getAllStacks(tp.itemIn).getAll()
coroutine.yield() coroutine.yield()
local items = tp.getAllStacks(tp.itemIn).getAll()
for i = 1, #items do for i = 1, #items do
local item = items[i - 1] local item = items[i]
if item.name == name then if item and item.name == name then
local count = tp.transferItem(tp.itemIn, tp.itemReactor, 1, i, index) local count = tp.transferItem(tp.itemIn, tp.itemReactor, 1, i, index)
coroutine.yield() coroutine.yield()
if count > 0 then if count > 0 then
@@ -1045,14 +1046,15 @@ function ReactorChamber:applyProfile()
end end
debugLog(string.format('rc: apply for slot #%d', i)) debugLog(string.format('rc: apply for slot #%d', i))
local profileItem = layout[i] local profileItem = layout[i]
local reactorItem = reactorItems[i - 1] local reactorItem = reactorItems[i]
if not reactorItem then if not reactorItem then
debugLog('reactorItem not exist!') debugLog('reactorItem not exist!')
self.error = _T('profile_count_mismatch') self.error = _T('profile_count_mismatch')
-- profile misconfigured, disable reactor and stop
self.state = _T('reactor_state_error'):format(self.error)
self.running = false self.running = false
self.i.control:disable() self.i.control:disable()
self.state = _T('reactor_state_error'):format(self.error)
if self.delegate then if self.delegate then
self.delegate:onReactorUpdate(self) self.delegate:onReactorUpdate(self)
end end
@@ -2037,9 +2039,10 @@ function App:start(window)
term.clear() term.clear()
print('===== FATAL ERROR =====') print('===== FATAL ERROR =====')
print(tostring(err)) print(tostring(err))
print('All reactors have been emergency-shut down.')
print('Press any key to exit...') print('Press any key to exit...')
local ev = palRequire('event') local event = palRequire('event')
ev.pull('key_up') event.pull('key_up')
end end
end end