Compare commits
238 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b0b172e9a | ||
|
|
1784bfb93e | ||
|
|
1d882ada6d | ||
|
|
52970328c6 | ||
|
|
56f860a282 | ||
|
|
37c2491422 | ||
|
|
aaf40e2e77 | ||
|
|
2c224f29c6 | ||
|
|
9950ff6883 | ||
|
|
b4a3abb210 | ||
|
|
ee8a5da868 | ||
|
|
c00d495460 | ||
|
|
7285c63b57 | ||
|
|
46635141da | ||
|
|
ce90d6c69c | ||
|
|
a4cc3d4c8e | ||
|
|
31846cf5c1 | ||
|
|
b5aeb3532e | ||
|
|
154f96b676 | ||
|
|
d3946ad589 | ||
|
|
3eca913146 | ||
|
|
98bf2b2f31 | ||
|
|
d257aa382c | ||
|
|
abb746e2a6 | ||
|
|
c987dd1f5f | ||
|
|
8a2b7d210a | ||
|
|
cc817f53ea | ||
|
|
0b2e412ad9 | ||
|
|
7a8636f054 | ||
|
|
15764993e0 | ||
|
|
834202e4cb | ||
|
|
692b24d96d | ||
|
|
c81c15d427 | ||
|
|
432bdddd4e | ||
|
|
5e887959b0 | ||
|
|
6b6fcac1d1 | ||
|
|
37bfb2965a | ||
|
|
32783f7ebc | ||
|
|
12a50ebf6a | ||
|
|
4091084bea | ||
|
|
1747263d26 | ||
|
|
c2e63d427a | ||
|
|
edcab83d4e | ||
|
|
69a04a19d5 | ||
|
|
c030b03a11 | ||
|
|
c639157bdb | ||
|
|
a78ed6dba3 | ||
|
|
c5dcfaf2b7 | ||
|
|
83a819c038 | ||
|
|
50cecdee4d | ||
|
|
ffccce4700 | ||
|
|
f6ab6d6f77 | ||
|
|
4062c87f48 | ||
|
|
494c32faf6 | ||
|
|
022f778f0b | ||
|
|
3181d68839 | ||
|
|
55c8e44a27 | ||
|
|
12c6f1d2c0 | ||
|
|
744cf941ad | ||
|
|
3df6cfa706 | ||
|
|
c7d0f5681a | ||
|
|
1da3d12cdc | ||
|
|
8e053ddacf | ||
|
|
b00180d780 | ||
|
|
aba45e6b47 | ||
|
|
1183442712 | ||
|
|
cd2e624182 | ||
|
|
626a195bb4 | ||
|
|
1748b29bbd | ||
|
|
a1364a4bff | ||
|
|
6a50fef10a | ||
|
|
802c8e2a43 | ||
|
|
201831b16c | ||
|
|
43d931994a | ||
|
|
58bb9d6d24 | ||
|
|
702dd4833a | ||
|
|
6b36dd3c92 | ||
|
|
4502db1ba1 | ||
|
|
88d7b6dcc8 | ||
|
|
9b9b312a2e | ||
|
|
8687ec057b | ||
|
|
2febed4d6e | ||
|
|
61ec9d5d59 | ||
|
|
523f4b7775 | ||
|
|
99d9551a68 | ||
|
|
566df84c75 | ||
|
|
1d36a684bf | ||
|
|
7fbbf6f028 | ||
|
|
a8ef9f7d32 | ||
|
|
472415a59f | ||
|
|
3ef5930f4f | ||
|
|
f9fb8e26df | ||
|
|
f1174fb6a2 | ||
|
|
309cc3aae4 | ||
|
|
491d85c951 | ||
|
|
28ea977b86 | ||
|
|
c0ea32bf17 | ||
|
|
8ece6948e5 | ||
|
|
bd843779e2 | ||
|
|
598bf7bfa5 | ||
|
|
4b7a38ebf0 | ||
|
|
64176024f3 | ||
|
|
642604da9d | ||
|
|
f45d979312 | ||
|
|
8d4768813b | ||
|
|
69b31f24a9 | ||
|
|
56558fad7a | ||
|
|
4903f0a96c | ||
|
|
7f7d3e801e | ||
|
|
ab17e80fa5 | ||
|
|
5501e06844 | ||
|
|
b2beb2fdbb | ||
|
|
b8c83bc959 | ||
|
|
ddea713ae4 | ||
|
|
e451631810 | ||
|
|
be9e57ebd7 | ||
|
|
b1c24ee6a2 | ||
|
|
11831c9054 | ||
|
|
6af0669b99 | ||
|
|
30c1da50e5 | ||
|
|
9e09ac85c3 | ||
|
|
dd79ecb941 | ||
|
|
a28ccbc920 | ||
|
|
b88bc8a023 | ||
|
|
49669c6e59 | ||
|
|
3bdfec3eda | ||
|
|
877773bb5a | ||
|
|
b2658117fd | ||
|
|
c555e4e0fc | ||
|
|
43c936bf3b | ||
|
|
baba577b62 | ||
|
|
24ffcce68d | ||
|
|
77210784ff | ||
|
|
f38f3dc7bf | ||
|
|
90a3ac864e | ||
|
|
38b9c24bd6 | ||
|
|
f988356750 | ||
|
|
ddf028b4a4 | ||
|
|
6ca8bacaf6 | ||
|
|
467378f0c9 | ||
|
|
a29f8ac5cb | ||
|
|
0a9294310a | ||
|
|
b8fb2390f2 | ||
|
|
9bd5bbc127 | ||
|
|
7233ed7eb5 | ||
|
|
b9144d6847 | ||
|
|
97bb32b544 | ||
|
|
6071fdb6b4 | ||
|
|
11b6f140e1 | ||
|
|
cf39e16fa3 | ||
|
|
a4a33d1eef | ||
|
|
9dd106763e | ||
|
|
cfea9f575f | ||
|
|
4f16cef9f5 | ||
|
|
5305afe203 | ||
|
|
3f92309ac6 | ||
|
|
6294b38806 | ||
|
|
cdad0204ba | ||
|
|
cc1d083aa7 | ||
|
|
efeba6f4cb | ||
|
|
e628ec2153 | ||
|
|
b075b953ab | ||
|
|
d4cbe6e808 | ||
|
|
4af7ae8cfe | ||
|
|
d16a9b2587 | ||
|
|
be9c2c10a1 | ||
|
|
f483c41c88 | ||
|
|
8f14f48914 | ||
|
|
87188093f4 | ||
|
|
e883deba5a | ||
|
|
cdb0e6e84f | ||
|
|
5e2234da78 | ||
|
|
340d5f4cff | ||
|
|
930b104522 | ||
|
|
e9ebf63c55 | ||
|
|
b1c3eac1b9 | ||
|
|
a8788ca3b1 | ||
|
|
86baeb83f0 | ||
|
|
997fe76fd1 | ||
|
|
586b656fd7 | ||
|
|
0f78c1c994 | ||
|
|
6c38ea6845 | ||
|
|
c42b8472dc | ||
|
|
865d6b4f5b | ||
|
|
f7a83ef911 | ||
|
|
6b4e4f248d | ||
|
|
daa36c82de | ||
|
|
0b724c7334 | ||
|
|
51e93081a1 | ||
|
|
a4f2d4d27c | ||
|
|
381b438da2 | ||
|
|
3631dfcea1 | ||
|
|
cd0e42e34c | ||
|
|
694a23fb68 | ||
|
|
d971f0acf3 | ||
|
|
e02a7a70c2 | ||
|
|
98cb51510f | ||
|
|
b81c0b6921 | ||
|
|
eee19c8a48 | ||
|
|
084b6d941f | ||
|
|
115ffd2e78 | ||
|
|
e5e63f4223 | ||
|
|
ad14b3f521 | ||
|
|
5216041e76 | ||
|
|
ea4f132f2e | ||
|
|
799d1654c2 | ||
|
|
3b674110a3 | ||
|
|
e7a95dcb44 | ||
|
|
2ee725388a | ||
|
|
fd72a34ecb | ||
|
|
710b681738 | ||
|
|
be8ab78425 | ||
|
|
66968b0186 | ||
|
|
f322b42af2 | ||
|
|
b1cbacc920 | ||
|
|
70854f7a75 | ||
|
|
f76877b0ab | ||
|
|
8483ffc696 | ||
|
|
66e9eff8ac | ||
|
|
9770321c20 | ||
|
|
467a1c9168 | ||
|
|
e20c7fd073 | ||
|
|
1438db9973 | ||
|
|
88d8fae296 | ||
|
|
1e9536b089 | ||
|
|
f811031db5 | ||
|
|
0c2b64beb8 | ||
|
|
0338aeae67 | ||
|
|
8a3e422c9d | ||
|
|
27afb3eade | ||
|
|
7dc979f8c4 | ||
|
|
6b8cec89e8 | ||
|
|
e8ce29bad6 | ||
|
|
7a7542e851 | ||
|
|
84e497300e | ||
|
|
809f2a78c0 | ||
|
|
9e3a356eca | ||
|
|
bedd5adbcb |
37
README.md
37
README.md
@ -1,5 +1,38 @@
|
||||
# Installation
|
||||
# minecraft-cc-tools
|
||||
|
||||
Some tools for cc:tweaked minecraft mod (aka computercraft)
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1
|
||||
|
||||
Download the install script:
|
||||
|
||||
```
|
||||
wget run https://git.trapcloud.fr/guillaumearm/minecraft-cc-tools/raw/branch/master/install.lua
|
||||
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
|
||||
```
|
||||
|
||||
175
coal-crafter.lua
Normal file
175
coal-crafter.lua
Normal file
@ -0,0 +1,175 @@
|
||||
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()
|
||||
20
config/inferium.lua
Normal file
20
config/inferium.lua
Normal file
@ -0,0 +1,20 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
17
config/tunnels.lua
Normal file
17
config/tunnels.lua
Normal file
@ -0,0 +1,17 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
251
inferium-gui.lua
Normal file
251
inferium-gui.lua
Normal file
@ -0,0 +1,251 @@
|
||||
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)
|
||||
579
inferium-harvester.lua
Normal file
579
inferium-harvester.lua
Normal file
@ -0,0 +1,579 @@
|
||||
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()
|
||||
271
inferium-server.lua
Normal file
271
inferium-server.lua
Normal file
@ -0,0 +1,271 @@
|
||||
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')
|
||||
129
install.lua
129
install.lua
@ -1,28 +1,121 @@
|
||||
local LIST_FILES = {
|
||||
'turtle-utils.lua',
|
||||
'robot.lua',
|
||||
'miner.lua'
|
||||
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_CONFIG_FILES = {
|
||||
'config-miner.lua'
|
||||
local LIST_TURTLE_CONFIG_FILES = {
|
||||
'config/tunnels.lua',
|
||||
'config/mining.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 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)
|
||||
local removeFiles = function(list)
|
||||
for _, filePath in pairs(list) do
|
||||
if filePath then
|
||||
fs.delete(filePath)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shell.setDir(previousDir)
|
||||
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()
|
||||
|
||||
42
libs/inferium.lua
Normal file
42
libs/inferium.lua
Normal file
@ -0,0 +1,42 @@
|
||||
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
|
||||
72
libs/net.lua
Normal file
72
libs/net.lua
Normal file
@ -0,0 +1,72 @@
|
||||
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
|
||||
21
libs/notify.lua
Normal file
21
libs/notify.lua
Normal file
@ -0,0 +1,21 @@
|
||||
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
|
||||
231
libs/robot.lua
Normal file
231
libs/robot.lua
Normal file
@ -0,0 +1,231 @@
|
||||
-- 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
|
||||
68
libs/rs.lua
Normal file
68
libs/rs.lua
Normal file
@ -0,0 +1,68 @@
|
||||
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
|
||||
486
libs/turtle-utils.lua
Normal file
486
libs/turtle-utils.lua
Normal file
@ -0,0 +1,486 @@
|
||||
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
|
||||
277
libs/ui/CountersSelector.lua
Normal file
277
libs/ui/CountersSelector.lua
Normal file
@ -0,0 +1,277 @@
|
||||
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
|
||||
109
libs/utils.lua
Normal file
109
libs/utils.lua
Normal file
@ -0,0 +1,109 @@
|
||||
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
|
||||
130
light-server.lua
Normal file
130
light-server.lua
Normal file
@ -0,0 +1,130 @@
|
||||
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
150
miner.lua
@ -1,11 +1,9 @@
|
||||
local robotApi = require('robot')
|
||||
local turtleUtils = require('turtle-utils')
|
||||
local config = require('config-miner')
|
||||
local robotApi = require('libs/robot')
|
||||
local turtleUtils = require('libs/turtle-utils')
|
||||
local config = require('config/mining')
|
||||
|
||||
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
|
||||
@ -15,18 +13,70 @@ 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 = robot,
|
||||
robot = robotApi.create(),
|
||||
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
|
||||
@ -43,6 +93,30 @@ 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')
|
||||
@ -65,8 +139,19 @@ local function checkConfig()
|
||||
end
|
||||
end
|
||||
|
||||
local function prepareTurtle()
|
||||
local function startMiner()
|
||||
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()
|
||||
@ -119,11 +204,11 @@ local function returnMineProcedure()
|
||||
return
|
||||
end
|
||||
|
||||
if robot.lastPositionState then
|
||||
if miner.robot.lastPositionState then
|
||||
local robotState = miner.robot.getState()
|
||||
|
||||
if robotState.y > miner.lastPositionState.y then
|
||||
miner.robot.down()
|
||||
robotForceDown()
|
||||
elseif robotState.z < miner.lastPositionState.z then
|
||||
robotForceForward()
|
||||
else
|
||||
@ -131,10 +216,12 @@ 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'
|
||||
@ -154,7 +241,7 @@ local function mineProcedure()
|
||||
-- 1. Move
|
||||
if robotState.y > targetY then
|
||||
turtle.digDown()
|
||||
miner.robot.down()
|
||||
robotForceDown()
|
||||
elseif robotState.dir == 'FORWARD' and robotState.z < targetZ then
|
||||
turtleUtils.digAll()
|
||||
robotForceForward()
|
||||
@ -185,11 +272,12 @@ local function mineProcedure()
|
||||
|
||||
for i=1, config.size - 1, 1 do
|
||||
robotForceForward()
|
||||
saveMinerState()
|
||||
end
|
||||
|
||||
miner.robot.turnRight()
|
||||
-- mine the next stage
|
||||
miner.robot.up()
|
||||
robotForceUp()
|
||||
miner.targetY = miner.targetY + 1
|
||||
end
|
||||
|
||||
@ -197,6 +285,10 @@ 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
|
||||
|
||||
@ -210,7 +302,7 @@ local function returnHomeProcedure()
|
||||
miner.robot.turnRight()
|
||||
end
|
||||
|
||||
miner.robot.up()
|
||||
robotForceUp()
|
||||
elseif robotState.x > 0 then
|
||||
if robotState.dir == 'FORWARD' then
|
||||
miner.robot.turnLeft()
|
||||
@ -220,6 +312,7 @@ local function returnHomeProcedure()
|
||||
|
||||
for i=1, robotState.x, 1 do
|
||||
robotForceForward()
|
||||
saveMinerState()
|
||||
end
|
||||
|
||||
miner.robot.turnLeft()
|
||||
@ -233,11 +326,31 @@ 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()
|
||||
prepareTurtle()
|
||||
loadMinerState()
|
||||
|
||||
print("> Miner program started, minimum percentgae fuel needed: " .. MIN_PERCENTAGE_NEEDED)
|
||||
if not miner.started then
|
||||
while not waitForUserConfirmation() do end
|
||||
end
|
||||
|
||||
startMiner()
|
||||
saveMinerState()
|
||||
|
||||
while not isProgramFinished() do
|
||||
if miner.mission == 'unload' then
|
||||
@ -246,11 +359,14 @@ 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
|
||||
|
||||
print("> Miner program finished!")
|
||||
minerFinished()
|
||||
271
mystical-upgrader.lua
Normal file
271
mystical-upgrader.lua
Normal file
@ -0,0 +1,271 @@
|
||||
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()
|
||||
31
notify-server.lua
Normal file
31
notify-server.lua
Normal file
@ -0,0 +1,31 @@
|
||||
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()
|
||||
16
refuel.lua
Normal file
16
refuel.lua
Normal file
@ -0,0 +1,16 @@
|
||||
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
118
robot.lua
@ -1,118 +0,0 @@
|
||||
-- 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
|
||||
2
startup.lua
Normal file
2
startup.lua
Normal file
@ -0,0 +1,2 @@
|
||||
-- shell.execute('/inferium-harvester.lua')
|
||||
-- shell.execute('/miner.lua')
|
||||
303
tunnels-miner.lua
Normal file
303
tunnels-miner.lua
Normal file
@ -0,0 +1,303 @@
|
||||
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)
|
||||
@ -1,63 +0,0 @@
|
||||
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
|
||||
16
upgrade.lua
Normal file
16
upgrade.lua
Normal file
@ -0,0 +1,16 @@
|
||||
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()
|
||||
Loading…
Reference in New Issue
Block a user