Compare commits

..

No commits in common. "master" and "v1" have entirely different histories.
master ... v1

28 changed files with 218 additions and 3670 deletions

View File

@ -1,38 +1,5 @@
# minecraft-cc-tools
Some tools for cc:tweaked minecraft mod (aka computercraft)
## Installation
### Step 1
Download the install script:
# Installation
```
wget https://git.trapcloud.fr/guillaumearm/minecraft-cc-tools/raw/branch/master/install.lua
```
### Step 2
Run the install script:
```
install
```
## Upgrade to a new version
```
upgrade
```
## Reset the whole installation
```
rm config
rm data
rm startup.lua
rm .minerstate
rm .robotstate
upgrade
wget run https://git.trapcloud.fr/guillaumearm/minecraft-cc-tools/raw/branch/master/install.lua
```

View File

@ -1,175 +0,0 @@
local turtleUtils = require('libs/turtle-utils')
local NB_NEEDED_COAL = 64 * 8
local WAIT_INVENTORY_TIME = 3
local IDLE_TIME = 30
local IDLE_TIME_ENOUGH_COAL = 120
local MIN_ESSENCE_NEEDED = 8
local COAL_ESSENCE_NAME = 'mysticalagriculture:coal_essence'
local COAL_NAME = 'minecraft:coal'
local CRAFTING_SLOT = 1
local STORAGE_BUFFER_SIDE = 'bottom'
local STORAGE_INVENTORY_SIDE = 'front'
local dropInStorageFn = turtle.drop
local suckFromBufferFn = turtle.suckDown
local function dropAll(side, dropFn)
local previousSelectedSlot = turtle.getSelectedSlot()
for i=1, 16, 1 do
local count = turtle.getItemCount(i)
if count > 0 then
turtleUtils.waitForInventory(side, IDLE_TIME)
local errorPrinted = false
while turtle.getItemCount(i) > 0 and not turtleUtils.dropSlot(i, dropFn) do
if not errorPrinted then
print('> please empty the turtle inventory')
errorPrinted = true
end
os.sleep(WAIT_INVENTORY_TIME)
end
end
end
if previousSelectedSlot ~= turtle.getSelectedSlot() then
turtle.select(previousSelectedSlot)
end
end
local function countCoalItems(inventory)
local total = 0
for slot, item in pairs(inventory.list()) do
if item.name == COAL_NAME then
total = total + item.count
end
end
return total
end
local function waitForNotEnoughCoal(inventory)
while true do
local nbCoals = countCoalItems(inventory)
if nbCoals < NB_NEEDED_COAL then
return
end
os.sleep(IDLE_TIME_ENOUGH_COAL)
end
end
-- the slot of the coal essence is returned
local function waitForCoalEssence(inventory)
while true do
for slot, item in pairs(inventory.list()) do
if item.name == COAL_ESSENCE_NAME and item.count >= MIN_ESSENCE_NEEDED then
return slot
end
end
os.sleep(IDLE_TIME)
end
end
local function strictTransferOne(slot)
local ok = turtle.transferTo(slot, 1)
if not ok then
error('cannot transfer item')
end
return ok
end
local function prepareCraftShape()
strictTransferOne(2)
strictTransferOne(3)
strictTransferOne(5)
strictTransferOne(7)
strictTransferOne(9)
strictTransferOne(10)
strictTransferOne(11)
end
local function craft()
local craftOk = turtle.craft()
if not craftOk then
error('cannot craft')
end
return craftOk
end
local function dropSelected()
dropInStorageFn()
end
local function findStorageChestOrientation()
for i=1, 4, 1 do
local inv = turtleUtils.getInventory(STORAGE_INVENTORY_SIDE)
if inv then
return
end
turtle.turnRight()
end
error('storage chest inventory not found')
end
local function main()
findStorageChestOrientation()
print('> Waiting for the back inventory (storage)')
local storageInventory = turtleUtils.waitForInventory(STORAGE_INVENTORY_SIDE, WAIT_INVENTORY_TIME)
print('> Waiting for the front inventory (buffer chest)')
local bufferInventory = turtleUtils.waitForInventory(STORAGE_BUFFER_SIDE, WAIT_INVENTORY_TIME)
print('> drop all')
dropAll(STORAGE_INVENTORY_SIDE, dropInStorageFn)
if not turtleUtils.isInventoryEmpty() then
error('Error: inventory is not empty', 0)
end
turtle.select(CRAFTING_SLOT)
print('> coal-crafter process started')
while true do
storageInventory = turtleUtils.waitForInventory(STORAGE_INVENTORY_SIDE, WAIT_INVENTORY_TIME)
bufferInventory = turtleUtils.waitForInventory(STORAGE_BUFFER_SIDE, WAIT_INVENTORY_TIME)
waitForNotEnoughCoal(storageInventory)
local coalEssenceSlot = waitForCoalEssence(storageInventory)
local pushOk = storageInventory.pushItems(peripheral.getName(bufferInventory), coalEssenceSlot, MIN_ESSENCE_NEEDED)
if not pushOk then
error('cannot pushItems from storage to buffer')
end
local suckOk, suckErr = suckFromBufferFn()
if not suckOk then
error('cannot suck into front buffer chest: ' .. tostring(suckErr))
end
turtleUtils.ensureSelected(CRAFTING_SLOT)
prepareCraftShape()
craft()
dropInStorageFn()
end
end
main()

View File

@ -1,20 +0,0 @@
local function mystical(essenceName)
return 'mysticalagriculture:' .. essenceName .. '_seeds'
end
return {
defaultConfig = {
plan = {
mystical('coal'),
mystical('coal'),
mystical('inferium'),
mystical('inferium'),
mystical('inferium'),
mystical('inferium'),
mystical('inferium'),
mystical('inferium')
},
fertilizedBoost = false,
firstCropZ = 1
}
}

View File

@ -1,17 +0,0 @@
return {
DIRECTION = 'right',
DISTANCE_Z = 256,
FUEL_MARGIN = 10,
VEIN_MODE = true,
FUEL_MARGIN_VEIN_MODE = 1000,
VEIN_ORES_WHITELIST = {
['create_new_age:magnetite_block'] = true,
['mysticalagriculture:prosperity_ore'] = true,
['mysticalagriculture:deepslate_prosperity_ore'] = true,
['mysticalagradditions:nether_prosperity_ore'] = true,
['mysticalagradditions:end_prosperity_ore'] = true
},
VEIN_ORES_WHITELIST_ITEMS = { -- do not drop these items
['mysticalagriculture:prosperity_shard'] = true
}
}

View File

@ -1,3 +0,0 @@
while true do
print(os.pullEvent())
end

View File

@ -1,251 +0,0 @@
local net = require('libs/net')
local utils = require('libs/utils')
local inferium = require('libs/inferium')
local CountersSelector = require('libs/ui/CountersSelector')
local function centerString(str, width)
width = width or term.getSize()
local padding = (width / 2) - (#str / 2)
return string.rep(' ', padding) .. str
end
local function getCountersSelectorConfig(counterMax)
local titleFn = function(countersMap, _, win)
local total = 0;
for _, counterPayload in pairs(countersMap) do
if counterPayload and counterPayload.count then
total = total + counterPayload.count
end
end
local width = win.getSize()
return centerString("" .. total .. '/' .. counterMax .. ' used farmlands' .. "", width)
end
local config = {
counterMax = counterMax,
titleFn = titleFn
}
return config
end
local function fetchAvailableSeeds(serverName)
local availableSeeds, errMessage = net.sendQuery(serverName, { type = 'list-seeds' })
if errMessage then
error('fetchAvailableSeeds error: ' .. errMessage, 0)
end
if not availableSeeds then
error('no available seeds', 0)
end
return availableSeeds
end
local function fetchAllHarvesters(serverName)
local harvesters, listErrMessage = net.sendQuery(serverName, { type = 'list-harvesters' })
if listErrMessage then
error('getAllHarvesters cannot list: ' .. listErrMessage, 0)
end
if utils.sizeof(harvesters) == 0 then
error('getAllHarvesters get no harvesters from the remote server', 0)
end
for k, harvester in pairs(harvesters) do
local response, errMessage = net.sendQuery(serverName, { type = 'get-config', payload = { id = harvester.id } })
if errMessage then
error('getAllHarvesters cannot get-config: ' .. errMessage, 0)
end
local payload = response and response.payload
if not payload then
error('getAllHarvesters no payload returned for harvester ' .. harvester.id, 0)
end
if not payload.plan then
error('getAllHarvesters no plan returned for the harvester ' .. harvester.id, 0)
end
harvesters[k].config = payload
end
return harvesters
end
local function getMaxCounter(harvesters)
local maxCounter = 0
for _, harvester in pairs(harvesters) do
maxCounter = maxCounter + utils.sizeof(harvester.config.plan)
end
return maxCounter
end
local function getCountersMapFromIndexed(countersMapIndexed)
local countersMap = {}
local i = 1
for name, count in pairs(countersMapIndexed) do
countersMap[i] = { name = name, count = count }
i = i + 1
end
return countersMap
end
local function mergeCountersMap(countersMapLeft, countersMapRight)
local indexed = {}
for _, counterPayload in pairs(countersMapLeft) do
indexed[counterPayload.name] = counterPayload.count
end
for _, counterPayload in pairs(countersMapRight) do
indexed[counterPayload.name] = counterPayload.count
end
return getCountersMapFromIndexed(indexed)
end
local function getInitialCountersMap(availableSeeds)
local result = {}
for k, seedName in pairs(availableSeeds) do
result[k] = {
name = inferium.parseSeedName(seedName),
count = 0
}
end
return result
end
local function createCountersMap(harvesters)
local countersMapIndexed = {}
for _, harvester in pairs(harvesters) do
for _, seedName in pairs(harvester.config.plan) do
local name = inferium.parseSeedName(seedName)
if name then
local currentCounter = countersMapIndexed[name] or 0
countersMapIndexed[name] = currentCounter + 1
end
end
end
return getCountersMapFromIndexed(countersMapIndexed)
end
local function pickSeedNameFromCountersMap(countersMap)
local newCountersMap = {}
local pickedCounterName = nil
for k, counterPayload in pairs(countersMap) do
local count = counterPayload.count
local name = counterPayload.name
if not pickedCounterName and count > 0 then
pickedCounterName = name
count = count - 1
end
local newCounterPayload = {
count = count,
name = name
}
newCountersMap[k] = newCounterPayload
end
return inferium.formatSeedName(pickedCounterName), newCountersMap
end
local function preparePayloads(countersMap, maxCounter, harvesters)
local payloads = {}
local globalCounter = 0
local configIndex = 1
for _, harvester in pairs(harvesters) do
local newConfig = utils.merge(harvester.config, {
plan = {},
})
payloads[configIndex] = { id = harvester.id, config = newConfig }
for k, _ in pairs(harvester.config.plan) do
local seedName, newCountersMaps = pickSeedNameFromCountersMap(countersMap)
countersMap = newCountersMaps
newConfig.plan[k] = seedName
globalCounter = globalCounter + 1
if globalCounter > maxCounter then
error('preparePayload fatal error: globalCounter is greater than maxCounter')
end
end
configIndex = configIndex + 1
end
return payloads
end
local function saveAllConfigs(serverName, payloads)
for _, payload in pairs(payloads) do
local setConfigResponse, errMessage = net.sendQuery(serverName, { type = 'set-config', payload = payload })
if errMessage then
return false, errMessage
end
if not setConfigResponse then
return false, 'unable to save config (check server logs)'
end
end
return true
end
local function main(serverName)
net.openRednet() -- TODO: handle closeRednet properly
print('> fetching available seeds from ' .. serverName)
local availableSeeds = fetchAvailableSeeds(serverName)
print('> ' .. utils.sizeof(availableSeeds) .. ' available seeds fetched')
print('> fetching all configs from ' .. serverName)
local harvesters = fetchAllHarvesters(serverName)
print('> ' .. utils.sizeof(harvesters) .. ' harvesters fetched')
local maxCounter = getMaxCounter(harvesters)
local countersMap = mergeCountersMap(getInitialCountersMap(availableSeeds), createCountersMap(harvesters))
local countersMapResult = CountersSelector(countersMap, getCountersSelectorConfig(maxCounter))
if not countersMapResult then
print('> canceled')
net.closeRednet()
return
end
local payloads = preparePayloads(countersMapResult, maxCounter, harvesters)
if utils.sizeof(payloads) == 0 then
error('fatal error: there is no payloads to save', 0)
end
print('> saving...')
local saveOk, saveErrMessage = saveAllConfigs(serverName, payloads)
if saveOk then
print('> done')
else
error('Cannot save configs because: ' .. tostring(saveErrMessage), 0)
end
net.closeRednet()
end
main(inferium.SERVER)

View File

@ -1,579 +0,0 @@
local net = require('libs/net')
local utils = require('libs/utils')
local inferium = require('libs/inferium')
local turtleUtils = require('libs/turtle-utils')
local VERSION = "4.1.1"
local IDLE_TIME = 2
local WAIT_ITEM_IDLE_TIME = 5
local NETWORK_TIMEOUT = 4
local MIN_INITIAL_FUEL_NEEDED = 16
local ADDITIONAL_FUEL_MARGIN = 100
local MAX_FERTILIZED_ESSENCE = 32
local MIN_FREE_SLOTS_BEFORE_COMPACT = 4
local TIME_TO_START = 3
-- a table with the list of current crops
local localPlan = nil
local previouslyFetchedRemoteConfig = nil
-- UTILS
local function removeFirst(t, x)
local res = {}
local removed = false
for k,v in pairs(t) do
if not removed and x == v then
removed = true
else
table.insert(res, v)
end
end
return res
end
local function getIndexedCount(t)
local res = {}
for k,v in pairs(t) do
local count = res[v] or 0
res[v] = count + 1
end
return res
end
local function getMinFuelNeeded(config)
return (ADDITIONAL_FUEL_MARGIN + config.firstCropZ + config.length) * 2
end
local function hasEnoughInitialFuel()
return turtle.getFuelLevel() >= MIN_INITIAL_FUEL_NEEDED
end
local function retrieveChestFuelOrientation()
local inventoryFound = false
for i=1, 4, 1 do
if turtleUtils.getInventory('front') then
inventoryFound = true
break
end
turtle.turnRight()
end
if not inventoryFound then
error('chest fuel not found')
end
end
local function getSeedNameFromCropName(cropName)
if not cropName then
return cropName
end
return string.gsub(cropName, 'crop', 'seeds')
end
-- Inventory utils
local function getItemSlot(inventory, itemName)
for slot, item in pairs(inventory.list()) do
if item and item.name == itemName then
return slot
end
end
end
local function getCountItem(inventory, itemName)
for _, item in pairs(inventory.list()) do
if item and item.name == itemName then
return item.count
end
end
end
-- Implementations
local function retrieveHomePositionProcedure()
print("> retrieving the home position")
if turtleUtils.getInventory('bottom') then
retrieveChestFuelOrientation()
return
end
turtle.turnRight()
if turtle.inspect() then
turtle.turnRight()
end
while true do
if turtleUtils.getInventory('bottom') then
turtle.turnLeft()
return
end
if turtle.inspect() then
break
else
turtle.forward()
end
end
turtle.turnLeft()
turtle.turnLeft()
while true do
if turtleUtils.getInventory('bottom') then
break
end
if turtle.inspect() then
error('Cannot retrieve home position')
else
turtle.forward()
end
end
turtle.turnLeft()
end
-- first parameter is config
local function dropAllProcedure(_)
print('> drop all')
for i=1, 16, 1 do
local count = turtle.getItemCount(i)
if count > 0 then
turtleUtils.waitForInventory('bottom', IDLE_TIME)
while turtle.getItemCount(i) and not turtleUtils.dropSlot(i, turtle.dropDown) do
os.sleep(IDLE_TIME)
end
end
end
end
local function refuelProcedure(config)
local fuelLevel = turtle.getFuelLevel()
print('> fuel: ' .. tostring(fuelLevel))
local minFuelNeeded = getMinFuelNeeded(config)
if fuelLevel < minFuelNeeded then
print('> refuel needed (minimum is ' .. minFuelNeeded .. ')')
local refuelOk, refuelErr = turtleUtils.refuelWithBuffer('bottom', 'front', minFuelNeeded, IDLE_TIME)
if not refuelOk then
error('Cannot refuel the turtle: "' .. refuelErr .. '"')
end
print('> fuel: ' .. tostring(fuelLevel))
end
end
local function goToHarvestPoint(config)
turtle.turnLeft()
for i=1, config.firstCropZ, 1 do
turtle.forward()
end
end
local function goBackToHome(config)
for i=1, config.firstCropZ, 1 do
turtle.forward()
end
turtle.turnLeft()
end
local function compactIfNeeded()
if turtleUtils.countFreeSlots() < MIN_FREE_SLOTS_BEFORE_COMPACT then
turtleUtils.compactInventory()
end
end
local function applyFertilizedEssenceIfAny()
if turtleUtils.selectItemByName(inferium.FERTILIZED_ESSENCE) then
while turtle.placeDown() do end
end
end
local function harvestDown(index, config)
if config.fertilizedBoost then
applyFertilizedEssenceIfAny()
end
local cropName = turtleUtils.waitForMatureCrop(turtle.inspectDown, IDLE_TIME)
local seedName = getSeedNameFromCropName(cropName)
localPlan[index] = seedName
if not seedName then
return false
end
if not turtle.digDown() then
error('turtle cannot harvest crop')
end
if not turtleUtils.selectItemByName(seedName) then
error('turtle cannot find any crop to place')
end
if not turtle.placeDown() then
error('turtle cannot place crop')
end
compactIfNeeded()
return true
end
local function forward()
local blocked = false
while not turtle.forward() do
if not blocked then
blocked = true
print('> turtle is blocked')
end
end
if blocked then
print('turtle is unblocked')
end
end
local function harvestProcedure(config)
goToHarvestPoint(config)
term.write('> harvest on ' .. tostring(config.length) .. ' blocks ')
local nbHarvested = 0
for i=1, config.length, 1 do
if harvestDown(i, config) then
nbHarvested = nbHarvested + 1
term.write('.')
else
term.write('!')
end
if i ~= config.length then
forward()
end
end
print()
print('> ' .. tostring(nbHarvested) .. ' harvested crops')
turtle.turnLeft()
turtle.turnLeft()
for i=1, config.length, 1 do
if i ~= config.length then
forward()
end
end
goBackToHome(config)
end
local function retrieveLocalPlan(config)
goToHarvestPoint(config)
print('> retrieve the local plan')
localPlan = {}
for i=1, config.length, 1 do
local ok, block = turtle.inspectDown()
local blockName = block and block.name
localPlan[i] = getSeedNameFromCropName(blockName)
if i ~= config.length then
forward()
end
end
turtle.turnLeft()
turtle.turnLeft()
for i=1, config.length, 1 do
if i ~= config.length then
forward()
end
end
goBackToHome(config)
end
local function fetchRemoteConfig(serverName)
print('> fetch remote config')
local message = { type = 'register-and-get-config' }
local lastErrMessage = nil
while true do
local replyMessage, errMessage = net.sendQuery(serverName, message, NETWORK_TIMEOUT)
if replyMessage and replyMessage.payload then
previouslyFetchedRemoteConfig = replyMessage.payload
print('> config fetched')
local payload = replyMessage.payload
if not payload then
error('cannot fetch the remote config')
end
if not payload.plan then
error('no "plan" property found in remote config')
end
return payload
elseif previouslyFetchedRemoteConfig then
print('> failed to fetch: ' .. tostring(errMessage))
print('> use the previous recorded remote config instead')
return previouslyFetchedRemoteConfig
elseif not replyMessage and errMessage then
if lastErrMessage ~= errMessage then
lastErrMessage = errMessage
print('> failed to fetch: ' .. tostring(errMessage))
end
else
error('unknown error during fetch')
end
os.sleep(IDLE_TIME)
end
end
local function removeSeeds(seeds, config)
goToHarvestPoint(config)
local nbOfSeedsToRemove = utils.sizeof(seeds)
print('> remove ' .. nbOfSeedsToRemove .. ' seed(s)')
local stateSeeds = seeds -- warning: do not mutate the data (only the ref)
local nbBlockTraveled = 0
for i=1, config.length, 1 do
local ok, block = turtle.inspectDown()
local blockName = block and block.name
local found = utils.find(stateSeeds, function(seedName) return seedName == getSeedNameFromCropName(blockName) end)
if found then
local digOk = turtle.digDown()
compactIfNeeded()
if not digOk then
error('cannot remove seed')
end
stateSeeds = removeFirst(stateSeeds, found)
nbOfSeedsToRemove = nbOfSeedsToRemove - 1
if nbOfSeedsToRemove < 1 then
break
end
end
if i ~= config.length then
forward()
nbBlockTraveled = nbBlockTraveled + 1
end
end
turtle.turnLeft()
turtle.turnLeft()
for i=1, nbBlockTraveled, 1 do
forward()
end
goBackToHome(config)
dropAllProcedure(config)
end
local function retrieveSeeds(seeds)
print('> retrieve seeds from storage')
local seedRetrieved = false
local seedsCount = getIndexedCount(seeds)
local storageInventory = turtleUtils.waitForInventory('bottom', WAIT_ITEM_IDLE_TIME)
local bufferInventory = turtleUtils.waitForInventory('front', WAIT_ITEM_IDLE_TIME)
for seedName, count in pairs(seedsCount) do
local slot = getItemSlot(storageInventory, seedName)
if slot and count > 0 then
local inventoryCount = getCountItem(storageInventory, seedName)
local realCount = math.min(count, inventoryCount)
local pushOk = storageInventory.pushItems(peripheral.getName(bufferInventory), slot, realCount)
if not pushOk then
error('retrieveSeeds error: cannot pushItems from storage to buffer')
end
local suckOk = turtle.suck()
if not suckOk then
error('retrieveSeeds error: cannot suck items from buffer')
end
seedRetrieved = true
end
end
return seedRetrieved
end
-- first parameter is config
local function retrieveFertilizedEssences(_)
local storageInventory = turtleUtils.waitForInventory('bottom', WAIT_ITEM_IDLE_TIME)
local bufferInventory = turtleUtils.waitForInventory('front', WAIT_ITEM_IDLE_TIME)
local slot = getItemSlot(storageInventory, inferium.FERTILIZED_ESSENCE)
if slot then
local inventoryCount = getCountItem(storageInventory, inferium.FERTILIZED_ESSENCE) or 0
local realCount = math.min(MAX_FERTILIZED_ESSENCE, inventoryCount)
print('> retrieve ' .. tostring(realCount) .. ' fertilized essences from storage')
local pushOk = storageInventory.pushItems(peripheral.getName(bufferInventory), slot, realCount)
if not pushOk then
return false
end
local suckOk = turtle.suck()
if not suckOk then
return false
end
return true
end
return false
end
local function replantSeeds(config)
goToHarvestPoint(config)
print('> replant seeds')
local nbBlockTraveled = 0
for i=1, config.length, 1 do
local ok, block = turtle.inspectDown()
if not ok then
turtleUtils.selectItemBy(turtleUtils.isSeedInSlot)
turtle.placeDown()
if turtleUtils.isInventoryEmpty() then
break
end
end
if i ~= config.length then
forward()
nbBlockTraveled = nbBlockTraveled + 1
end
end
turtle.turnLeft()
turtle.turnLeft()
for i=1, nbBlockTraveled, 1 do
forward()
end
goBackToHome(config)
end
local function replantProcedure(config)
local remotePlan = config.plan
if localPlan == nil then
retrieveLocalPlan(config)
end
if localPlan == nil then
error('cannot retrieve the local plan')
end
local seedsToRemove = utils.shallowDiff(localPlan, remotePlan)
local seedsToPlant = utils.shallowDiff(remotePlan, localPlan)
if utils.sizeof(seedsToRemove) > 0 then
removeSeeds(seedsToRemove, config)
end
if utils.sizeof(seedsToPlant) > 0 then
if retrieveSeeds(seedsToPlant) then
replantSeeds(config)
end
end
-- reset the local plan (it will be re-updated during the harvest procedure)
localPlan = {}
end
local function resetTerminal()
term.clear()
term.setCursorPos(1, 1)
end
-- Main procedure
local function main()
net.openRednet()
print("Starting Trap's inferium harvester v" .. VERSION)
if not hasEnoughInitialFuel() then
turtleUtils.refuelAllFromInventory()
end
if not hasEnoughInitialFuel() then
error('Not enough fuel', 0)
end
print('Starting harvester in ' .. TIME_TO_START .. ' seconds...')
os.sleep(TIME_TO_START)
retrieveHomePositionProcedure()
local cycleNumber = 1
while true do
resetTerminal()
print('> Starting cycle ' .. tostring(cycleNumber))
print()
local remoteConfig = fetchRemoteConfig(inferium.SERVER)
dropAllProcedure(remoteConfig)
refuelProcedure(remoteConfig)
replantProcedure(remoteConfig)
if remoteConfig.fertilizedBoost then
print('> fertilized boost enabled')
retrieveFertilizedEssences(remoteConfig)
end
harvestProcedure(remoteConfig)
cycleNumber = cycleNumber + 1
end
net.closeRednet()
end
main()

View File

@ -1,271 +0,0 @@
local net = require('libs/net')
local utils = require('libs/utils')
local inferium = require('libs/inferium')
local inferiumConfig = require('config/inferium')
local DEFAULT_HARVESTER_NAME = 'harvester'
local VERSION = "2.1.1"
local PERSISTED_CONFIGS = '/data/inferium-configs'
local UPGRADE_SCRIPT = '/upgrade.lua'
local defaultConfig = inferiumConfig.defaultConfig or error('no default config provided in config', 0)
local defaultAvailableSeeds = {
inferium.formatSeedName('inferium'),
inferium.formatSeedName('coal')
}
-- Persistance utils
local function stringifyConfigs(configs)
local stringifiedConfigs = {}
for k, v in pairs(configs) do
stringifiedConfigs[k] = textutils.serialize(v)
end
return textutils.serialize(stringifiedConfigs)
end
local function parseConfigs(rawConfigs)
local configs = {}
local stringifiedConfigs = textutils.unserialize(rawConfigs)
for k, v in pairs(stringifiedConfigs) do
configs[k] = textutils.unserialize(v)
end
return configs
end
local savePersistedConfigs = function(configs)
local rawConfigs = nil
local ok, errMessage = pcall(function() rawConfigs = stringifyConfigs(configs) end)
if not rawConfigs or not ok then
error('savePersistedConfigs stringify error: ' .. tostring(errMessage))
end
local file = fs.open(PERSISTED_CONFIGS, 'w')
if not file then
error('savePersistedConfigs: cannot open ' .. PERSISTED_CONFIGS .. ' file!')
end
file.write(rawConfigs)
file.close()
end
local readPersistedConfigs = function()
local file = fs.open(PERSISTED_CONFIGS, 'r')
if not file then
local initialConfig = {}
savePersistedConfigs(initialConfig)
return initialConfig
end
local rawConfigs = file.readAll()
file.close()
return parseConfigs(rawConfigs)
end
-- State
local LOCAL_CONFIGS = readPersistedConfigs()
local function getConfigForComputer(computerId)
return LOCAL_CONFIGS[tostring(computerId)]
end
local function saveConfigForComputer(computerId, config)
LOCAL_CONFIGS[tostring(computerId)] = config
savePersistedConfigs(LOCAL_CONFIGS)
end
-- Utils
local function getConfigWithLength(config)
if not config then
return config
end
local configWithLength = utils.shallowClone(config)
configWithLength.length = utils.sizeof(config.plan)
return configWithLength
end
local function isValidConfig(config)
if not config or type(config.plan) ~= 'table' then
return false
end
if config.firstCropZ == nil or config.firstCropZ < 1 then
return false
end
if config.fertilizedBoost == nil then
return false
end
return true
end
-- Routes
local function getConfig(message)
local id = message and message.payload and message.payload.id
if not id then
return {
type = "get-config/error",
payload = nil,
errorMessage = "no id provided in message payload"
}
end
local config = getConfigForComputer(id)
if not config then
return {
type = "get-config/error",
payload = nil,
errorMessage = "cannot retrieve configuration for given id"
}
end
return {
type = "get-config/response",
payload = getConfigWithLength(config)
}
end
local function registerAndGetConfig(_, computerId)
if not computerId == nil then
print('get-config error: no computerId found')
return nil
end
local config = getConfigForComputer(computerId)
if not config then
print('new harvester detected: ' .. tostring(computerId))
saveConfigForComputer(computerId, defaultConfig)
config = defaultConfig
end
return {
type = "register-and-get-config/response",
payload = getConfigWithLength(config)
}
end
local function setConfig(message)
local payload = message and message.payload
local harvesterId = payload and payload.id
local config = payload and payload.config
if not isValidConfig(config) or not harvesterId then
return false
end
saveConfigForComputer(harvesterId, config)
return true
end
local function deleteConfig(message)
local payload = message and message.payload
local harvesterId = payload and payload.id
if not harvesterId then
return false
end
if LOCAL_CONFIGS[tostring(harvesterId)] then
saveConfigForComputer(harvesterId, nil)
return true
end
return false
end
-- return a table of harvesters { id: string, name: string }[]
local function listHarvesters()
local result = {}
for k, v in pairs(LOCAL_CONFIGS) do
local harvester = {
id = k,
name = v.name or DEFAULT_HARVESTER_NAME
}
table.insert(result, harvester)
end
return result
end
local function listAvailableSeeds()
local inv = peripheral.find('inventory')
if not inv then
print('warning: no inventory found for the inferium server')
return defaultAvailableSeeds
end
local seeds = {}
for _, item in pairs(inv.list()) do
if inferium.isMysticalSeed(item.name) then
table.insert(seeds, item.name)
end
end
return seeds
end
local function exitServer(_, _, stopServer)
stopServer();
return true
end
local function upgradeServer()
shell.execute(UPGRADE_SCRIPT)
return true
end
local ROUTES = {
['register-and-get-config'] = registerAndGetConfig,
['get-config'] = getConfig,
['set-config'] = setConfig,
['delete-config'] = deleteConfig,
['list-harvesters'] = listHarvesters,
['list-seeds'] = listAvailableSeeds,
['exit-server'] = exitServer,
['upgrade-server'] = upgradeServer,
}
net.openRednet()
print('> inferium server v' .. VERSION .. ' started')
net.listenQuery(inferium.SERVER, function(message, computerId, stopServer)
if type(message) ~= 'table' then
print('error: malformed message received', textutils.serialize(message))
return {}
end
local router = ROUTES[message.type]
if not router then
print('warning: unknown type of message received', message.type)
return {}
end
return router(message, computerId, stopServer)
end)
print('> server stopped')
net.closeRednet()
print('> rednet closed')

View File

@ -1,121 +1,28 @@
local LIST_TURTLE_FILES = {
'refuel.lua',
'miner.lua',
'tunnels-miner.lua',
'coal-crafter.lua',
'mystical-upgrader.lua',
'inferium-harvester.lua',
'inferium-server.lua'
local LIST_FILES = {
'turtle-utils.lua',
'robot.lua',
'miner.lua'
};
local LIST_TURTLE_CONFIG_FILES = {
'config/tunnels.lua',
'config/mining.lua'
local LIST_CONFIG_FILES = {
'config-miner.lua'
}
local LIST_SERVER_FILES = {
'light-server.lua',
'inferium-server.lua',
'notify-server.lua'
}
local LIST_SERVER_CONFIG_FILES = {
'config/inferium.lua'
}
local LIST_CLIENT_FILES = {
'inferium-gui.lua'
}
local LIST_CLIENT_CONFIG_FILES = {}
local LIST_LIBS_FILES = {
'libs/rs.lua',
'libs/net.lua',
'libs/utils.lua',
'libs/turtle-utils.lua',
'libs/robot.lua',
'libs/inferium.lua',
'libs/notify.lua',
'libs/ui/CountersSelector.lua'
}
local LIST_GLOBAL_FILES = {
'dmesg.lua'
}
local LIST_GLOBAL_CONFIG_FILES = {
'startup.lua',
'upgrade.lua'
}
-- old files that need to be cleaned up
local LIST_OLD_FILES = {
'inferium-upgrader-lua',
'config/inferium-plans.lua',
'config/harvesting.lua'
}
local REPO_PREFIX = 'https://git.trapcloud.fr/guillaumearm/minecraft-cc-tools/raw/branch/master/'
local removeFiles = function(list)
for _, filePath in pairs(list) do
if filePath then
fs.delete(filePath)
end
local previousDir = shell.dir()
for _, filePath in pairs(LIST_FILES) do
fs.delete(filePath)
shell.execute('wget', REPO_PREFIX .. filePath, filePath)
end
-- do not override existing config files
for _, filePath in pairs(LIST_CONFIG_FILES) do
if not fs.exists(filePath) then
shell.execute('wget', REPO_PREFIX .. filePath, filePath)
end
end
local installFiles = function(list)
for _, filePath in pairs(list) do
if filePath then
fs.delete(filePath)
shell.execute('wget', REPO_PREFIX .. filePath, filePath)
end
end
end
local installConfig = function(list)
-- do not override existing config files
for _, filePath in pairs(list) do
if filePath and not fs.exists(filePath) then
shell.execute('wget', REPO_PREFIX .. filePath, filePath)
end
end
end
local prepareDirs = function()
fs.makeDir('/libs')
fs.makeDir('/libs/ui')
fs.makeDir('/config')
fs.makeDir('/data')
end
local mainSetup = function()
local previousDir = shell.dir()
prepareDirs()
removeFiles(LIST_OLD_FILES)
installFiles(LIST_LIBS_FILES)
if turtle then
installFiles(LIST_TURTLE_FILES)
installConfig(LIST_TURTLE_CONFIG_FILES)
elseif pocket then
installFiles(LIST_CLIENT_FILES)
installConfig(LIST_CLIENT_CONFIG_FILES)
else -- regular computer
installFiles(LIST_CLIENT_FILES)
installConfig(LIST_CLIENT_CONFIG_FILES)
installFiles(LIST_SERVER_FILES)
installConfig(LIST_SERVER_CONFIG_FILES)
end
installFiles(LIST_GLOBAL_FILES)
installConfig(LIST_GLOBAL_CONFIG_FILES)
shell.setDir(previousDir)
end
mainSetup()
shell.setDir(previousDir)

View File

@ -1,42 +0,0 @@
local inferium = {}
inferium.SERVER = 'inferium.com'
inferium.FERTILIZED_ESSENCE = 'mysticalagriculture:fertilized_essence'
inferium.formatSeedName = function(essenceName)
if not essenceName then
return false
end
return 'mysticalagriculture:' .. essenceName .. '_seeds'
end
inferium.parseSeedName = function(seedName)
if not seedName then
return nil
end
local result, nbReplaced = string.gsub(seedName, 'mysticalagriculture:', '')
if nbReplaced == 0 then
return nil
end
local finalResult, nbFinalReplaced = string.gsub(result, '_seeds', '')
if nbFinalReplaced == 0 then
return nil
end
return finalResult
end
inferium.isMysticalSeed = function(seedName)
if inferium.parseSeedName(seedName) then
return true
end
return false
end
return inferium

View File

@ -1,72 +0,0 @@
local DEFAULT_TIMEOUT = 2
local QUERY_PROTO = 'trap/query'
local QUERY_RESPONSE_PROTO = 'trap/query:response'
local serializeOpts = {
compact = true
}
local net = {}
local function assertRednetIsOpened()
if not rednet.isOpen() then
error('rednet should be enabled with rednet.open(modemName)')
end
end
net.openRednet = function(modem)
modem = modem or peripheral.find("modem") or error("No modem attached", 0)
return rednet.open(peripheral.getName(modem))
end
net.closeRednet = function(modem)
modem = modem or peripheral.find("modem") or error("No modem attached", 0)
return rednet.close(peripheral.getName(modem))
end
net.listenQuery = function(hostname, processQueryMessage)
assertRednetIsOpened()
local serverRunning = true
local function stopServer()
serverRunning = false
end
rednet.host(QUERY_PROTO, hostname)
while serverRunning do
local computerId, message = rednet.receive(QUERY_PROTO)
local responseMessage = processQueryMessage(textutils.unserialize(message), computerId, stopServer)
rednet.send(computerId, textutils.serialize(responseMessage, serializeOpts), QUERY_RESPONSE_PROTO)
end
rednet.unhost(QUERY_PROTO)
end
net.sendQuery = function(hostname, message, timeout)
timeout = timeout or DEFAULT_TIMEOUT
assertRednetIsOpened()
local serverId = rednet.lookup(QUERY_PROTO, hostname)
if not serverId then
return nil, 'hostname lookup error'
end
local sendOk = rednet.send(serverId, textutils.serialize(message, serializeOpts), QUERY_PROTO)
if not sendOk then
return nil, 'rednet error'
end
local responseServerId, responseMessage = rednet.receive(QUERY_RESPONSE_PROTO, timeout)
if not responseServerId then
return nil, 'timeout'
end
return textutils.unserialize(responseMessage)
end
return net

View File

@ -1,21 +0,0 @@
local net = require('libs/net')
local notify = {}
local DEFAULT_PREFIX = 'chatBox'
notify.SERVER = 'notify.com'
notify.sendChatMessage = function(message, prefix)
prefix = prefix or os.getComputerLabel() or DEFAULT_PREFIX
return net.sendQuery(notify.SERVER, {
type = 'notify-chat',
payload = {
prefix = prefix,
message = message
}
})
end
return notify

View File

@ -1,231 +0,0 @@
-- robot api
local api = {}
local ROBOT_DEFAULT_STATE_FILE = '/.robotstate'
local DEFAULT_CONFIG = {
autosave = false,
autoload = false,
stateFilePath = ROBOT_DEFAULT_STATE_FILE
}
local function identity(x)
return x
end
local function getConfig(givenConfig)
if givenConfig.onSave and not givenConfig.onLoad then
error('robot.getConfig: custom "onSave" passed in config but no "onLoad"')
end
if givenConfig.onLoad and not givenConfig.onSave then
error('robot.getConfig: custom "onLoad" passed in config but no "onSave"')
end
return {
autosave = givenConfig.autosave or DEFAULT_CONFIG.autosave,
autoload = givenConfig.autoload or DEFAULT_CONFIG.autoload,
stateFilePath = givenConfig.stateFilePath or DEFAULT_CONFIG.stateFilePath,
onSave = givenConfig.onSave or identity,
onLoad = givenConfig.onLoad or identity
}
end
local function createDefaultState()
return {
y = 0,
x = 0,
z = 0,
dir = 'FORWARD' -- FORWARD | BACKWARD | LEFT | RIGHT
}
end
local saveRobotState = function(stateFile, onSave, state)
state = state or createDefaultState()
local file = fs.open(stateFile, 'w')
if not file then
error('saveRobotState: cannot open ' .. stateFile .. ' file!')
end
local transformedState = onSave(state)
file.write(textutils.serializeJSON(transformedState))
file.close()
return transformedState
end
local loadRobotState = function(stateFile, onLoad, onSave)
local file = fs.open(stateFile, 'r')
if not file then
return robot.saveRobotState(stateFile, onSave)
end
local serializedRobotState = file.readAll()
file.close()
local parsedResult = textutils.unserializeJSON(serializedRobotState)
if not parsedResult then
return robot.saveRobotState(stateFile, onSave)
end
return parsedResult, onLoad(parsedResult)
end
local getAutoSave = function(config, state)
local autoSave = function() end
if config.autosave then
autoSave = function()
saveRobotState(config.stateFilePath, config.onSave, state)
end
end
return autoSave
end
api.create = function(state, givenConfig)
state = state or createDefaultState()
local config = getConfig(givenConfig)
if config.autoload then
local originalState = loadRobotState(config.stateFilePath, config.onLoad, config.onSave)
state = originalState
end
local autoSave = getAutoSave(config, state)
local mutateRobotPosition = function(isBackward)
local incValue = 1
if isBackward then incValue = -1 end
if state.dir == 'FORWARD' then
state.z = state.z + incValue
elseif state.dir == 'BACKWARD' then
state.z = state.z - incValue
elseif state.dir == 'LEFT' then
state.x = state.x - incValue
elseif state.dir == 'RIGHT' then
state.x = state.x + incValue
end
end
local robot = {}
robot.forward = function()
local ok, err = turtle.forward()
if ok then
mutateRobotPosition(false)
autoSave()
end
return ok, err
end
robot.back = function()
local ok, err = turtle.back()
if ok then
mutateRobotPosition(true)
autoSave()
end
return ok, err
end
robot.backward = robot.back
robot.up = function()
local ok, err = turtle.up()
if ok then
state.y = state.y + 1
autoSave()
end
return ok, err
end
robot.down = function()
local ok, err = turtle.down()
if ok then
state.y = state.y - 1
autoSave()
end
return ok, err
end
robot.turnLeft = function()
local ok, err = turtle.turnLeft()
if ok then
if state.dir == 'FORWARD' then
state.dir = 'LEFT'
elseif state.dir == 'LEFT' then
state.dir = 'BACKWARD'
elseif state.dir == 'RIGHT' then
state.dir = 'FORWARD'
elseif state.dir == 'BACKWARD' then
state.dir = 'RIGHT'
end
autoSave()
end
return ok, err
end
robot.turnRight = function()
local ok, err = turtle.turnRight()
if ok then
if state.dir == 'FORWARD' then
state.dir = 'RIGHT'
elseif state.dir == 'LEFT' then
state.dir = 'FORWARD'
elseif state.dir == 'RIGHT' then
state.dir = 'BACKWARD'
elseif state.dir == 'BACKWARD' then
state.dir = 'LEFT'
end
autoSave()
end
return ok, err
end
robot.turnBack = function()
local ok, errorMessage = robot.turnLeft()
if not ok then
error('turnBack: cannot turn left because ' .. tostring(errorMessage))
end
return robot.turnLeft()
end
robot.getState = function()
return state
end
robot.saveState = function()
return saveRobotState(config.stateFilePath, config.onSave, state)
end
robot.loadState = function()
local originalState, transformedState = loadRobotState(config.stateFilePath, config.onLoad, config.onSave)
state = originalState
return transformedState
end
return robot
end
return api

View File

@ -1,68 +0,0 @@
local rs = {}
rs.listenInput = function(side, onChangeCallback)
local state = redstone.getInput(side)
while true do
os.pullEvent('redstone')
local newState = redstone.getInput(side)
if state ~= newState then
state = newState
onChangeCallback(newState)
end
end
end
rs.createBundledOutput = function(side, initialColorState)
local bundledOutput = {}
local _colorState = initialColorState or 0
local function getState()
return _colorState
end
local function setState(newColorState)
if newColorState == _colorState then
return false
end
_colorState = newColorState
redstone.setBundledOutput(side, newColorState)
return true
end
bundledOutput.getStateColor = getState
bundledOutput.setColorState = setState
bundledOutput.test = function(color)
return colors.test(getState(), color)
end
bundledOutput.setOn = function(color)
local colorState = getState()
local newColorState = colors.combine(colorState, color)
return setState(newColorState)
end
bundledOutput.setOff = function(color)
local colorState = getState()
local newColorState = colors.subtract(colorState, color)
return setState(newColorState)
end
bundledOutput.toggle = function(color)
if colors.test(getState(), color) then
return bundledOutput.setOff(color)
end
return bundledOutput.setOn(color)
end
return bundledOutput
end
return rs

View File

@ -1,486 +0,0 @@
local turtleUtils = {}
local DEFAULT_IDLE_TIME = 2
local DEFAULT_MIN_FUEL_NEEDED = 1000
local function noop() end
local function isTurtleInventoryFull()
for i=1, 16, 1 do
if turtle.getItemCount(i) == 0 then
return false
end
end
return true
end
turtleUtils.isFuelItem = function(item)
item = item or {}
if item.name == 'minecraft:stick' or item.name == 'minecraft:coal_coke_block' or item.name == 'immersiveengineering:coke' or item.name == 'silentgear:netherwood_charcoal' or item.name == 'silentgear:netherwood_charcoal_block' then
return true
end
local tags = item.tags or {}
return tags['minecraft:coals'] or tags['minecraft:logs'] or tags['minecraft:planks'] or tags['minecraft:saplings'] or false
end
turtleUtils.isSeedInSlot = function(slot)
local item = turtle.getItemDetail(slot, true)
local tags = item and item.tags or {}
return tags['forge:seeds'] or tags['mysticalagriculture:seeds'] or false
end
turtleUtils.getInventory = function(side)
side = side or 'front'
local inv = peripheral.wrap(side)
if inv and peripheral.hasType(inv, 'inventory') then
return inv
end
return nil
end
turtleUtils.isInventoryEmpty = function()
for i=1, 16, 1 do
if turtle.getItemCount(i) > 0 then
return false
end
end
return true
end
turtleUtils.isInventoryFull = function(withCompact)
local isFull = isTurtleInventoryFull()
if isFull and withCompact then
turtleUtils.compactInventory()
return isTurtleInventoryFull()
end
return isFull
end
turtleUtils.getItemName = function(slotIndex)
local item = turtle.getItemDetail(slotIndex)
return item and item.name
end
turtleUtils.getItemCount = function(slotIndex)
return turtle.getItemCount(slotIndex)
end
local function waitFor(predicate, sleepTime)
sleepTime = sleepTime or DEFAULT_IDLE_TIME
while true do
local result = predicate()
if result ~= nil and result ~= false then
return result
end
os.sleep(sleepTime)
end
end
turtleUtils.waitForInventory = function(side, sleepTime, onWaitCb)
onWaitCb = onWaitCb or noop
local counter = 0
return waitFor(function()
local res = turtleUtils.getInventory(side)
counter = counter + 1
if counter == 2 then
onWaitCb()
end
return res
end, sleepTime)
end
turtleUtils.waitForMatureCrop = function(inspectFn, sleepTime)
inspectFn = inspectFn or turtle.inspect
if not inspectFn() then
return nil
end
return waitFor(function()
return turtleUtils.getMatureCrop(inspectFn)
end, sleepTime)
end
turtleUtils.trySuck = function(suckFn, sleepTime)
suckFn = suckFn or turtle.suck
sleepTime = sleepTime or DEFAULT_IDLE_TIME
while not suckFn() do
os.sleep(sleepTime)
end
end
turtleUtils.trySuckUp = function()
return turtleUtils.trySuck(turtle.suckUp)
end
turtleUtils.trySuckDown = function()
return turtleUtils.trySuck(turtle.suckDown)
end
turtleUtils.tryDrop = function(dropFn, sleepTime)
dropFn = dropFn or turtle.drop
sleepTime = sleepTime or DEFAULT_IDLE_TIME
while true do
if turtle.getItemCount() == 0 then
return false
end
local dropOk = dropFn();
if (dropOk) then
return true
end
os.sleep(sleepTime)
end
end
turtleUtils.tryDropUp = function()
return turtleUtils.tryDrop(turtle.dropUp)
end
turtleUtils.tryDropDown = function()
return turtleUtils.tryDrop(turtle.dropDown)
end
turtleUtils.dropSlot = function(slotIndex, dropFn)
dropFn = dropFn or turtle.drop
if turtle.getItemCount(slotIndex) == 0 then
return false
end
if turtle.getSelectedSlot() ~= slotIndex then
turtle.select(slotIndex)
end
local ok, err = dropFn()
if not ok then
return false
end
return true
end
turtleUtils.digAll = function()
while true do
local ok, err = turtle.dig()
if not ok then
return ok, err
end
end
end
turtleUtils.digAllUp = function()
while true do
local ok, err = turtle.digUp()
if not ok then
return ok, err
end
end
end
turtleUtils.digAllDown = function()
while true do
local ok, err = turtle.digDown()
if not ok then
return ok, err
end
end
end
turtleUtils.countFreeSlots = function()
local freeSlots = 0
for i=1, 16, 1 do
if turtle.getItemCount(i) == 0 then
freeSlots = freeSlots + 1
end
end
return freeSlots
end
turtleUtils.getFuelPercentage = function(fuelLevel)
fuelLevel = fuelLevel or turtle.getFuelLevel()
local rawPercentage = (fuelLevel / turtle.getFuelLimit()) * 100
return math.floor(rawPercentage * 100) / 100
end
turtleUtils.getMatureCrop = function(inspectFn)
inspectFn = inspectFn or turtle.inspect
local isBlock, block = inspectFn()
local blockStateAge = block and block.state and block.state.age
if blockStateAge == 7 then
return block.name
end
return nil
end
turtleUtils.selectFirstEmptySlot = function()
for i = 1, 16, 1 do
if turtle.getItemCount(i) == 0 then
turtle.select(i)
return true
end
end
return false
end
turtleUtils.selectItemByName = function(itemName)
for i = 1, 16, 1 do
if itemName == turtleUtils.getItemName(i) then
turtle.select(i)
return true
end
end
return false
end
turtleUtils.selectItemBy = function(predicate)
for i = 1, 16, 1 do
if predicate(i) then
turtle.select(i)
return true
end
end
return false
end
turtleUtils.ensureSelected = function(slot)
if turtle.getSelectedSlot() ~= slot then
return turtle.select(slot)
end
return true
end
turtleUtils.goGround = function()
while true do
if turtle.inspectDown() then
return true
end
local ok, errorMessage = turtle.down()
if not ok then
return false, errorMessage
end
end
end
turtleUtils.forceForward = function()
local ok = turtle.forward()
if ok then
return ok
end
turtleUtils.digAll()
return turtle.forward()
end
turtleUtils.forceDown = function()
local ok = turtle.down()
if ok then
return ok
end
turtleUtils.digAllDown()
return turtle.down()
end
turtleUtils.forceUp = function()
local ok = turtle.up()
if ok then
return ok
end
turtleUtils.digAllUp()
return turtle.up()
end
turtleUtils.refuel = function(minFuel, suckFn, sleepTime)
suckFn = suckFn or turtle.suck
sleepTime = sleepTime or DEFAULT_IDLE_TIME
if not turtleUtils.selectFirstEmptySlot() then
return false, 'turtle inventory is full'
end
while turtle.getFuelLevel() < minFuel do
local suckRes = suckFn()
if suckRes then
local ok = turtle.refuel()
if not ok then
return false, 'selected item is not considered as fuel'
end
else
os.sleep(sleepTime)
end
end
return true
end
turtleUtils.refuelAllFromInventory = function()
local initialSelectedSlot = turtle.getSelectedSlot()
for i=1, 16, 1 do
if turtle.getItemCount(i) > 0 then
turtle.select(i)
turtle.refuel()
end
end
if turtle.getSelectedSlot() ~= initialSelectedSlot then
turtle.select(initialSelectedSlot)
end
end
-- utils functions used by "compactInventory"
local function getItemsMap()
local result = {}
for i=1, 16, 1 do
local itemName = turtleUtils.getItemName(i)
if itemName then
local nbSlotUsed = result[itemName] or 0
result[itemName] = nbSlotUsed + 1
end
end
return result
end
local function getSlotsWithSpaces(itemName)
local slots = {}
for i=1, 16, 1 do
if turtleUtils.getItemName(i) == itemName then
if turtle.getItemSpace(i) > 0 then
table.insert(slots, i)
end
end
end
return slots
end
local function compactItem(itemName)
while true do
local slots = getSlotsWithSpaces(itemName)
if #slots < 2 then
break
end
turtle.select(slots[1])
turtle.transferTo(slots[2], turtle.getItemSpace(slots[2]))
end
end
-- Compact the internal turtle inventory (return the number of freed slots)
turtleUtils.compactInventory = function()
local initialNbFreeSlots = turtleUtils.countFreeSlots()
local initialSelectedSlot = turtle.getSelectedSlot()
local itemsMap = getItemsMap()
for itemName, nbSlotUsed in pairs(itemsMap) do
if nbSlotUsed > 1 then
compactItem(itemName)
end
end
if initialSelectedSlot ~= turtle.getSelectedSlot() then
turtle.select(initialSelectedSlot)
end
return turtleUtils.countFreeSlots() - initialNbFreeSlots
end
-- utils functions used by "refuelWithBuffer"
local function getSuckFn(side)
if side == 'front' then
return turtle.suck
elseif side == 'top' then
return turtle.suckUp
elseif side == 'bottom' then
return turtle.suckDown
end
end
local function findFuelSlot(inventory)
for slot in pairs(inventory.list()) do
local item = inventory.getItemDetail(slot)
if item and turtleUtils.isFuelItem(item) then
return slot
end
end
end
-- Refuel using a buffer chest
turtleUtils.refuelWithBuffer = function(sideFuel, sideBuffer, minFuelNeeded, sleepTime)
sleepTime = sleepTime or DEFAULT_IDLE_TIME
minFuelNeeded = minFuelNeeded or DEFAULT_MIN_FUEL_NEEDED
local suckFn = getSuckFn(sideBuffer)
if not suckFn then
return false, 'refuelWithBuffer: sideBuffer should be "front", "top" or "bottom"'
end
local fuelInventory = turtleUtils.waitForInventory(sideFuel, sleepTime)
local bufferInventory = turtleUtils.waitForInventory(sideBuffer, sleepTime)
local ok, errMessage = turtleUtils.refuel(minFuelNeeded, function()
local fuelSlot = findFuelSlot(fuelInventory)
if fuelSlot ~= nil then
fuelInventory.pushItems(peripheral.getName(bufferInventory), fuelSlot)
end
return suckFn()
end, sleepTime)
return ok, errMessage
end
return turtleUtils

View File

@ -1,277 +0,0 @@
local utils = require('libs/utils')
local function noTitleFn()
return ""
end
local function withColor(win, textColor, backgroundColor, callbackFn)
local originalTextColor = nil
local originalBackgroundColor = nil
if textColor then
originalTextColor = win.getTextColor()
win.setTextColor(textColor)
end
if backgroundColor then
originalBackgroundColor = win.getBackgroundColor()
win.setBackgroundColor(backgroundColor)
end
local result = table.pack(callbackFn(win))
if originalTextColor then
win.setTextColor(originalTextColor)
end
if originalBackgroundColor then
win.setBackgroundColor(originalBackgroundColor)
end
return table.unpack(result)
end
-- local function createWriteWithColor(textColor, backgroundColor, givenWin)
-- local win = givenWin or term
-- return function(str)
-- return withColor(win, textColor, backgroundColor, function()
-- return win.write(str)
-- end)
-- end
-- end
local function getTotalCount(countersMap)
local total = 0
for _, counterPayload in pairs(countersMap) do
if counterPayload then
total = total + (counterPayload.count or 0)
end
end
return total
end
local function dropN(t, n)
local result = {}
local i = 1
for _, v in pairs(t) do
if n == 0 then
result[i] = v
i = i + 1
else
n = n - 1
end
end
return result
end
local function takeN(t, n)
local result = {}
for k,v in pairs(t) do
if n == 0 then
break
end
result[k] = v
n = n - 1
end
return result
end
local function resetAllCounters(countersMap)
local result = {}
for k, counterPayload in pairs(countersMap) do
result[k] = {
name = counterPayload.name,
count = 0
}
end
return result
end
local function renderCountersMap(win, countersMap, selectedCounter)
win.clear()
win.setCursorPos(1, 1)
local _, height = win.getSize()
-- local nbCounters = utils.sizeof(countersMap)
-- local totalCount = getTotalCount(countersMap)
local selectedPage = math.floor((selectedCounter - 1) / height) + 1
-- local totalPages = (nbCounters % height) + 1
local nbElementsToOmit = (selectedPage - 1) * height
local displayedCounters = takeN(dropN(countersMap, nbElementsToOmit), height)
local cursorYIndex = 1
for k,v in pairs(displayedCounters) do
win.setCursorPos(1, cursorYIndex)
local writeLine = function()
win.write(tostring(v.name) .. ' ' .. tostring(v.count))
end
if k + nbElementsToOmit == selectedCounter then
withColor(win, colors.black, colors.white, writeLine)
else
withColor(win, colors.white, colors.black, writeLine)
end
cursorYIndex = cursorYIndex + 1
end
end
local function renderTitle(win, countersMap, selectedCounter, titleFn)
win.clear()
win.setCursorPos(1, 1)
withColor(win, colors.white, colors.green, function()
win.clearLine()
win.write(titleFn(countersMap, selectedCounter, win))
end)
end
local function incrementSelectedCounter(countersMap, selectedCounter)
local counterPayload = countersMap[selectedCounter]
if counterPayload and counterPayload.count then
countersMap[selectedCounter] = {
count = counterPayload.count + 1,
name = counterPayload.name
}
return true
end
return false
end
local function decrementSelectedCounter(countersMap, selectedCounter)
local counterPayload = countersMap[selectedCounter]
if counterPayload and counterPayload.count and counterPayload.count > 0 then
countersMap[selectedCounter] = {
count = counterPayload.count - 1,
name = counterPayload.name
}
return true
end
return false
end
local function switchToMaxSelectedCounter(countersMap, selectedCounter, maxPossibleCount)
local counterPayload = countersMap[selectedCounter]
if counterPayload and counterPayload.count then
countersMap[selectedCounter] = {
count = maxPossibleCount,
name = counterPayload.name
}
end
end
local function switchToMinSelectedCounter(countersMap, selectedCounter)
local counterPayload = countersMap[selectedCounter]
if counterPayload and counterPayload.count then
countersMap[selectedCounter] = {
count = 0,
name = counterPayload.name
}
end
end
local function CountersSelector(initialCountersMap, config)
local countersMap = utils.shallowClone(initialCountersMap)
local counterMax = config.counterMax
local titleFn = config.titleFn or noTitleFn
if not counterMax then
error('no counterMax found in CountersSelector config')
end
if utils.sizeof(countersMap) == 0 then
error('empty countersMap provided')
end
local selectedCounter = 1
local nbCounters = utils.sizeof(countersMap)
local globalCounter = getTotalCount(countersMap)
if globalCounter > counterMax then
error('counter cannot be greater than the counterMax')
end
local topHeight = 1
local width, height = term.getSize()
local mainHeight = height - topHeight
local nbOfElemsPerPage = mainHeight
local mainWin = window.create(term.current(), 1, 1 + topHeight, width, mainHeight)
local titleWin = window.create(term.current(), 1, 1, width, 1 + topHeight)
local forceExited = false
local shouldContinue = true
while shouldContinue do
renderTitle(titleWin, countersMap, selectedCounter, titleFn)
renderCountersMap(mainWin, countersMap, selectedCounter)
local _, keyPressed, _ = os.pullEvent('key')
if keyPressed == keys.up then
selectedCounter = math.max(1, selectedCounter - 1)
elseif keyPressed == keys.down then
selectedCounter = math.min(nbCounters, selectedCounter + 1)
elseif keyPressed == keys.home then
selectedCounter = 1
elseif keyPressed == keys['end'] then
selectedCounter = nbCounters
elseif keyPressed == keys.pageUp then
selectedCounter = math.max(1, selectedCounter - nbOfElemsPerPage)
elseif keyPressed == keys.pageDown then
selectedCounter = math.min(nbCounters, selectedCounter + nbOfElemsPerPage)
elseif keyPressed == keys.left and globalCounter > 0 then
if decrementSelectedCounter(countersMap, selectedCounter) then
globalCounter = globalCounter - 1
end
elseif keyPressed == keys.right and globalCounter < counterMax then
if incrementSelectedCounter(countersMap, selectedCounter) then
globalCounter = globalCounter + 1
end
elseif keyPressed == keys.m then
switchToMaxSelectedCounter(countersMap, selectedCounter, counterMax - globalCounter)
globalCounter = getTotalCount(countersMap)
elseif keyPressed == keys.delete then
switchToMinSelectedCounter(countersMap, selectedCounter)
globalCounter = getTotalCount(countersMap)
elseif keyPressed == keys.r then
countersMap = resetAllCounters(countersMap)
globalCounter = 0
elseif keyPressed == keys.enter then
shouldContinue = false
elseif keyPressed == keys.q then
os.pullEvent('key_up') -- fix to avoid writing a 'q' in the terminal on exit
shouldContinue = false
forceExited = true
end
end
mainWin.clear()
titleWin.clear()
-- TODO: better window management on clear
term.clear()
term.setCursorPos(1, 1)
if forceExited then
return nil
end
return countersMap, selectedCounter
end
return CountersSelector

View File

@ -1,109 +0,0 @@
local utils = {}
utils.sizeof = function(t)
if not t then
return 0
end
local size = 0
for k,v in pairs(t) do
size = size + 1
end
return size
end
utils.shallowDiff = function(a, b)
local counters = {}
for k,v in pairs(a) do
local currentCounter = counters[v] or 0
counters[v] = currentCounter + 1
end
for k,v in pairs(b) do
if counters[v] then
counters[v] = counters[v] - 1
end
end
local ret = {}
local n = 0
for k,v in pairs(a) do
if counters[v] and counters[v] > 0 then
counters[v] = counters[v] - 1
n = n + 1
ret[n] = v
end
end
return ret
end
utils.shallowClone = function(t)
if not t then
return t
end
local res = {}
for k,v in pairs(t) do
res[k] = v
end
return res
end
utils.merge = function(a, b)
local res = {}
for k, v in pairs(a) do
res[k] = v
end
for k, v in pairs(b) do
res[k] = v
end
return res
end
utils.concat = function(a, b)
local res = {}
local i = 0
for _, v in pairs(a) do
res[i] = v
i = i + 1
end
for _, v in pairs(b) do
res[i] = v
i = i + 1
end
return res
end
utils.find = function(t, predicate)
for k,v in pairs(t) do
if predicate(v, k) then
return v, k
end
end
end
-- repeat n times the given element x in a new table
utils.rep = function(x, n)
if n < 1 then
return {}
end
local result = {}
for i=1, n, 1 do
result[i] = x
end
return result
end
return utils

View File

@ -1,130 +0,0 @@
local rs = require('libs/rs')
local MAIN_DELAY_ON = 0.25
local MAIN_DELAY_OFF = 0.10
local SECONDARY_DELAY_ON = 1.25
local SECONDARY_DELAY_OFF = 0.5
local MAIN_INPUT_SIDE = 'top'
local MAIN_OUTPUT_SIDE = 'left'
local SECONDARY_INPUT_SIDE = 'back'
local SECONDARY_OUTPUT_SIDE = 'front'
local VERSION = '1.1.1'
local function getMainColorsOrder()
return {
colors.white,
colors.orange,
colors.magenta,
colors.lightBlue,
colors.yellow,
colors.lime,
colors.pink,
colors.gray,
colors.lightGray,
colors.cyan,
colors.purple,
colors.blue,
colors.brown,
colors.green,
colors.red,
colors.black
}
end
local function getSecondaryColorsOrder()
return {
colors.white,
colors.orange,
colors.magenta
}
end
local function switchOnLight(bundledOutput, colorsOrder, sleepTime)
if sleepTime <= 0 then
bundledOutput.setColorState(colors.combine(table.unpack(colorsOrder)))
return
end
for _, color in pairs(colorsOrder) do
bundledOutput.setOn(color)
os.sleep(sleepTime)
end
end
local function switchOffLight(bundledOutput, colorsOrder, sleepTime)
if sleepTime <= 0 then
bundledOutput.setColorState(0)
return
end
for _, color in pairs(colorsOrder) do
bundledOutput.setOff(color)
os.sleep(sleepTime)
end
end
local function applyCommand(bundledOutput, loopQueue, colorsOrder, delayOn, delayOff)
local nextState = table.remove(loopQueue, 1)
if nextState == nil then
return
end
if nextState then
switchOnLight(bundledOutput, colorsOrder, delayOn)
else
switchOffLight(bundledOutput, colorsOrder, delayOff)
end
return applyCommand(bundledOutput, loopQueue, colorsOrder, delayOn, delayOff)
end
local function createLoop(side, eventName, loopQueue, colorsOrder, delayOn, delayOff)
local bundledOutput = rs.createBundledOutput(side)
return function()
while true do
os.pullEvent(eventName)
applyCommand(bundledOutput, loopQueue, colorsOrder, delayOn, delayOff)
end
end
end
local function createRedstoneLoop(mainLoopQueue, secondaryLoopQueue)
local mainState = nil
local secondaryState = nil
return function()
while true do
local newMainState = redstone.getInput(MAIN_INPUT_SIDE)
local newSecondaryState = redstone.getInput(SECONDARY_INPUT_SIDE)
if mainState ~= newMainState then
mainState = newMainState
table.insert(mainLoopQueue, newMainState)
os.queueEvent('light_command_main', newMainState)
end
if secondaryState ~= newSecondaryState then
secondaryState = newSecondaryState
table.insert(secondaryLoopQueue, newSecondaryState)
os.queueEvent('light_command_secondary', newSecondaryState)
end
os.pullEvent('redstone')
end
end
end
local mainLoopQueue = {}
local secondaryLoopQueue = {}
local mainLoop = createLoop(MAIN_OUTPUT_SIDE, 'light_command_main', mainLoopQueue, getMainColorsOrder(), MAIN_DELAY_ON, MAIN_DELAY_OFF)
local secondaryLoop = createLoop(SECONDARY_OUTPUT_SIDE, 'light_command_secondary', secondaryLoopQueue, getSecondaryColorsOrder(), SECONDARY_DELAY_ON, SECONDARY_DELAY_OFF)
local redstoneLoop = createRedstoneLoop(mainLoopQueue, secondaryLoopQueue)
print('> Starting light server v' .. VERSION)
parallel.waitForAny(mainLoop, secondaryLoop, redstoneLoop)

150
miner.lua
View File

@ -1,9 +1,11 @@
local robotApi = require('libs/robot')
local turtleUtils = require('libs/turtle-utils')
local config = require('config/mining')
local robotApi = require('robot')
local turtleUtils = require('turtle-utils')
local config = require('config-miner')
local LOWER_SIZE_LIMIT = 2;
local HIGHER_SIZE_LIMIT = 128;
local FUEL_NEEDED_MULTIPLIER = 4
local ADDITIONAL_FUEL_NEEDED = 1024
local INITIAL_TARGET_Y = config.targetY - config.startY
local TARGET_Z = config.size - 1
@ -13,70 +15,18 @@ local MAX_X = TARGET_X
local MAX_Y = INITIAL_TARGET_Y + (config.height - 1)
local MAX_Z = TARGET_Z
local robot = robotApi.create()
local MIN_PERCENTAGE_NEEDED = 10
local TIME_TO_START = 3
local miner = {
robot = robotApi.create(),
robot = robot,
mission = 'unload', -- "unload" | "refuel" | "mine" | "return-home" | "return-mine" | nil
started = false,
finished = false,
lastPositionState = nil, -- { x, y, z, dir }
targetY = INITIAL_TARGET_Y
}
local function saveMinerState()
local minerState = {
robotState = miner.robot.getState(),
mission = miner.mission,
started = miner.started,
finished = miner.finished,
lastPositionState = miner.lastPositionState and textutils.serializeJSON(miner.lastPositionState),
targetY = miner.targetY
}
local file = fs.open('.minerstate', 'w')
if not file then
error('saveMinerState: cannot open .minerstate file!')
end
file.write(textutils.serializeJSON(minerState))
file.close()
end
local function loadMinerState()
local file = fs.open('.minerstate', 'r')
if not file then
return
end
local serializedMinerState = file.readAll()
file.close()
local minerState = textutils.unserializeJSON(serializedMinerState)
local lastPositionState = nil
if minerState.lastPositionState then
lastPositionState = minerState.lastPositionState
end
miner = {
robot = robotApi.create(minerState.robotState),
mission = minerState.mission,
started = minerState.started,
finished = minerState.finished,
lastPositionState = lastPositionState,
targetY = minerState.targetY
}
end
local function cleanupMinerState()
fs.delete('.minerstate')
end
local function isOdd(x)
return (x % 2) == 1
end
@ -93,30 +43,6 @@ local function robotForceForward()
return miner.robot.forward()
end
local function robotForceDown()
local ok = miner.robot.down()
if ok then
return ok
end
turtle.digDown()
return miner.robot.down()
end
local function robotForceUp()
local ok = miner.robot.up()
if ok then
return ok
end
turtle.digUp()
return miner.robot.up()
end
local function checkConfig()
if config.targetY >= config.startY then
error('targetY should be lower than startY')
@ -139,19 +65,8 @@ local function checkConfig()
end
end
local function startMiner()
local function prepareTurtle()
turtle.select(1)
print('Miner will starting in ' .. TIME_TO_START .. ' seconds...')
os.sleep(TIME_TO_START)
miner.started = true
print("> Miner program started, minimum percentgae fuel needed: " .. MIN_PERCENTAGE_NEEDED)
end
local function minerFinished()
cleanupMinerState()
print("> Miner program finished!")
end
local function isProgramFinished()
@ -204,11 +119,11 @@ local function returnMineProcedure()
return
end
if miner.robot.lastPositionState then
if robot.lastPositionState then
local robotState = miner.robot.getState()
if robotState.y > miner.lastPositionState.y then
robotForceDown()
miner.robot.down()
elseif robotState.z < miner.lastPositionState.z then
robotForceForward()
else
@ -216,12 +131,10 @@ local function returnMineProcedure()
for i=1, miner.lastPositionState.x, 1 do
robotForceForward()
saveMinerState()
end
while miner.robot.getState().dir ~= miner.lastPositionState.dir do
miner.robot.turnRight()
saveMinerState()
end
miner.mission = 'mine'
@ -241,7 +154,7 @@ local function mineProcedure()
-- 1. Move
if robotState.y > targetY then
turtle.digDown()
robotForceDown()
miner.robot.down()
elseif robotState.dir == 'FORWARD' and robotState.z < targetZ then
turtleUtils.digAll()
robotForceForward()
@ -272,12 +185,11 @@ local function mineProcedure()
for i=1, config.size - 1, 1 do
robotForceForward()
saveMinerState()
end
miner.robot.turnRight()
-- mine the next stage
robotForceUp()
miner.robot.up()
miner.targetY = miner.targetY + 1
end
@ -285,10 +197,6 @@ local function mineProcedure()
print('> Starting return home procedure because inventory is almost full...')
miner.mission = 'return-home'
miner.lastPositionState = miner.robot.getState()
elseif turtleUtils.getFuelPercentage() <= 1 then
print('> Starting return home procedure because there is less than 1% of fuel...')
miner.mission = 'return-home'
miner.lastPositionState = miner.robot.getState()
end
end
@ -302,7 +210,7 @@ local function returnHomeProcedure()
miner.robot.turnRight()
end
robotForceUp()
miner.robot.up()
elseif robotState.x > 0 then
if robotState.dir == 'FORWARD' then
miner.robot.turnLeft()
@ -312,7 +220,6 @@ local function returnHomeProcedure()
for i=1, robotState.x, 1 do
robotForceForward()
saveMinerState()
end
miner.robot.turnLeft()
@ -326,31 +233,11 @@ local function returnHomeProcedure()
end
end
local function waitForUserConfirmation()
print('> startY: ' .. config.startY)
print('> targetY: ' .. config.targetY)
print('> height: ' .. config.height)
print('> size: ' .. config.size)
print('> type "yes" to start the mining process.')
local val = read()
if val == 'yes' then
return true
end
return false
end
checkConfig()
loadMinerState()
prepareTurtle()
if not miner.started then
while not waitForUserConfirmation() do end
end
startMiner()
saveMinerState()
print("> Miner program started, minimum percentgae fuel needed: " .. MIN_PERCENTAGE_NEEDED)
while not isProgramFinished() do
if miner.mission == 'unload' then
@ -359,14 +246,11 @@ while not isProgramFinished() do
refuelProcedure()
elseif miner.mission == 'mine' then
mineProcedure()
saveMinerState()
elseif miner.mission == 'return-home' then
returnHomeProcedure()
saveMinerState()
elseif miner.mission == 'return-mine' then
returnMineProcedure()
saveMinerState()
end
end
minerFinished()
print("> Miner program finished!")

View File

@ -1,271 +0,0 @@
local turtleUtils = require('libs/turtle-utils')
local NB_ROWS = 8
local DIRECTION = 'left' -- 'left' | 'right'
local FILL_BLOCK = 'minecraft:cobblestone'
local LEVEL_PER_TIER = 12
local formatBlockName = function(tierName)
return "mysticalagriculture:" .. tierName .. "_growth_accelerator"
end
local BLOCKS_PER_TIER = {
formatBlockName('inferium'),
formatBlockName('prudentium'),
formatBlockName('tertium'),
formatBlockName('imperium'),
formatBlockName('supremium')
}
local MAX_LEVEL = LEVEL_PER_TIER * #BLOCKS_PER_TIER
local countItems = function(itemName)
local total = 0
for i=1,16,1 do
local details = turtle.getItemDetail(i)
if details and details.name == itemName then
total = total + details.count
end
end
return total
end
local assertEnoughFillBlocks = function()
local count = countItems(FILL_BLOCK)
if count < MAX_LEVEL then
error('Not enough fill blocks, please provide at least ' .. MAX_LEVEL .. ' of ' .. FILL_BLOCK)
end
end
local assertEnoughFuel = function()
local minFuel = 10 + NB_ROWS + (MAX_LEVEL * 2 * NB_ROWS)
if turtle.getFuelLevel() < minFuel then
error('Not enough fuel')
end
end
local refuel = function()
for i=1, 16, 1 do
local count = turtle.getItemCount(i)
if count > 0 then
turtle.select(i)
turtle.refuel()
end
end
end
local MOVES = {
left = function(strictMode)
turtle.turnLeft()
local ok, reason = turtle.forward()
if strictMode and not ok then
error('cannot forward because ' .. tostring(reason), 0)
end
turtle.turnRight()
return ok, reason
end,
right = function(strictMode)
turtle.turnRight()
local ok, reason = turtle.forward()
if strictMode and not ok then
error('cannot forward because ' .. tostring(reason), 0)
end
turtle.turnLeft()
return ok, reason
end,
}
local getTierFromLevel = function(level)
return math.floor((level - 1) / LEVEL_PER_TIER) + 1
end
local getBlockNameFromLevel = function(level)
local tier = getTierFromLevel(level)
return BLOCKS_PER_TIER[tier]
end
local inspectBlockName = function()
local ok, block = turtle.inspect()
if not ok then
return nil
end
return block and block.name
end
local selectItem = function(itemName)
for i=1, 16, 1 do
local details = turtle.getItemDetail(i)
if details and details.name == itemName then
turtle.select(i)
return true
end
end
return false
end
local replaceBlockAt = function(y)
local blockName = getBlockNameFromLevel(y);
if inspectBlockName() == blockName then
return false
end
local selected = selectItem(blockName)
if not selected then
return 'error'
end
turtle.dig()
turtle.place()
return true
end
local placeDownFillBlock = function()
selectItem(FILL_BLOCK)
turtle.placeDown()
end
local placeDownItem = function(itemName)
if selectItem(itemName) then
turtle.placeDown()
else
placeDownFillBlock()
end
end
-- Main state and stateful methods
local function getInitialState()
return {
firstPickedItem = nil,
y = 1,
initialLevel = 0,
newLevel = 0,
errorLevel = nil
}
end
-- local state = getInitialState()
local digAndGoDown = function(state)
turtle.digDown()
turtle.down()
state.y = state.y + 1
end
local goUp = function(state)
turtle.up()
state.y = state.y - 1
end
local function tryToSelectEmptySlot()
if not turtleUtils.selectFirstEmptySlot() then
turtleUtils.compactInventory()
if not turtleUtils.selectFirstEmptySlot() then
error('Fatal error: turtle inventory is full', 0)
end
end
end
-- 1. down procedure
local downProcedure = function(state)
while state.y < 60 do
local replaced = replaceBlockAt(state.y)
if replaced == false then
state.initialLevel = state.y
state.newLevel = state.initialLevel
end
if replaced == true then
state.newLevel = state.y
end
if replaced == 'error' then
state.errorLevel = state.y
break
end
if not state.firstPickedItem then
tryToSelectEmptySlot()
digAndGoDown(state)
local item = turtle.getItemDetail(turtle.getSelectedSlot())
-- consider FILL_BLOCK if no block was picked
state.firstPickedItem = (item and item.name) or FILL_BLOCK
else
digAndGoDown(state)
end
end
end
-- 2. up procedure
local upProcedure = function(state)
local firstY = getInitialState().y
while state.y ~= firstY do
goUp(state)
if state.y == firstY and state.firstPickedItem then
placeDownItem(state.firstPickedItem)
else
placeDownFillBlock()
end
end
end
-- 3. print report
local printReport = function(state)
local baseReport = 'Level ' .. tostring(state.initialLevel) .. ' -> ' .. state.newLevel
if state.errorLevel then
print (baseReport .. ' (tier ' .. getTierFromLevel(state.errorLevel) .. ')')
else
print (baseReport)
end
end
local upgradeProcedure = function()
turtleUtils.compactInventory()
local state = getInitialState()
downProcedure(state)
upProcedure(state)
printReport(state)
return state
end
-- Main function
local main = function()
assertEnoughFillBlocks()
refuel()
assertEnoughFuel()
for i=1, NB_ROWS, 1 do
upgradeProcedure()
if i ~= NB_ROWS then
MOVES[DIRECTION](true)
else
MOVES[DIRECTION](false) -- do not throw if cannot move forward
end
end
end
main()

View File

@ -1,31 +0,0 @@
local notify = require('libs/notify')
local net = require('libs/net')
local VERSION = '1.0.0'
local chatBox = peripheral.find('chatBox') or error('no chatBox peripheral found', 0)
local function notifyChat(message, prefix)
if not message then
print('warning: no message provided')
return
end
chatBox.sendMessage(message, prefix)
end
local function main()
net.openRednet()
print('Notify Server v' .. VERSION .. ' started')
net.listenQuery(notify.SERVER, function(message)
if message.type == 'notify-chat' and message.payload then
notifyChat(message.payload.message, message.payload.prefix)
end
end)
net.closeRednet()
end
main()

View File

@ -1,16 +0,0 @@
local turtleUtils = require('turtle-utils')
local function main()
local initialFuelLevel = turtle.getFuelLevel()
print('Current fuel: ' .. turtleUtils.getFuelPercentage() .. '%')
turtleUtils.refuelAllFromInventory()
if initialFuelLevel == turtle.getFuelLevel() then
error('nothing to refuel', 0)
end
print('New fuel: ' .. turtleUtils.getFuelPercentage() .. '%')
end
main()

118
robot.lua Normal file
View File

@ -0,0 +1,118 @@
-- robot api
local api = {}
local function createDefaultState()
return {
y = 0,
x = 0,
z = 0,
-- | BACKWARD | LEFT | RIGHT
dir = 'FORWARD'
}
end
api.create = function(state)
state = state or createDefaultState()
local mutateRobotPosition = function(isBackward)
local incValue = 1
if isBackward then invValue = -1 end
if state.dir == 'FORWARD' then
state.z = state.z + incValue
elseif state.dir == 'BACKWARD' then
state.z = state.z - incValue
elseif state.dir == 'LEFT' then
state.x = state.x - incValue
elseif state.dir == 'RIGHT' then
state.x = state.x + incValue
end
end
local robot = {}
robot.forward = function()
local ok, err = turtle.forward()
if ok then
mutateRobotPosition(false)
end
return ok, err
end
robot.backward = function()
local ok, err = turtle.back()
if ok then
mutateRobotPosition(true)
end
return ok, err
end
robot.up = function()
local ok, err = turtle.up()
if ok then
state.y = state.y + 1
end
return ok, err
end
robot.down = function()
local ok, err = turtle.down()
if ok then
state.y = state.y - 1
end
return ok, err
end
robot.turnLeft = function()
local ok, err = turtle.turnLeft()
if ok then
if state.dir == 'FORWARD' then
state.dir = 'LEFT'
elseif state.dir == 'LEFT' then
state.dir = 'BACKWARD'
elseif state.dir == 'RIGHT' then
state.dir = 'FORWARD'
elseif state.dir == 'BACKWARD' then
state.dir = 'RIGHT'
end
end
return ok, err
end
robot.turnRight = function()
local ok, err = turtle.turnRight()
if ok then
if state.dir == 'FORWARD' then
state.dir = 'RIGHT'
elseif state.dir == 'LEFT' then
state.dir = 'FORWARD'
elseif state.dir == 'RIGHT' then
state.dir = 'BACKWARD'
elseif state.dir == 'BACKWARD' then
state.dir = 'LEFT'
end
end
return ok, err
end
robot.getState = function()
return state
end
return robot
end
return api

View File

@ -1,2 +0,0 @@
-- shell.execute('/inferium-harvester.lua')
-- shell.execute('/miner.lua')

View File

@ -1,303 +0,0 @@
local STATIC_CONFIG = require('config/tunnels')
local turtleUtils = require('libs/turtle-utils')
local IDLE_TIME = 3
local TIME_TO_START = 3
local IDLE_TIME_BETWEEN_TUNNELS = 1
local SPACE_BETWEEN_TUNNELS = 3
local VERSION = "2.2.0"
local MOVES_BY_DIRECTION = {
right = {
turnLeft = turtle.turnLeft,
turnRight = turtle.turnRight
},
left = { -- this is inversed
turnLeft = turtle.turnRight,
turnRight = turtle.turnLeft
}
}
local function noop() end
local function getComputerLabel()
return os.getComputerLabel() or 'Unknown miner'
end
local function getMoves(config)
return MOVES_BY_DIRECTION[config.DIRECTION]
end
local function getMinFuelNeeded(config)
local fuelMultiplier = 2
local fuelMargin = config.FUEL_MARGIN
if config.VEIN_MODE then
fuelMultiplier = 4
fuelMargin = fuelMargin + config.FUEL_MARGIN_VEIN_MODE
end
return (config.DISTANCE_Z * fuelMultiplier) + SPACE_BETWEEN_TUNNELS + fuelMargin
end
local function checkEnoughFuelForTunnel(config)
local minFuelNeeded = getMinFuelNeeded(config)
if turtle.getFuelLevel() < minFuelNeeded then
turtleUtils.refuelAllFromInventory()
return turtle.getFuelLevel() < minFuelNeeded
end
return true
end
local function assertEnoughFuelForTunnel(config, withNotif)
if not checkEnoughFuelForTunnel(config) then
if withNotif then
local message = 'Il n\'y a plus assez de fuel sur "' .. getComputerLabel() .. '"'
print(message)
-- TODO: notify.sendChatMessage(o)
end
error('not enough fuel', 0)
end
end
local function assertValidConfig(config)
if config.DIRECTION ~= 'left' and config.DIRECTION ~= 'right' then
error('config.DIRECTION should be "left" or "right"', 0)
end
if config.DISTANCE_Z <= 0 then
error('config.DISTANCE_Z should be a positive number', 0)
end
local minFuelMargin = 6
if config.FUEL_MARGIN < minFuelMargin then
error('config.FUEL_MARGIN cannot be lower than ' .. minFuelMargin)
end
end
local function assertInventoryIsEmpty()
if not turtleUtils.isInventoryEmpty() then
error('turtle inventory should be empty', 0)
end
end
local function printFuelReport(config)
print('> current fuel: ' .. turtleUtils.getFuelPercentage() .. '%')
print('> fuel needed: ' .. turtleUtils.getFuelPercentage(getMinFuelNeeded(config)) .. '%')
end
local function findGroundProcedure()
local foundGround, errMessage = turtleUtils.goGround()
if not foundGround then
error('cannot find the ground because ' .. tostring(errMessage), 0)
end
end
local function digForward(n, cb)
n = n or 1
cb = cb or noop
for i=1, n, 1 do
turtleUtils.forceForward()
turtle.digDown()
cb(i)
end
end
local function isWhitelistedOre(config, itemOrBlock)
local itemOrBlockName = itemOrBlock and itemOrBlock.name
if config.VEIN_ORES_WHITELIST[itemOrBlockName] then
return true
end
if config.VEIN_ORES_WHITELIST_ITEMS[itemOrBlockName] then
return true
end
return false
end
local function inspectWhitelistedOre(config, inspectFn)
local _, block = inspectFn()
return isWhitelistedOre(config, block)
end
local function trySelectEmptySlot()
if turtle.getItemDetail() then
turtleUtils.selectItemBy(function(slot)
return not not turtle.getItemDetail(slot)
end)
end
end
local function ensureEnoughSpaceInInventory(config)
if not turtleUtils.isInventoryFull() then
return
end
-- 1. try to compact inventory
turtleUtils.compactInventory()
if not turtleUtils.isInventoryFull() then
return
end
-- 2. otherwise select an item that is not in whitelist item
local selected = turtleUtils.selectItemBy(function(slot)
return not isWhitelistedOre(config, turtle.getItemDetail(slot))
end)
if selected then
-- 3. and drop the selected item
turtle.drop()
trySelectEmptySlot()
end
end
local function mineVeinOres(config)
ensureEnoughSpaceInInventory(config)
-- 1. front
if inspectWhitelistedOre(config, turtle.inspect) then
turtleUtils.forceForward()
mineVeinOres(config)
turtle.back()
end
-- 2. up
if inspectWhitelistedOre(config, turtle.inspectUp) then
turtleUtils.forceUp()
mineVeinOres(config)
turtle.down()
end
-- 3. down
if inspectWhitelistedOre(config, turtle.inspectDown) then
turtleUtils.forceDown()
mineVeinOres(config)
turtle.up()
end
-- 4. check left
turtle.turnLeft()
if inspectWhitelistedOre(config, turtle.inspect) then
turtleUtils.forceForward()
mineVeinOres(config)
turtle.back()
end
turtle.turnRight()
-- 5. right
turtle.turnRight()
if inspectWhitelistedOre(config, turtle.inspect) then
turtleUtils.forceForward()
mineVeinOres(config)
turtle.back()
end
turtle.turnLeft()
end
local function mineVeinProcedure(config)
if not config.VEIN_MODE then
return
end
mineVeinOres(config)
turtleUtils.forceDown()
mineVeinOres(config)
turtleUtils.forceUp()
end
local function mineTunnelProcedure(config)
local distance = config.DISTANCE_Z
local onMineCb = function()
ensureEnoughSpaceInInventory(config)
mineVeinProcedure(config)
end
digForward(distance, onMineCb)
getMoves(config).turnRight()
digForward(SPACE_BETWEEN_TUNNELS, onMineCb)
getMoves(config).turnRight()
digForward(distance, onMineCb)
end
local function dropAllProcedure(config)
turtleUtils.forceDown()
getMoves(config).turnRight()
-- try to refuel before dropping items
turtleUtils.refuel(getMinFuelNeeded(config), turtle.suck, IDLE_TIME)
turtleUtils.refuelAllFromInventory()
-- drop all items
for i=1, 16, 1 do
local count = turtle.getItemCount(i)
if count > 0 then
turtleUtils.waitForInventory('front', IDLE_TIME, function()
local message = getComputerLabel() .. ' attends un inventaire pour continuer le minage'
print(message)
-- TODO: notify.sendChatMessage(o)
end)
while turtle.getItemCount(i) and not turtleUtils.dropSlot(i, turtle.drop) do
os.sleep(IDLE_TIME)
end
end
end
getMoves(config).turnLeft()
end
local function goToNextMineProcedure(config)
getMoves(config).turnLeft()
for _=1, SPACE_BETWEEN_TUNNELS, 1 do
turtleUtils.forceForward()
end
getMoves(config).turnLeft()
turtleUtils.forceUp()
end
local function main(config)
assertValidConfig(config)
turtleUtils.refuelAllFromInventory()
assertInventoryIsEmpty() -- Warning here when implementing persistance
printFuelReport(config)
assertEnoughFuelForTunnel(config)
findGroundProcedure()
turtleUtils.forceUp()
assertEnoughFuelForTunnel(config)
print('> Starting tunnels-miner v' .. VERSION .. ' in ' .. TIME_TO_START .. ' seconds...')
os.sleep(TIME_TO_START)
local tunnelNumber = 1
while true do
if tunnelNumber > 1 then
printFuelReport(config)
assertEnoughFuelForTunnel(config, true)
end
print('> Start mining tunnel number ' .. tostring(tunnelNumber))
mineTunnelProcedure(config)
dropAllProcedure(config)
goToNextMineProcedure(config)
os.sleep(IDLE_TIME_BETWEEN_TUNNELS)
tunnelNumber = tunnelNumber + 1
end
end
main(STATIC_CONFIG)

63
turtle-utils.lua Normal file
View File

@ -0,0 +1,63 @@
local turtleUtils = {}
local IDLE_TIME = 2
turtleUtils.waitForInventory = function(side)
local inv = nil
while true do
inv = peripheral.wrap(side)
if inv and peripheral.hasType(inv, 'inventory') then
break
end
os.sleep(IDLE_TIME)
end
return inv
end
turtleUtils.trySuckUp = function()
while not turtle.suckUp() do
os.sleep(IDLE_TIME)
end
end
turtleUtils.tryDrop = function()
while true do
if turtle.getItemCount() == 0 then
return false
end
local dropOk = turtle.drop();
if (dropOk) then
return true
end
os.sleep(IDLE_TIME)
end
end
turtleUtils.digAll = function()
while turtle.dig() do end
end
turtleUtils.countFreeSlots = function()
local freeSlots = 0
for i=1, 16, 1 do
if turtle.getItemCount(i) == 0 then
freeSlots = freeSlots + 1
end
end
return freeSlots
end
turtleUtils.getFuelPercentage = function()
return (turtle.getFuelLevel() / turtle.getFuelLimit()) * 100
end
return turtleUtils

View File

@ -1,16 +0,0 @@
local REPO_PREFIX = 'https://git.trapcloud.fr/guillaumearm/minecraft-cc-tools/raw/branch/master/'
local INSTALL_SCRIPT_NAME = '/install.lua'
local DOWNLOAD_INSTALL_PATH = REPO_PREFIX .. INSTALL_SCRIPT_NAME
local function reinstall()
if fs.exists(INSTALL_SCRIPT_NAME) then
fs.delete(INSTALL_SCRIPT_NAME)
end
shell.execute('wget', DOWNLOAD_INSTALL_PATH, INSTALL_SCRIPT_NAME)
shell.execute('install')
os.reboot()
end
reinstall()