228 lines
9.0 KiB
Lua
228 lines
9.0 KiB
Lua
-- manager/config.lua — Configuration constants, data tables, and lookup structures
|
|
-- Usage: local cfg = dofile(_path("manager/config.lua"))(log, _path)
|
|
|
|
return function(log, _path)
|
|
|
|
-- Fall back to CWD-relative if _path not provided (standalone use)
|
|
if not _path then _path = function(p) return p end end
|
|
|
|
-- Persistent config path: survives Opus package updates
|
|
local _PERSIST_DIR = "usr/config/inventory-manager"
|
|
local function _configPath(rel)
|
|
if fs.isDir(_PERSIST_DIR) or fs.isDir("packages/inventory-manager") then
|
|
if not fs.isDir(_PERSIST_DIR) then fs.makeDir(_PERSIST_DIR) end
|
|
return fs.combine(_PERSIST_DIR, rel)
|
|
end
|
|
return _path(rel)
|
|
end
|
|
|
|
local C = {}
|
|
|
|
-------------------------------------------------
|
|
-- Default configuration (overridden by .manager_config)
|
|
-------------------------------------------------
|
|
|
|
C.DROPPER_NAME = "minecraft:dropper_9"
|
|
C.BARREL_NAME = "minecraft:barrel_0"
|
|
C.POLL_INTERVAL = 2
|
|
C.MONITOR_SIDE = "left"
|
|
C.SCAN_INTERVAL = 120
|
|
C.SMELT_INTERVAL = 3
|
|
C.SMELT_RESERVE = 128
|
|
C.DEFRAG_INTERVAL = 600
|
|
C.COMPOST_INTERVAL = 3
|
|
C.ALERT_INTERVAL = 15
|
|
C.CACHE_FILE = _configPath(".inventory_cache")
|
|
C.SMELTER_MONITOR_SIDE = "top"
|
|
C.DISABLED_RECIPES_FILE = _configPath(".disabled_recipes")
|
|
|
|
-- Network
|
|
C.BROADCAST_CHANNEL = 4200
|
|
C.ORDER_CHANNEL = 4201
|
|
C.BROADCAST_INTERVAL = 1
|
|
C.CRAFT_CHANNEL = 4203
|
|
C.CRAFT_REPLY_CHANNEL = 4204
|
|
C.SYSTEM_CHANNEL = 4205
|
|
|
|
-- Crafting
|
|
C.CRAFT_TIMEOUT = 15
|
|
C.GRID_TO_SLOT = {1, 2, 3, 5, 6, 7, 9, 10, 11}
|
|
|
|
-- Compost (overridable via config file)
|
|
C.COMPOST_RESERVE = 128
|
|
C.COMPOST_DROPPER = "minecraft:dropper_10"
|
|
C.COMPOST_HOPPER = "minecraft:hopper_0"
|
|
|
|
-- Collection hoppers: periodically emptied into storage (e.g. egg spawner, mob farm)
|
|
C.COLLECTION_HOPPERS = { "minecraft:hopper_1" }
|
|
C.COLLECTION_INTERVAL = 3 -- seconds between hopper collection
|
|
|
|
-- Stock limits / auto-discard (overridable via config file)
|
|
C.TRASH_DROPPERS = { -- droppers facing lava/void for destroying excess items
|
|
"minecraft:dropper_11",
|
|
"minecraft:dropper_12",
|
|
"minecraft:dropper_13",
|
|
"minecraft:dropper_14",
|
|
"minecraft:dropper_15",
|
|
"minecraft:dropper_16",
|
|
}
|
|
C.DISCARD_INTERVAL = 5 -- seconds between discard checks
|
|
|
|
-- Auto-craft (overridable via config file)
|
|
C.AUTO_CRAFT_INTERVAL = 10 -- seconds between auto-craft checks
|
|
C.AUTO_CRAFT_OUTPUT_CAP = 512 -- max items of any output before smart-craft stops crafting it
|
|
C.AUTO_CRAFT_FROM_EXCESS = true -- auto-discover recipes for over-stocked items
|
|
|
|
-- Peripheral
|
|
C.PERIPHERAL_CACHE_TTL = 5
|
|
|
|
-- Parallel scanning
|
|
C.PARALLEL_SCAN_CHUNKS = 8
|
|
|
|
-- Storage priority (higher = preferred; keyed by chest peripheral name)
|
|
C.CHEST_PRIORITY = {}
|
|
|
|
-- Builder / supply manifest
|
|
C.SUPPLY_CHEST = "" -- peripheral name of supply chest (empty = disabled)
|
|
C.SUPPLY_INTERVAL = 10 -- seconds between supply checks
|
|
C.SUPPLY_MANIFEST = {} -- { { name = "mod:item", count = N }, ... }
|
|
|
|
-- Furnace types
|
|
C.FURNACE_TYPES = {
|
|
"minecraft:furnace",
|
|
"minecraft:smoker",
|
|
"minecraft:blast_furnace",
|
|
}
|
|
C.SLOT_INPUT = 1
|
|
C.SLOT_FUEL = 2
|
|
C.SLOT_OUTPUT = 3
|
|
|
|
-------------------------------------------------
|
|
-- Config file loader
|
|
-------------------------------------------------
|
|
|
|
local CONFIG_FILE = _configPath(".manager_config")
|
|
|
|
function C.loadConfig()
|
|
if not fs.exists(CONFIG_FILE) then return end
|
|
local f = fs.open(CONFIG_FILE, "r")
|
|
local data = f.readAll()
|
|
f.close()
|
|
local ok, cfg = pcall(textutils.unserialiseJSON, data)
|
|
if not ok or not cfg then
|
|
log.warn("CONFIG", "Failed to parse %s", CONFIG_FILE)
|
|
return
|
|
end
|
|
C._raw = cfg
|
|
if cfg.dropperName then C.DROPPER_NAME = cfg.dropperName end
|
|
if cfg.barrelName then C.BARREL_NAME = cfg.barrelName end
|
|
if cfg.monitorSide then C.MONITOR_SIDE = cfg.monitorSide end
|
|
if cfg.smelterMonitorSide then C.SMELTER_MONITOR_SIDE = cfg.smelterMonitorSide end
|
|
if cfg.pollInterval then C.POLL_INTERVAL = cfg.pollInterval end
|
|
if cfg.scanInterval then C.SCAN_INTERVAL = cfg.scanInterval end
|
|
if cfg.smeltInterval then C.SMELT_INTERVAL = cfg.smeltInterval end
|
|
if cfg.defragInterval then C.DEFRAG_INTERVAL = cfg.defragInterval end
|
|
if cfg.compostInterval then C.COMPOST_INTERVAL = cfg.compostInterval end
|
|
if cfg.alertInterval then C.ALERT_INTERVAL = cfg.alertInterval end
|
|
if cfg.broadcastInterval then C.BROADCAST_INTERVAL = cfg.broadcastInterval end
|
|
if cfg.smeltReserve then C.SMELT_RESERVE = cfg.smeltReserve end
|
|
if cfg.broadcastChannel then C.BROADCAST_CHANNEL = cfg.broadcastChannel end
|
|
if cfg.orderChannel then C.ORDER_CHANNEL = cfg.orderChannel end
|
|
if cfg.craftChannel then C.CRAFT_CHANNEL = cfg.craftChannel end
|
|
if cfg.craftReplyChannel then C.CRAFT_REPLY_CHANNEL = cfg.craftReplyChannel end
|
|
if cfg.compostReserve then C.COMPOST_RESERVE = cfg.compostReserve end
|
|
if cfg.compostDropper then C.COMPOST_DROPPER = cfg.compostDropper end
|
|
if cfg.compostHopper then C.COMPOST_HOPPER = cfg.compostHopper end
|
|
if cfg.collectionHoppers then C.COLLECTION_HOPPERS = cfg.collectionHoppers end
|
|
if cfg.collectionInterval then C.COLLECTION_INTERVAL = cfg.collectionInterval end
|
|
if cfg.trashDroppers then C.TRASH_DROPPERS = cfg.trashDroppers end
|
|
if cfg.discardInterval then C.DISCARD_INTERVAL = cfg.discardInterval end
|
|
if cfg.autoCraftInterval then C.AUTO_CRAFT_INTERVAL = cfg.autoCraftInterval end
|
|
if cfg.autoCraftOutputCap then C.AUTO_CRAFT_OUTPUT_CAP = cfg.autoCraftOutputCap end
|
|
if cfg.autoCraftFromExcess ~= nil then C.AUTO_CRAFT_FROM_EXCESS = cfg.autoCraftFromExcess end
|
|
if cfg.stockLimits then
|
|
for item, limit in pairs(cfg.stockLimits) do
|
|
C.STOCK_LIMITS[item] = limit -- merge / override per-item
|
|
end
|
|
end
|
|
if cfg.parallelScanChunks then C.PARALLEL_SCAN_CHUNKS = cfg.parallelScanChunks end
|
|
if cfg.chestPriority then C.CHEST_PRIORITY = cfg.chestPriority end
|
|
if cfg.supplyChest then C.SUPPLY_CHEST = cfg.supplyChest end
|
|
if cfg.supplyInterval then C.SUPPLY_INTERVAL = cfg.supplyInterval end
|
|
if cfg.supplyManifest then C.SUPPLY_MANIFEST = cfg.supplyManifest end
|
|
if cfg.logLevel then log.setLevel(cfg.logLevel) end
|
|
log.info("CONFIG", "Loaded from %s", CONFIG_FILE)
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Data tables
|
|
-------------------------------------------------
|
|
|
|
C.FUEL_LIST = dofile(_path("data/fuel.lua"))
|
|
local _compostData = dofile(_path("data/compostable.lua"))
|
|
C.COMPOSTABLE = _compostData.items
|
|
C.COMPOST_TRASH = _compostData.trash
|
|
C.STOCK_LIMITS = dofile(_path("data/stock_limits.lua"))
|
|
C.AUTO_CRAFT_RULES = dofile(_path("data/auto_craft.lua"))
|
|
C.LOW_STOCK_ALERTS = dofile(_path("data/alerts.lua"))
|
|
|
|
-- Recipe book: merges built-in recipes + user-learned recipes
|
|
local recipeBook = dofile(_path("lib/recipeBook.lua"))
|
|
recipeBook.init(_configPath(".recipes.db"))
|
|
recipeBook.loadLegacyCrafting(dofile(_path("data/craftable.lua")))
|
|
recipeBook.loadLegacySmelting(dofile(_path("data/smeltable.lua")))
|
|
C.recipeBook = recipeBook
|
|
C.SMELTABLE = recipeBook.getSmeltingTable()
|
|
C.CRAFTABLE = recipeBook.getCraftingList()
|
|
|
|
-- Rebuild furnace/smelt indices from current recipe data
|
|
function C.rebuildIndices()
|
|
for _, recipe in pairs(C.SMELTABLE) do
|
|
recipe.furnaceSet = {}
|
|
for _, ft in ipairs(recipe.furnaces) do
|
|
recipe.furnaceSet[ft] = true
|
|
end
|
|
end
|
|
C.smeltCandidatesByType = {}
|
|
for _, ftype in ipairs(C.FURNACE_TYPES) do
|
|
C.smeltCandidatesByType[ftype] = {}
|
|
end
|
|
for itemName, recipe in pairs(C.SMELTABLE) do
|
|
local isFood = recipe.furnaceSet["minecraft:smoker"] or false
|
|
for ft in pairs(recipe.furnaceSet) do
|
|
table.insert(C.smeltCandidatesByType[ft], { name = itemName, recipe = recipe, food = isFood })
|
|
end
|
|
end
|
|
for _, list in pairs(C.smeltCandidatesByType) do
|
|
table.sort(list, function(a, b)
|
|
if a.food ~= b.food then return a.food end
|
|
return a.name < b.name
|
|
end)
|
|
end
|
|
end
|
|
|
|
-- Refresh recipes from recipeBook and rebuild all indices
|
|
function C.refreshRecipes()
|
|
C.SMELTABLE = C.recipeBook.getSmeltingTable()
|
|
C.CRAFTABLE = C.recipeBook.getCraftingList()
|
|
C.rebuildIndices()
|
|
end
|
|
|
|
C.rebuildIndices()
|
|
|
|
-- Build fuel set for quick lookup
|
|
C.FUEL_SET = {}
|
|
for _, f in ipairs(C.FUEL_LIST) do C.FUEL_SET[f.name] = true end
|
|
|
|
-- Build compostable set for quick lookup
|
|
C.COMPOSTABLE_SET = {}
|
|
for _, name in ipairs(C.COMPOSTABLE) do C.COMPOSTABLE_SET[name] = true end
|
|
|
|
-- Build stock limits set for quick lookup
|
|
C.STOCK_LIMITS_SET = {}
|
|
for name, _ in pairs(C.STOCK_LIMITS) do C.STOCK_LIMITS_SET[name] = true end
|
|
|
|
return C
|
|
|
|
end
|