The Opus package manager deletes the entire package directory on update (fs.delete(packageDir)) before re-downloading files. This wiped all config files (.manager_config, .client_config, etc.) that were stored inside packages/inventory-manager/. Fix: add _configPath() helper to every program that resolves config file paths to usr/config/inventory-manager/ when running under Opus, which lives outside the package directory and survives updates. Falls back to the local _path() for standalone (non-Opus) use. Updated files: - inventoryManager.lua, manager/config.lua, inventoryClient.lua, inventoryWebBridge.lua, dropperController.lua, craftingTurtle.lua - .package install script: saves configs to usr/config/inventory-manager/ - autorun/startup.lua: checks both persistent and package dirs - startup/client.lua: uses persistent dir for .client_setup/.dropper_config - web/server/Dockerfile: switch health check to wget (from prior fix)
204 lines
5.9 KiB
Lua
204 lines
5.9 KiB
Lua
-- startup.lua for Inventory Client computer
|
|
-- Auto-updates from git then launches inventoryClient + dropperController in parallel
|
|
|
|
local REPO_RAW = "https://git.spatulaa.com/MayaTheShy/Inventory-Manager-CC/raw/branch/main"
|
|
|
|
-- Persistent config directory (survives Opus package updates)
|
|
local PERSIST_DIR = "usr/config/inventory-manager"
|
|
if not fs.isDir(PERSIST_DIR) then fs.makeDir(PERSIST_DIR) end
|
|
|
|
local SETUP_FILE = fs.combine(PERSIST_DIR, ".client_setup")
|
|
local DROPPER_CONFIG = fs.combine(PERSIST_DIR, ".dropper_config")
|
|
|
|
-- Files to download (destination -> repo path)
|
|
local FILES = {
|
|
["inventoryClient.lua"] = "inventoryClient.lua",
|
|
["dropperController.lua"] = "dropperController.lua",
|
|
["lib/log.lua"] = "lib/log.lua",
|
|
["lib/ui.lua"] = "lib/ui.lua",
|
|
}
|
|
|
|
-------------------------------------------------
|
|
|
|
local function ensureDir(filePath)
|
|
local dir = filePath:match("^(.+)/")
|
|
if dir and not fs.isDir(dir) then
|
|
fs.makeDir(dir)
|
|
end
|
|
end
|
|
|
|
local function download(remotePath, localPath)
|
|
local url = REPO_RAW .. "/" .. remotePath
|
|
local response = http.get(url)
|
|
if response then
|
|
ensureDir(localPath)
|
|
local f = fs.open(localPath, "w")
|
|
f.write(response.readAll())
|
|
f.close()
|
|
response.close()
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-------------------------------------------------
|
|
|
|
term.clear()
|
|
term.setCursorPos(1, 1)
|
|
print("==================================")
|
|
print(" Inventory Client - Startup")
|
|
print(" Computer ID: " .. os.getComputerID())
|
|
print("==================================")
|
|
print("")
|
|
|
|
-- Update files
|
|
local updated, failed = 0, 0
|
|
for localPath, remotePath in pairs(FILES) do
|
|
write(" " .. localPath .. " ... ")
|
|
if download(remotePath, localPath) then
|
|
print("OK")
|
|
updated = updated + 1
|
|
else
|
|
print("FAIL")
|
|
failed = failed + 1
|
|
end
|
|
end
|
|
|
|
print("")
|
|
if failed > 0 then
|
|
print(string.format("Updated %d files, %d failed.", updated, failed))
|
|
print("Continuing with existing files...")
|
|
else
|
|
print(string.format("All %d files up to date.", updated))
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- First-run setup
|
|
-------------------------------------------------
|
|
|
|
local VALID_SIDES = { "top", "bottom", "left", "right", "front", "back" }
|
|
local function isValidSide(s)
|
|
for _, v in ipairs(VALID_SIDES) do
|
|
if v == s then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function firstRunSetup()
|
|
if fs.exists(SETUP_FILE) then return end
|
|
|
|
print("")
|
|
print("========== First-Run Setup ==========")
|
|
print("")
|
|
write("Is there a dropper next to this computer? (y/n): ")
|
|
local answer = read():lower():sub(1, 1)
|
|
|
|
if answer == "y" then
|
|
-- Ask which side the dropper is on
|
|
local side
|
|
while true do
|
|
print("")
|
|
print("Valid sides: top, bottom, left, right, front, back")
|
|
write("Which side is the dropper on? ")
|
|
side = read():lower():gsub("%s+", "")
|
|
if isValidSide(side) then
|
|
break
|
|
end
|
|
print("Invalid side. Please try again.")
|
|
end
|
|
|
|
-- Ask which side to pulse redstone from (defaults to same side)
|
|
print("")
|
|
write("Redstone output side [" .. side .. "]: ")
|
|
local rsSide = read():lower():gsub("%s+", "")
|
|
if rsSide == "" then rsSide = side end
|
|
if not isValidSide(rsSide) then
|
|
print("Invalid side, defaulting to " .. side)
|
|
rsSide = side
|
|
end
|
|
|
|
-- Save dropper config
|
|
local cfg = textutils.serialiseJSON({
|
|
dropperSide = side,
|
|
redstoneSide = rsSide,
|
|
enabled = true,
|
|
})
|
|
local f = fs.open(DROPPER_CONFIG, "w")
|
|
f.write(cfg)
|
|
f.close()
|
|
print("")
|
|
print("Dropper configured: peripheral=" .. side .. ", redstone=" .. rsSide)
|
|
else
|
|
-- No dropper - save config with enabled=false
|
|
local f = fs.open(DROPPER_CONFIG, "w")
|
|
f.write(textutils.serialiseJSON({ enabled = false }))
|
|
f.close()
|
|
print("No dropper configured.")
|
|
end
|
|
|
|
-- Mark setup as complete
|
|
local f = fs.open(SETUP_FILE, "w")
|
|
f.write("done")
|
|
f.close()
|
|
|
|
print("")
|
|
print("Setup complete!")
|
|
print("(Delete " .. SETUP_FILE .. " to re-run setup)")
|
|
sleep(2)
|
|
end
|
|
|
|
firstRunSetup()
|
|
|
|
-------------------------------------------------
|
|
-- Determine if dropper controller should run
|
|
-------------------------------------------------
|
|
|
|
local dropperEnabled = false
|
|
if fs.exists(DROPPER_CONFIG) then
|
|
local f = fs.open(DROPPER_CONFIG, "r")
|
|
local ok, cfg = pcall(textutils.unserialiseJSON, f.readAll())
|
|
f.close()
|
|
if ok and cfg and cfg.enabled then
|
|
dropperEnabled = true
|
|
end
|
|
end
|
|
|
|
print("")
|
|
if dropperEnabled then
|
|
print("Starting inventoryClient + dropperController...")
|
|
else
|
|
print("Starting inventoryClient (no dropper)...")
|
|
end
|
|
sleep(1)
|
|
|
|
-- Reboot listener: reboots this computer on remote command
|
|
local SYSTEM_CHANNEL = 4205
|
|
local ROLE = "client"
|
|
|
|
local function rebootListener()
|
|
local m = peripheral.find("modem")
|
|
if not m then return end
|
|
m.open(SYSTEM_CHANNEL)
|
|
while true do
|
|
local _, _, channel, _, message = os.pullEvent("modem_message")
|
|
if channel == SYSTEM_CHANNEL and type(message) == "table" and message.type == "reboot" then
|
|
local target = message.target or "all"
|
|
if target == "all" or target == ROLE or target == tostring(os.getComputerID()) then
|
|
print("[SYSTEM] Reboot command received. Rebooting...")
|
|
sleep(0.5)
|
|
os.reboot()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local tasks = {
|
|
function() shell.run("inventoryClient.lua") end,
|
|
rebootListener,
|
|
}
|
|
if dropperEnabled then
|
|
table.insert(tasks, function() shell.run("dropperController.lua") end)
|
|
end
|
|
|
|
parallel.waitForAny(table.unpack(tasks))
|