diff --git a/CLAUDE.md b/CLAUDE.md index f7c033c..d5e191d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,27 +33,16 @@ Three layers, bottom-up: - `9` — ping - `10` — router / default routing channel -- `64` — cube (deployment/control) These are duplicated as local constants across files; keep them in sync when changing. -## The `cube` deployment system - -`cube` (client `programs/cube.lua`, server `servers/cube-server.lua`) manages a cluster of "cube" machines over channel 64: - -- `cube ls` — list reachable cubes (broadcast ping; each replies with its boot command). `*` marks the local machine. -- `cube deploy` — walk the local filesystem (skipping `IGNORED_PATHS`: `/rom`, `/.cubeboot`, `/.git`, `/.gitignore`, `/startup.lua`), send every file to each remote cube via `deploy-file`, then reboot it. -- `cube set-boot [command]` — write/clear the remote's `/.cubeboot`, then reboot. Empty command deletes the boot hook. -- `cube reboot ` — remote reboot. -- `.cubeboot` holds a per-machine startup shell command, run by `servers/cube-boot.lua` at boot. - ## Boot flow `startup/servers.lua` is the entry point on each machine: it adds `/programs` to `shell.path`, then `parallel.waitForAll` runs an interactive shell alongside every server in `SERVERS`. When all stop, it reboots. ## CraftOS-PC emulation -`startup/servers.lua` detects the `periphemu` global and, when present, creates an emulated modem plus (on computer 0) a few emulated peer/router computers. Code guards CraftOS-PC quirks with `if periphemu then ... end` (e.g. `os.sleep(0.5)` after reboots in `cube.lua` to avoid crashes). Preserve these guards. +`startup/servers.lua` detects the `periphemu` global and, when present, creates an emulated modem plus (on computer 0) a few emulated peer/router computers. Preserve these guards. ## Installation / distribution diff --git a/README.md b/README.md index 09ea0ca..b3df93d 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,10 @@ wget run https://raw.githubusercontent.com/guillaumearm/cc-libs/master/install.l All servers are automatically started at boot. - `/servers/ping-server`: allows a machine to respond to a `ping` command. -- `/servers/cube-server`: allows a machine to be controllable via `cube`. -- `/servers/cube-boot.lua`: `cube` boot script. ## Programs - `router`: routes messages. You need to set up a router to use all `apis/net`-based programs and libraries. - `ping`: pings machines using `apis/net`. -- `cube`: cube client for deployment. Use the `cube help` command for more details. -- `goo`: turtle program for Just Dire Things goo block processing. ## Development See [DEVELOPMENT.md](./DEVELOPMENT.md) for development setup and workflow. diff --git a/install.lua b/install.lua index 8aec1e1..d8a18ce 100644 --- a/install.lua +++ b/install.lua @@ -1,17 +1,13 @@ -local _VERSION = '2.0.3' +local _VERSION = '2.0.4' local LIST_FILES = { -- startup 'startup/servers.lua', -- servers 'servers/ping-server.lua', - 'servers/cube-server.lua', - 'servers/cube-boot.lua', -- programs 'programs/router.lua', -- router is not in servers folder because he's not ran on every machines 'programs/ping.lua', - 'programs/cube.lua', - 'programs/goo.lua', 'programs/upgrade.lua', -- apis 'apis/net.lua', @@ -24,6 +20,10 @@ fs.delete('ping.lua') -- replaced by `programs/ping.lua` fs.delete('cube.lua') -- replaced by `programs/cube.lua` fs.delete('router.lua') -- replaced by `programs/router.lua` fs.delete('servers/cube-startup.lua'); -- replaced by `servers/cube-boot.lua` +fs.delete('programs/cube.lua'); +fs.delete('programs/goo.lua'); +fs.delete('servers/cube-server.lua'); +fs.delete('servers/cube-boot.lua'); local REPO_PREFIX = 'https://raw.githubusercontent.com/guillaumearm/cc-libs/master/' diff --git a/programs/cube.lua b/programs/cube.lua deleted file mode 100644 index 9247a69..0000000 --- a/programs/cube.lua +++ /dev/null @@ -1,342 +0,0 @@ -local _VERSION = '2.3.0'; -local CUBE_CHANNEL = 64; - -local net = require('/apis/net')(); - -local args = table.pack(...); -local cubeCommand = args[1]; -local firstArg = args[2]; -local secondArg = args[3]; - -local function getRemainingArgs(startIndex) - local remainingArgs = {}; - - for i = startIndex, args.n do - table.insert(remainingArgs, tostring(args[i])); - end - - return table.concat(remainingArgs, ' '); -end - -local IGNORED_PATHS = { - ['/rom'] = true, - ['/.cubeboot'] = true, - ['/.git'] = true, - ['/.gitignore'] = true, - ['/startup.lua'] = true, -} - -local function isValidPath(givenPath) - return not IGNORED_PATHS[givenPath] -end - -local function getAllFiles(basePath, result) - basePath = basePath or '/' - result = result or {}; - - local fileNames = fs.list(basePath) - - for i = 1, #fileNames do - local filePath = basePath .. fileNames[i]; - local valid = isValidPath(filePath); - - if valid and fs.isDir(filePath) then - getAllFiles(filePath .. '/', result); - elseif valid and not fs.isDir(filePath) then - table.insert(result, filePath) - end - end - - return result; -end - -local function readFile(path) - local file = fs.open(path, "r"); - - if not file then - return nil; - end - - local contents = file.readAll() - file.close() - - return contents -end - ---- Pads str to length len with char from right -local leftPad = function(str, len, char) - if char == nil then char = ' ' end - local nbRepetition = len - #str; - - if nbRepetition > 0 then - return str .. string.rep(char, len - #str) - end - - return str; -end - -local function getRow(margin, str1, str2, str3) - - margin = margin or ''; - - local row1 = leftPad(margin .. tostring(str1 or ''), 8, ' ') - local row2 = leftPad(tostring(str2 or ''), 16, ' ') - local row3 = leftPad(tostring(str3 or ''), 6, ' ') - - return row1 .. row2 .. row3; -end - -local function isFlag(name) - return function(arg) - return arg == '-' .. name or arg == '--' .. name; - end -end - -local isHelpFlag = isFlag('help'); -local isVersionFlag = isFlag('version'); - -local function printUsage() - print('cube usage:') - print(); - print('\t\t\tcube ls'); - print('\t\t\tcube configure'); - print('\t\t\tcube set-boot [command]') - print('\t\t\tcube reboot ') - print('\t\t\tcube deploy') - print('\t\t\tcube version') - print('\t\t\tcube help ') -end - -local function printUsageCommand(commandName) - local function setBootUsage() - print('\t\t\tcube set-boot [command]') - print('Setup a startup shell command on a remote cube.') - end - - local USAGES = { - ls = function() - print('\t\t\tcube ls'); - print('Print all available cubes in the cluster.') - end, - configure = function() - print('\t\t\tcube configure'); - print('Setup remote slave cubes.') - end, - ["set-boot"] = setBootUsage, - ["setboot"] = setBootUsage, - ["set-start"] = setBootUsage, - ["setstart"] = setBootUsage, - ["set-startup"] = setBootUsage, - ["setstartup"] = setBootUsage, - reboot = function() - print('\t\t\tcube reboot ') - print('Reboot a cube machine.'); - end, - deploy = function() - print('\t\t\tcube deploy') - print('Transfer files on all slave cubes.') - end, - version = function() - print('\t\t\tcube version') - print('Print the program version.') - end, - help = function() - print('\t\t\tcube help ') - print('Print help on commands.') - end, - } - - local usageFn = USAGES[commandName] - - if not usageFn then - return printUsage(); - end - - return usageFn(); -end - -if cubeCommand == nil or cubeCommand == '' or isHelpFlag(cubeCommand) then - printUsage(); - return; -end - ------------- --- reboot -- ------------- -local function rebootCommand(machineId, silentReboot) - if not machineId or machineId == '' then - printUsageCommand('reboot'); - return; - end - - local ok, results, packets = net.sendMultipleRequests(CUBE_CHANNEL, 'reboot', true, machineId); - - if not ok then - error(results); - end - - for k in ipairs(results) do - local packet = packets[k]; - - if silentReboot ~= true then - print('reboot machine \'' .. tostring(packet.sourceId) .. '\''); - end - end -end - --------------- --- set-boot -- --------------- -local function setBootCommand(machineId, shellCommand) - if not machineId then - printUsageCommand('set-boot'); - return; - end - - local ok, results, packets = net.sendMultipleRequests(CUBE_CHANNEL, 'set-boot', shellCommand, machineId); - - if not ok then - error(results); - end - - for k in ipairs(results) do - local packet = packets[k]; - - if shellCommand == nil or shellCommand == '' then - print('boot DELETED'); - else - print('boot UPDATED'); - end - - rebootCommand(packet.sourceId, true); - - -- prevent CraftOS-PC crashes - if periphemu then - os.sleep(0.5) - end - end -end - ------------- --- deploy -- ------------- -local function deployCommand() - local allFiles = getAllFiles() - - -- 1. get all machine ids (except the current one) - local ok, results, packets = net.sendMultipleRequests(CUBE_CHANNEL, 'ping', 'ping'); - - if not ok then - error(results); - end - - local machineIds = {}; - - local localComputerId = os.getComputerID(); - - for k in ipairs(results) do - local packet = packets[k]; - - if packet.sourceId ~= localComputerId then - table.insert(machineIds, packet.sourceId); - end - end - - -- 2. transfer files on all concerned machines - for machineIndex = 1, #machineIds do - local machineId = machineIds[machineIndex]; - - local fileTransfered = 0; - - for i = 1, #allFiles do - local filePath = allFiles[i]; - local fileContent = readFile(filePath) - - local transferOk, res = net.sendRequest(CUBE_CHANNEL, 'deploy-file', { path = filePath, content = fileContent }, machineId); - - if transferOk and res then - fileTransfered = fileTransfered + 1; - else - print('Error transfering file \'' .. filePath .. '\''); - end - end - - print(tostring(fileTransfered) .. ' file(s) transfered on machine ' .. tostring(machineId)) - - rebootCommand(machineId, true); - - -- prevent CraftOS-PC crashes - if periphemu then - os.sleep(0.5) - end - end -end - -local COMMANDS = { - ls = function() - local ok, results, packets = net.sendMultipleRequests(CUBE_CHANNEL, 'ping', 'ping'); - - if not ok then - error(results); - end - - -- print('ID LABEL\t\t\t\tSTARTUP'); - print(getRow(' ', 'ID', 'LABEL', 'BOOT')) - print('--------------------------------------------') - - local localMachineId = os.getComputerID(); - - for k in ipairs(results) do - local result = results[k]; - local packet = packets[k]; - - local prefix = ' '; - - if packet.sourceId == localMachineId then - prefix = '* ' - end - - print(getRow(prefix, packet.sourceId, packet.sourceLabel, result.startup)) - end - end, - configure = function() - print('not implemented yet.'); - end, - ["set-boot"] = setBootCommand, - ["setboot"] = setBootCommand, - ["set-start"] = setBootCommand, - ["setstart"] = setBootCommand, - ["set-startup"] = setBootCommand, - ["setstartup"] = setBootCommand, - reboot = rebootCommand, - deploy = deployCommand, - version = function() - print('cube client v' .. _VERSION); - end, - help = function(commandName) - printUsageCommand(commandName); - end -} - -local cmd; -if isVersionFlag(cubeCommand) then - cmd = COMMANDS.version; -else - cmd = COMMANDS[cubeCommand]; -end - -if not cmd then - printUsage(); - return; -end - -if (isHelpFlag(firstArg)) then - printUsageCommand(cubeCommand); - return; -end - -if cmd == setBootCommand then - cmd(firstArg, getRemainingArgs(3)); -else - cmd(firstArg, secondArg); -end diff --git a/programs/goo.lua b/programs/goo.lua deleted file mode 100644 index eb718a4..0000000 --- a/programs/goo.lua +++ /dev/null @@ -1,832 +0,0 @@ -local _VERSION = "1.0.0" - -local args = table.pack(...) -local command = args[1] - -local GOO_BLOCK_PREFIX = "justdirethings:gooblock_tier" -local MIN_START_FUEL = 50 -local REFUEL_THRESHOLD = 1000 -local WAIT_SECONDS = 2 -local MIN_FEEDING_ITEMS = 4 -local MAX_SUCK_ATTEMPTS = 16 - -local FUEL_ITEMS = { - "justdirethings:coal_t4", - "justdirethings:coal_t3", - "justdirethings:coal_t2", - "justdirethings:coal_t1", - "minecraft:coal", -} - -local PROCESS_ITEMS = { - ["minecraft:iron_block"] = { tier = 1 }, - ["minecraft:coal_block"] = { tier = 1 }, - ["mekanism:block_charcoal"] = { tier = 1 }, - ["justdirethings:coalblock_t1"] = { tier = 1 }, - ["justdirethings:coalblock_t2"] = { tier = 2 }, - ["justdirethings:coalblock_t3"] = { tier = 3 }, - ["justdirethings:coalblock_t4"] = { tier = 4 }, - ["minecraft:gold_block"] = { tier = 2 }, - ["minecraft:diamond_block"] = { tier = 3 }, - ["minecraft:netherite_block"] = { tier = 4 }, -} - -local FEEDING_ITEMS_BY_TIER = { - [1] = { "minecraft:sugar", "minecraft:rotten_flesh" }, - [2] = { "minecraft:nether_wart" }, - [3] = { "minecraft:chorus_fruit" }, - [4] = { "minecraft:sculk" }, -} - -local DIR_NORTH = 0 -local DIR_EAST = 1 -local DIR_SOUTH = 2 -local DIR_WEST = 3 - -local DIRECTION_DELTAS = { - [DIR_NORTH] = { x = 0, z = -1 }, - [DIR_EAST] = { x = 1, z = 0 }, - [DIR_SOUTH] = { x = 0, z = 1 }, - [DIR_WEST] = { x = -1, z = 0 }, -} - -local HOME = { x = -2, y = 0, z = 0, facing = DIR_EAST } -local GOO_CHECK = { x = -1, y = 0, z = 0, facing = DIR_EAST } - -local GOO_CHECKS = { - { name = "west goo check", x = -1, y = 0, z = 0, facing = DIR_EAST, action = "forward" }, - { name = "east goo check", x = 1, y = 0, z = 0, facing = DIR_WEST, action = "forward" }, - { name = "north goo check", x = 0, y = 0, z = -1, facing = DIR_SOUTH, action = "forward" }, - { name = "south goo check", x = 0, y = 0, z = 1, facing = DIR_NORTH, action = "forward" }, - { name = "top goo check", x = 0, y = 1, z = 0, facing = DIR_EAST, action = "down" }, -} - -local TARGETS = { - { name = "west side", x = -2, y = 0, z = 0, facing = DIR_EAST, action = "forward" }, - { name = "east side", x = 2, y = 0, z = 0, facing = DIR_WEST, action = "forward" }, - { name = "north side", x = 0, y = 0, z = -2, facing = DIR_SOUTH, action = "forward" }, - { name = "south side", x = 0, y = 0, z = 2, facing = DIR_NORTH, action = "forward" }, - { name = "top", x = -1, y = 1, z = 0, facing = DIR_EAST, action = "forward" }, -} - -local position = { x = HOME.x, y = HOME.y, z = HOME.z } -local facing = HOME.facing -local loggedBlockedItems = {} -local placedTargets = {} -local lastGooTier = nil - -local function isFlag(name) - return function(arg) - return arg == "-" .. name or arg == "--" .. name - end -end - -local isHelpFlag = isFlag("help") -local isVersionFlag = isFlag("version") - -local function printUsage() - print("goo usage:") - print() - print("\t\t\tgoo start") - print("\t\t\tgoo version") - print("\t\t\tgoo help") -end - -if command == "version" or isVersionFlag(command) then - print("goo v" .. _VERSION) - return -end - -if command == nil or command == "" or command == "help" or isHelpFlag(command) then - printUsage() - return -end - -if command ~= "start" then - printUsage() - return -end - -if not turtle then - error("goo must be run on a turtle") -end - -local function hasPickaxeEquipped() - local left = turtle.getEquippedLeft() - local right = turtle.getEquippedRight() - - return (left and string.match(left.name or "", "_pickaxe$") ~= nil) - or (right and string.match(right.name or "", "_pickaxe$") ~= nil) -end - -local function parseGooTier(blockName) - local tier = string.match(blockName or "", "^" .. GOO_BLOCK_PREFIX .. "(%d+)$") - - return tonumber(tier) -end - -local function isGooBlock(blockName) - return parseGooTier(blockName) ~= nil -end - -local function isProcessBlock(blockName) - return PROCESS_ITEMS[blockName] ~= nil -end - -local function isFuelItem(itemName) - for i = 1, #FUEL_ITEMS do - if FUEL_ITEMS[i] == itemName then - return true - end - end - - return false -end - -local function isFeedingItemForTier(itemName, gooTier) - local feedingItems = FEEDING_ITEMS_BY_TIER[gooTier] or {} - - for i = 1, #feedingItems do - if feedingItems[i] == itemName then - return true - end - end - - return false -end - -local function countItem(itemName) - local count = 0 - - for slot = 1, 16 do - local item = turtle.getItemDetail(slot) - - if item and item.name == itemName then - count = count + item.count - end - end - - return count -end - -local function countFeedingItems(gooTier) - local count = 0 - local feedingItems = FEEDING_ITEMS_BY_TIER[gooTier] or {} - - for i = 1, #feedingItems do - count = count + countItem(feedingItems[i]) - end - - return count -end - -local function findItemSlot(itemName) - for slot = 1, 16 do - local item = turtle.getItemDetail(slot) - - if item and item.name == itemName then - return slot, item - end - end - - return nil -end - -local function hasFreeSlot() - for slot = 1, 16 do - if turtle.getItemCount(slot) == 0 then - return true - end - end - - return false -end - -local function getFuelLevel() - local fuelLevel = turtle.getFuelLevel() - - if fuelLevel == "unlimited" then - return nil - end - - return fuelLevel -end - -local function getRefuelTarget() - local fuelLimit = turtle.getFuelLimit() - - if fuelLimit == "unlimited" or fuelLimit >= REFUEL_THRESHOLD then - return REFUEL_THRESHOLD - end - - return fuelLimit -end - -local function findFuelSlot() - for i = 1, #FUEL_ITEMS do - local slot, item = findItemSlot(FUEL_ITEMS[i]) - - if slot then - return slot, item - end - end - - return nil -end - -local function turnRight() - turtle.turnRight() - facing = (facing + 1) % 4 -end - -local function turnLeft() - turtle.turnLeft() - facing = (facing + 3) % 4 -end - -local function turnTo(direction) - while facing ~= direction do - local rightTurns = (direction - facing) % 4 - - if rightTurns == 3 then - turnLeft() - else - turnRight() - end - end -end - -local function moveForward() - if not turtle.forward() then - return false - end - - local delta = DIRECTION_DELTAS[facing] - position.x = position.x + delta.x - position.z = position.z + delta.z - - return true -end - -local function moveUp() - if not turtle.up() then - return false - end - - position.y = position.y + 1 - - return true -end - -local function moveDown() - if not turtle.down() then - return false - end - - position.y = position.y - 1 - - return true -end - -local function moveAxis(target, positiveDirection, negativeDirection, axis) - while position[axis] ~= target do - if position[axis] < target then - turnTo(positiveDirection) - else - turnTo(negativeDirection) - end - - if not moveForward() then - return false - end - end - - return true -end - -local function goTo(target) - while position.y < target.y do - if not moveUp() then - return false - end - end - - while position.y > target.y do - if not moveDown() then - return false - end - end - - if not moveAxis(target.x, DIR_EAST, DIR_WEST, "x") then - return false - end - - if not moveAxis(target.z, DIR_SOUTH, DIR_NORTH, "z") then - return false - end - - if target.facing then - turnTo(target.facing) - end - - return true -end - -local function mustGoTo(target, description) - while not goTo(target) do - print("Cannot reach " .. description .. ". Clear the path and waiting...") - os.sleep(WAIT_SECONDS) - end -end - -local function goHome() - mustGoTo(HOME, "home/provider") -end - -local function goToGooCheck() - mustGoTo(GOO_CHECK, "goo check position") -end - -local function inspectDirection(action) - if action == "forward" then - return turtle.inspect() - elseif action == "up" then - return turtle.inspectUp() - elseif action == "down" then - return turtle.inspectDown() - end - - error("unknown inspect action " .. tostring(action)) -end - -local function placeDirection(action) - if action == "forward" then - return turtle.place() - elseif action == "up" then - return turtle.placeUp() - elseif action == "down" then - return turtle.placeDown() - end - - error("unknown place action " .. tostring(action)) -end - -local function refuelFromInventory() - local fuelLevel = getFuelLevel() - - if not fuelLevel or fuelLevel >= getRefuelTarget() then - return false - end - - local previousSlot = turtle.getSelectedSlot() - local consumedFuel = false - - while fuelLevel < getRefuelTarget() do - local slot, item = findFuelSlot() - - if not slot then - break - end - - turtle.select(slot) - - if turtle.refuel(1) then - consumedFuel = true - print("Refueled with " .. item.name .. ". Fuel: " .. tostring(turtle.getFuelLevel())) - fuelLevel = getFuelLevel() - else - print("Could not refuel with " .. item.name .. ".") - break - end - end - - turtle.select(previousSlot) - - return consumedFuel -end - -local function isProviderBelow() - local ok = turtle.inspectDown() - - return ok -end - -local function pullFromProvider() - goHome() - - local pulled = false - - for _ = 1, MAX_SUCK_ATTEMPTS do - if not hasFreeSlot() then - break - end - - if turtle.suckDown() then - pulled = true - else - break - end - end - - return pulled -end - -local function shouldKeepItem(item, gooTier) - if isFuelItem(item.name) then - return true - end - - local processItem = PROCESS_ITEMS[item.name] - - if processItem and processItem.tier <= gooTier then - return true - end - - if isFeedingItemForTier(item.name, gooTier) then - return countFeedingItems(gooTier) <= MIN_FEEDING_ITEMS - end - - return false -end - -local function depositOutputs(gooTier) - goHome() - - local previousSlot = turtle.getSelectedSlot() - - for slot = 1, 16 do - local item = turtle.getItemDetail(slot) - - if item and not shouldKeepItem(item, gooTier) then - turtle.select(slot) - - if turtle.dropDown() then - print("Deposited " .. item.name .. ".") - else - print("Could not deposit " .. item.name .. ". Provider may be full.") - break - end - end - end - - turtle.select(previousSlot) -end - -local function waitForProvider(message) - if message then - print(message) - end - - repeat - os.sleep(WAIT_SECONDS) - until pullFromProvider() -end - -local function ensureStartFuel() - while true do - local fuelLevel = getFuelLevel() - - if not fuelLevel or fuelLevel >= MIN_START_FUEL then - return - end - - refuelFromInventory() - fuelLevel = getFuelLevel() - - if not fuelLevel or fuelLevel >= MIN_START_FUEL then - return - end - - waitForProvider("Fuel is below " .. tostring(MIN_START_FUEL) .. ". Add fuel to the provider below home.") - end -end - -local function ensureRuntimeFuel() - local fuelLevel = getFuelLevel() - - if not fuelLevel then - return - end - - if fuelLevel < getRefuelTarget() then - refuelFromInventory() - end - - fuelLevel = getFuelLevel() - - while fuelLevel and fuelLevel < MIN_START_FUEL do - waitForProvider("Fuel is below " .. tostring(MIN_START_FUEL) .. ". Add fuel to the provider below home.") - refuelFromInventory() - fuelLevel = getFuelLevel() - end -end - -local function findFeedingSlot(gooTier) - local feedingItems = FEEDING_ITEMS_BY_TIER[gooTier] or {} - - for i = 1, #feedingItems do - local slot, item = findItemSlot(feedingItems[i]) - - if slot then - return slot, item - end - end - - return nil -end - -local function findEligibleProcessSlot(gooTier) - for slot = 1, 16 do - local item = turtle.getItemDetail(slot) - local processItem = item and PROCESS_ITEMS[item.name] - - if processItem then - if processItem.tier <= gooTier then - return slot, item, processItem - end - - local logKey = item.name .. ":" .. tostring(gooTier) - - if not loggedBlockedItems[logKey] then - print( - item.name - .. " requires goo tier " - .. tostring(processItem.tier) - .. ", current tier is " - .. tostring(gooTier) - ) - loggedBlockedItems[logKey] = true - end - end - end - - return nil -end - -local function inspectGoo() - for i = 1, #GOO_CHECKS do - local check = GOO_CHECKS[i] - - if goTo(check) then - local ok, inspected = inspectDirection(check.action) - - if ok then - local tier = parseGooTier(inspected.name) - - if tier then - lastGooTier = tier - - return tier, inspected, check - end - end - end - end - - if lastGooTier then - print("Cannot reach the goo to inspect it. Using last known tier " .. tostring(lastGooTier) .. ".") - - return lastGooTier, { state = { alive = true } }, nil - end - - error("expected a reachable Just Dire Things goo block around the turtle") -end - -local function ensureGooAlive() - while true do - local gooTier, inspected, check = inspectGoo() - - if not check or inspected.state and inspected.state.alive == true then - return gooTier - end - - local slot, item = findFeedingSlot(gooTier) - - if not slot then - waitForProvider("Goo tier " .. tostring(gooTier) .. " is not alive. Add feeding items to the provider.") - else - turtle.select(slot) - print("Goo tier " .. tostring(gooTier) .. " is not alive. Feeding with " .. item.name .. "...") - - if not placeDirection(check.action) then - print("Could not feed the goo. Waiting before retry...") - os.sleep(WAIT_SECONDS) - else - os.sleep(1) - end - end - end -end - -local function selectProcessItem(gooTier) - local slot, item = findEligibleProcessSlot(gooTier) - - if not slot then - return nil - end - - turtle.select(slot) - - return item -end - -local function inspectTarget(target) - if target.action == "forward" then - return turtle.inspect() - elseif target.action == "up" then - return turtle.inspectUp() - elseif target.action == "down" then - return turtle.inspectDown() - end - - error("unknown target action " .. tostring(target.action)) -end - -local function digTarget(target) - if target.action == "forward" then - return turtle.dig() - elseif target.action == "up" then - return turtle.digUp() - elseif target.action == "down" then - return turtle.digDown() - end - - error("unknown target action " .. tostring(target.action)) -end - -local function placeTarget(target) - if target.action == "forward" then - return turtle.place() - elseif target.action == "up" then - return turtle.placeUp() - elseif target.action == "down" then - return turtle.placeDown() - end - - error("unknown target action " .. tostring(target.action)) -end - -local function ensureMiningSpace(gooTier) - while not hasFreeSlot() do - depositOutputs(gooTier) - - if not hasFreeSlot() then - print("Inventory is full and provider may be full. Waiting...") - os.sleep(WAIT_SECONDS) - end - end -end - -local function isTargetQueued(target) - for i = 1, #placedTargets do - if placedTargets[i] == target then - return true - end - end - - return false -end - -local function removeQueuedTarget(index) - table.remove(placedTargets, index) -end - -local function goToTarget(target) - mustGoTo(target, target.name) -end - -local function mineQueuedTarget(gooTier) - if #placedTargets == 0 then - return false - end - - local target = placedTargets[1] - - goToTarget(target) - ensureGooAlive() - goToTarget(target) - - local ok, inspected = inspectTarget(target) - - if not ok then - print("Queued " .. target.name .. " is empty.") - removeQueuedTarget(1) - return true - end - - if isProcessBlock(inspected.name) then - return false - end - - if isGooBlock(inspected.name) then - removeQueuedTarget(1) - return true - end - - print("Mining processed " .. target.name .. " block: " .. tostring(inspected.name)) - ensureMiningSpace(gooTier) - goToTarget(target) - - if digTarget(target) then - removeQueuedTarget(1) - return true - end - - print("Could not mine " .. target.name .. ".") - - return false -end - -local function placeAvailableTarget(gooTier) - for i = 1, #TARGETS do - local target = TARGETS[i] - - if not isTargetQueued(target) then - goToTarget(target) - ensureGooAlive() - goToTarget(target) - - local ok, inspected = inspectTarget(target) - - if ok then - if not isProcessBlock(inspected.name) and not isGooBlock(inspected.name) then - print("Mining existing " .. target.name .. " block: " .. tostring(inspected.name)) - ensureMiningSpace(gooTier) - goToTarget(target) - - if digTarget(target) then - return true - end - end - else - local item = selectProcessItem(gooTier) - - if not item then - return false - end - - print("Placing " .. item.name .. " on goo " .. target.name) - - if placeTarget(target) then - placedTargets[#placedTargets + 1] = target - return true - end - - print("Could not place " .. target.name .. ".") - end - end - end - - return false -end - -local function waitAtOldestTarget() - if #placedTargets == 0 then - goToGooCheck() - else - goToTarget(placedTargets[1]) - end - - print("Waiting for goo processing...") - os.sleep(WAIT_SECONDS) -end - -local function startup() - print("goo started. Layout: provider below turtle, goo two blocks ahead, turtle facing goo.") - - if not hasPickaxeEquipped() then - error("goo requires a turtle with a pickaxe equipped") - end - - if not isProviderBelow() then - error("goo requires a provider/deposit barrel below the turtle") - end - - pullFromProvider() - ensureStartFuel() -end - -startup() - -while true do - ensureRuntimeFuel() - - local gooTier = ensureGooAlive() - depositOutputs(gooTier) - pullFromProvider() - ensureRuntimeFuel() - - gooTier = ensureGooAlive() - local changed = mineQueuedTarget(gooTier) - - if not changed then - changed = placeAvailableTarget(gooTier) - end - - if not changed then - if findEligibleProcessSlot(gooTier) then - waitAtOldestTarget() - else - waitForProvider("No eligible process block found. Add inputs to the provider below home.") - end - end -end diff --git a/servers/cube-boot.lua b/servers/cube-boot.lua deleted file mode 100644 index 44f55a5..0000000 --- a/servers/cube-boot.lua +++ /dev/null @@ -1,28 +0,0 @@ -local _VERSION = '2.0.0'; - -local function trim(s) - return (string.gsub(s, "^%s*(.-)%s*$", "%1")) -end - -local function readFile(path) - local file = fs.open(path, "r"); - - if not file then - return nil; - end - - local contents = file.readAll() - file.close() - - return contents -end - -local startupCommand = trim(readFile('.cubeboot') or ""); - - -if startupCommand ~= "" then - print('cube-boot v' .. _VERSION .. ': execute \'' .. startupCommand .. '\'...'); - shell.run(startupCommand); -else - print('cube-startup v' .. _VERSION .. ' no startup command detected.') -end diff --git a/servers/cube-server.lua b/servers/cube-server.lua deleted file mode 100644 index ff85649..0000000 --- a/servers/cube-server.lua +++ /dev/null @@ -1,90 +0,0 @@ -local _VERSION = '2.1.0'; - -local net = require('/apis/net')(); - -local CUBE_CHANNEL = 64; - -local function trim(s) - return (string.gsub(s, "^%s*(.-)%s*$", "%1")) -end - -local function readFile(path) - local file = fs.open(path, "r"); - - if not file then - return nil; - end - - local contents = file.readAll() - file.close() - - return contents -end - -local function writeFile(path, content) - local file = fs.open(path, "w"); - - if not file then - return false; - end - - file.write(content) - file.close(); - - return true; -end - -local function ensureParentDir(path) - local parentPath = string.match(path, '^(.+)/[^/]+$'); - - if parentPath and parentPath ~= '' and not fs.exists(parentPath) then - fs.makeDir(parentPath); - end -end - -local function getStartupCommand() - return trim(readFile('.cubeboot') or "") -end - --- ping event -net.listenRequest(CUBE_CHANNEL, "ping", function(_, reply) - local startupCommand = getStartupCommand(); - - reply({ startup = startupCommand }); -end) - --- reboot event -net.listenRequest(CUBE_CHANNEL, "reboot", function(_, reply) - reply(true); - - os.sleep(0.2) - os.reboot() -end) - --- set-boot event -net.listenRequest(CUBE_CHANNEL, "set-boot", function(startupCommand, reply) - if startupCommand == nil or startupCommand == '' then - fs.delete('/.cubeboot'); - reply(true); - return; - end - - local res = writeFile('/.cubeboot', startupCommand); - reply(res); -end) - --- deploy-file event -net.listenRequest(CUBE_CHANNEL, "deploy-file", function(payload, reply) - if type(payload) ~= 'table' or type(payload.path) ~= 'string' or type(payload.content) ~= 'string' then - reply(false); - return; - end - - ensureParentDir(payload.path); - reply(writeFile(payload.path, payload.content)); -end) - -print('cube-server v' .. _VERSION .. ' started.') - --- start event loop -net.startLoop(); diff --git a/startup/servers.lua b/startup/servers.lua index 5d9cd66..62dd035 100644 --- a/startup/servers.lua +++ b/startup/servers.lua @@ -1,9 +1,7 @@ -local _VERSION = '1.1.1' +local _VERSION = '1.1.2' local SERVERS = { "servers/ping-server", - "servers/cube-server.lua", - "servers/cube-boot.lua", }; local function init()