chore: remove cube and goo programs
This commit is contained in:
parent
9dd4455203
commit
a373d140e8
13
CLAUDE.md
13
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 <machineId> [command]` — write/clear the remote's `/.cubeboot`, then reboot. Empty command deletes the boot hook.
|
||||
- `cube reboot <machineId>` — 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
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
10
install.lua
10
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/'
|
||||
|
||||
|
||||
@ -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 <machineId> [command]')
|
||||
print('\t\t\tcube reboot <machineId>')
|
||||
print('\t\t\tcube deploy')
|
||||
print('\t\t\tcube version')
|
||||
print('\t\t\tcube help <command>')
|
||||
end
|
||||
|
||||
local function printUsageCommand(commandName)
|
||||
local function setBootUsage()
|
||||
print('\t\t\tcube set-boot <machineId> [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 <machineId>')
|
||||
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 <command>')
|
||||
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
|
||||
832
programs/goo.lua
832
programs/goo.lua
@ -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
|
||||
@ -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
|
||||
@ -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();
|
||||
@ -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()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user