New miningTurtle.lua: sits on top of a cobble gen, continuously digs down, and pushes items into networked chests via wired modem. Features: - Configurable mine interval, dump threshold, fuel slot - Auto-dumps inventory when slots fill or timer expires - Auto-refuels from designated fuel slot - Remote reboot via SYSTEM_CHANNEL - Stats display (mined, dumped, uptime, fuel) - Persistent config in usr/config/inventory-manager/.miner_config Also adds: - startup/miner.lua: auto-update + launch script for standalone use - .package: new 'Mining Turtle' role (option 4) in setup wizard - autorun/startup.lua: detects .miner_config and launches miningTurtle
298 lines
9.1 KiB
Lua
298 lines
9.1 KiB
Lua
-- Mining Turtle Script
|
|
-- Sits on top of an infinite cobblestone generator.
|
|
-- Continuously mines the block below and pushes items into
|
|
-- networked storage via wired modem.
|
|
-- Requires a wired modem attached to the turtle.
|
|
|
|
local shell = _ENV.shell
|
|
|
|
local function fatal(msg)
|
|
printError(msg)
|
|
print("\nPress any key to exit...")
|
|
os.pullEvent("key")
|
|
return
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Default configuration (overridden by .miner_config)
|
|
-------------------------------------------------
|
|
|
|
local MINE_INTERVAL = 0.5 -- seconds between dig attempts
|
|
local DUMP_THRESHOLD = 14 -- dump when this many slots are occupied
|
|
local DUMP_INTERVAL = 30 -- force dump every N seconds even if not full
|
|
local FUEL_SLOT = 16 -- reserve this slot for fuel (0 = disabled)
|
|
local SYSTEM_CHANNEL = 4205 -- remote reboot channel
|
|
|
|
-------------------------------------------------
|
|
-- Load config from file if present
|
|
-------------------------------------------------
|
|
|
|
local _baseDir = fs.getDir(shell.getRunningProgram())
|
|
local function _path(rel) return fs.combine(_baseDir, rel) 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 CONFIG_FILE = _configPath(".miner_config")
|
|
|
|
local function 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
|
|
print("[WARN] Failed to parse " .. CONFIG_FILE)
|
|
return
|
|
end
|
|
if cfg.mineInterval then MINE_INTERVAL = cfg.mineInterval end
|
|
if cfg.dumpThreshold then DUMP_THRESHOLD = cfg.dumpThreshold end
|
|
if cfg.dumpInterval then DUMP_INTERVAL = cfg.dumpInterval end
|
|
if cfg.fuelSlot then FUEL_SLOT = cfg.fuelSlot end
|
|
print("[CONFIG] Loaded from " .. CONFIG_FILE)
|
|
end
|
|
|
|
loadConfig()
|
|
|
|
-------------------------------------------------
|
|
-- Setup
|
|
-------------------------------------------------
|
|
|
|
print("=================================")
|
|
print(" Mining Turtle (Cobble Miner)")
|
|
print("=================================")
|
|
print("")
|
|
|
|
if not turtle then
|
|
return fatal("[ERR] Not a turtle!")
|
|
end
|
|
|
|
-- Find wired modem and get our network name
|
|
local modem = nil
|
|
local modemSide = nil
|
|
local selfName = nil
|
|
|
|
for _, side in ipairs({"top", "bottom", "left", "right", "front", "back"}) do
|
|
if peripheral.getType(side) == "modem" then
|
|
local m = peripheral.wrap(side)
|
|
if m.getNameLocal then
|
|
local name = m.getNameLocal()
|
|
if name then
|
|
modem = m
|
|
modemSide = side
|
|
selfName = name
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if not modem or not selfName then
|
|
return fatal("[ERR] No wired modem found!\n Attach a wired modem to the turtle\n and connect it to the network.")
|
|
end
|
|
|
|
-- Wrap our own inventory peripheral for pushItems
|
|
local selfInv = peripheral.wrap(selfName)
|
|
if not selfInv then
|
|
return fatal("[ERR] Cannot wrap own peripheral: " .. selfName .. "\n Make sure the wired modem is connected.")
|
|
end
|
|
|
|
print("[OK] Modem: " .. modemSide)
|
|
print("[OK] Network name: " .. selfName)
|
|
print("[OK] Mine interval: " .. MINE_INTERVAL .. "s")
|
|
print("[OK] Dump threshold: " .. DUMP_THRESHOLD .. " slots")
|
|
print("")
|
|
|
|
-------------------------------------------------
|
|
-- Find chests on the network to dump into
|
|
-------------------------------------------------
|
|
|
|
local function getChests()
|
|
local chests = {}
|
|
for _, name in ipairs(peripheral.getNames()) do
|
|
local ptype = peripheral.getType(name)
|
|
if ptype == "minecraft:chest" or ptype == "minecraft:barrel" or
|
|
ptype == "minecraft:shulker_box" then
|
|
table.insert(chests, name)
|
|
end
|
|
end
|
|
return chests
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Dump inventory into networked storage
|
|
-------------------------------------------------
|
|
|
|
local function dumpInventory()
|
|
local chests = getChests()
|
|
if #chests == 0 then
|
|
print("[DUMP] No chests on network!")
|
|
return 0
|
|
end
|
|
|
|
local totalPushed = 0
|
|
for slot = 1, 16 do
|
|
if slot ~= FUEL_SLOT and turtle.getItemCount(slot) > 0 then
|
|
for _, chestName in ipairs(chests) do
|
|
local ok, n = pcall(selfInv.pushItems, chestName, slot)
|
|
if ok and n and n > 0 then
|
|
totalPushed = totalPushed + n
|
|
if turtle.getItemCount(slot) == 0 then
|
|
break -- slot empty, move to next
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return totalPushed
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Count occupied inventory slots
|
|
-------------------------------------------------
|
|
|
|
local function occupiedSlots()
|
|
local count = 0
|
|
for slot = 1, 16 do
|
|
if slot ~= FUEL_SLOT and turtle.getItemCount(slot) > 0 then
|
|
count = count + 1
|
|
end
|
|
end
|
|
return count
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Auto-refuel from fuel slot
|
|
-------------------------------------------------
|
|
|
|
local function autoRefuel()
|
|
if FUEL_SLOT <= 0 then return end
|
|
if turtle.getFuelLevel() == "unlimited" then return end
|
|
if turtle.getFuelLevel() > 100 then return end
|
|
local prev = turtle.getSelectedSlot()
|
|
turtle.select(FUEL_SLOT)
|
|
if turtle.getItemCount() > 0 then
|
|
turtle.refuel(1)
|
|
print("[FUEL] Refueled. Level: " .. turtle.getFuelLevel())
|
|
end
|
|
turtle.select(prev)
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Stats
|
|
-------------------------------------------------
|
|
|
|
local totalMined = 0
|
|
local totalDumped = 0
|
|
local startTime = os.clock()
|
|
|
|
local function printStats()
|
|
local elapsed = os.clock() - startTime
|
|
local mins = math.floor(elapsed / 60)
|
|
local secs = math.floor(elapsed % 60)
|
|
term.clear()
|
|
term.setCursorPos(1, 1)
|
|
print("=================================")
|
|
print(" Mining Turtle - Running")
|
|
print("=================================")
|
|
print(string.format(" Mined: %d blocks", totalMined))
|
|
print(string.format(" Dumped: %d items", totalDumped))
|
|
print(string.format(" Uptime: %dm %ds", mins, secs))
|
|
if turtle.getFuelLevel() ~= "unlimited" then
|
|
print(string.format(" Fuel: %d", turtle.getFuelLevel()))
|
|
end
|
|
print(string.format(" Inv used: %d/16 slots", occupiedSlots()))
|
|
print("=================================")
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Main mining loop
|
|
-------------------------------------------------
|
|
|
|
local function mineLoop()
|
|
local lastDump = os.clock()
|
|
|
|
while true do
|
|
-- Auto-refuel if low
|
|
autoRefuel()
|
|
|
|
-- Check fuel
|
|
if turtle.getFuelLevel() ~= "unlimited" and turtle.getFuelLevel() < 1 then
|
|
print("[WARN] Out of fuel! Waiting...")
|
|
while turtle.getFuelLevel() < 1 do
|
|
autoRefuel()
|
|
sleep(5)
|
|
end
|
|
end
|
|
|
|
-- Mine the block below
|
|
if turtle.detectDown() then
|
|
local ok, err = turtle.digDown()
|
|
if ok then
|
|
totalMined = totalMined + 1
|
|
end
|
|
end
|
|
|
|
-- Dump inventory if threshold reached or timer expired
|
|
local now = os.clock()
|
|
if occupiedSlots() >= DUMP_THRESHOLD or (now - lastDump) >= DUMP_INTERVAL then
|
|
if occupiedSlots() > 0 then
|
|
local pushed = dumpInventory()
|
|
totalDumped = totalDumped + pushed
|
|
if pushed > 0 then
|
|
print(string.format("[DUMP] Pushed %d items to storage", pushed))
|
|
end
|
|
end
|
|
lastDump = os.clock()
|
|
end
|
|
|
|
-- Refresh display periodically
|
|
printStats()
|
|
|
|
sleep(MINE_INTERVAL)
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Reboot listener (remote reboot support)
|
|
-------------------------------------------------
|
|
|
|
local function rebootListener()
|
|
modem.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 == "miner" or target == tostring(os.getComputerID()) then
|
|
print("[SYSTEM] Reboot command received.")
|
|
sleep(0.5)
|
|
os.reboot()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Entry point
|
|
-------------------------------------------------
|
|
|
|
print("Starting mining loop...")
|
|
print("Press Ctrl+T to stop.")
|
|
print("")
|
|
sleep(1)
|
|
|
|
local ok, err = pcall(function()
|
|
parallel.waitForAny(mineLoop, rebootListener)
|
|
end)
|
|
|
|
if not ok then
|
|
fatal("[ERR] Mining turtle crashed:\n" .. tostring(err))
|
|
end
|