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 = {
|
local LIST_TURTLE_FILES = {
|
||||||
'turtle-utils.lua',
|
'refuel.lua',
|
||||||
'robot.lua',
|
'miner.lua',
|
||||||
'miner.lua'
|
'tunnels-miner.lua',
|
||||||
|
'coal-crafter.lua',
|
||||||
|
'mystical-upgrader.lua',
|
||||||
|
'inferium-harvester.lua',
|
||||||
|
'inferium-server.lua'
|
||||||
};
|
};
|
||||||
|
|
||||||
local LIST_CONFIG_FILES = {
|
local LIST_TURTLE_CONFIG_FILES = {
|
||||||
'config-miner.lua'
|
'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 REPO_PREFIX = 'https://git.trapcloud.fr/guillaumearm/minecraft-cc-tools/raw/branch/master/'
|
||||||
|
|
||||||
local previousDir = shell.dir()
|
local removeFiles = function(list)
|
||||||
|
for _, filePath in pairs(list) do
|
||||||
for _, filePath in pairs(LIST_FILES) do
|
if filePath then
|
||||||
fs.delete(filePath)
|
fs.delete(filePath)
|
||||||
shell.execute('wget', REPO_PREFIX .. filePath, filePath)
|
end
|
||||||
end
|
|
||||||
|
|
||||||
-- do not override existing config files
|
|
||||||
for _, filePath in pairs(LIST_CONFIG_FILES) do
|
|
||||||
if not fs.exists(filePath) then
|
|
||||||
shell.execute('wget', REPO_PREFIX .. filePath, filePath)
|
|
||||||
end
|
end
|
||||||
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 robotApi = require('libs/robot')
|
||||||
local turtleUtils = require('turtle-utils')
|
local turtleUtils = require('libs/turtle-utils')
|
||||||
local config = require('config-miner')
|
local config = require('config/mining')
|
||||||
|
|
||||||
local LOWER_SIZE_LIMIT = 2;
|
local LOWER_SIZE_LIMIT = 2;
|
||||||
local HIGHER_SIZE_LIMIT = 128;
|
local HIGHER_SIZE_LIMIT = 128;
|
||||||
local FUEL_NEEDED_MULTIPLIER = 4
|
|
||||||
local ADDITIONAL_FUEL_NEEDED = 1024
|
|
||||||
|
|
||||||
local INITIAL_TARGET_Y = config.targetY - config.startY
|
local INITIAL_TARGET_Y = config.targetY - config.startY
|
||||||
local TARGET_Z = config.size - 1
|
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_Y = INITIAL_TARGET_Y + (config.height - 1)
|
||||||
local MAX_Z = TARGET_Z
|
local MAX_Z = TARGET_Z
|
||||||
|
|
||||||
local robot = robotApi.create()
|
|
||||||
|
|
||||||
local MIN_PERCENTAGE_NEEDED = 10
|
local MIN_PERCENTAGE_NEEDED = 10
|
||||||
|
local TIME_TO_START = 3
|
||||||
|
|
||||||
local miner = {
|
local miner = {
|
||||||
robot = robot,
|
robot = robotApi.create(),
|
||||||
mission = 'unload', -- "unload" | "refuel" | "mine" | "return-home" | "return-mine" | nil
|
mission = 'unload', -- "unload" | "refuel" | "mine" | "return-home" | "return-mine" | nil
|
||||||
|
started = false,
|
||||||
finished = false,
|
finished = false,
|
||||||
lastPositionState = nil, -- { x, y, z, dir }
|
lastPositionState = nil, -- { x, y, z, dir }
|
||||||
targetY = INITIAL_TARGET_Y
|
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)
|
local function isOdd(x)
|
||||||
return (x % 2) == 1
|
return (x % 2) == 1
|
||||||
end
|
end
|
||||||
@ -43,6 +93,30 @@ local function robotForceForward()
|
|||||||
return miner.robot.forward()
|
return miner.robot.forward()
|
||||||
end
|
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()
|
local function checkConfig()
|
||||||
if config.targetY >= config.startY then
|
if config.targetY >= config.startY then
|
||||||
error('targetY should be lower than startY')
|
error('targetY should be lower than startY')
|
||||||
@ -65,8 +139,19 @@ local function checkConfig()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function prepareTurtle()
|
local function startMiner()
|
||||||
turtle.select(1)
|
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
|
end
|
||||||
|
|
||||||
local function isProgramFinished()
|
local function isProgramFinished()
|
||||||
@ -119,11 +204,11 @@ local function returnMineProcedure()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if robot.lastPositionState then
|
if miner.robot.lastPositionState then
|
||||||
local robotState = miner.robot.getState()
|
local robotState = miner.robot.getState()
|
||||||
|
|
||||||
if robotState.y > miner.lastPositionState.y then
|
if robotState.y > miner.lastPositionState.y then
|
||||||
miner.robot.down()
|
robotForceDown()
|
||||||
elseif robotState.z < miner.lastPositionState.z then
|
elseif robotState.z < miner.lastPositionState.z then
|
||||||
robotForceForward()
|
robotForceForward()
|
||||||
else
|
else
|
||||||
@ -131,10 +216,12 @@ local function returnMineProcedure()
|
|||||||
|
|
||||||
for i=1, miner.lastPositionState.x, 1 do
|
for i=1, miner.lastPositionState.x, 1 do
|
||||||
robotForceForward()
|
robotForceForward()
|
||||||
|
saveMinerState()
|
||||||
end
|
end
|
||||||
|
|
||||||
while miner.robot.getState().dir ~= miner.lastPositionState.dir do
|
while miner.robot.getState().dir ~= miner.lastPositionState.dir do
|
||||||
miner.robot.turnRight()
|
miner.robot.turnRight()
|
||||||
|
saveMinerState()
|
||||||
end
|
end
|
||||||
|
|
||||||
miner.mission = 'mine'
|
miner.mission = 'mine'
|
||||||
@ -154,7 +241,7 @@ local function mineProcedure()
|
|||||||
-- 1. Move
|
-- 1. Move
|
||||||
if robotState.y > targetY then
|
if robotState.y > targetY then
|
||||||
turtle.digDown()
|
turtle.digDown()
|
||||||
miner.robot.down()
|
robotForceDown()
|
||||||
elseif robotState.dir == 'FORWARD' and robotState.z < targetZ then
|
elseif robotState.dir == 'FORWARD' and robotState.z < targetZ then
|
||||||
turtleUtils.digAll()
|
turtleUtils.digAll()
|
||||||
robotForceForward()
|
robotForceForward()
|
||||||
@ -185,11 +272,12 @@ local function mineProcedure()
|
|||||||
|
|
||||||
for i=1, config.size - 1, 1 do
|
for i=1, config.size - 1, 1 do
|
||||||
robotForceForward()
|
robotForceForward()
|
||||||
|
saveMinerState()
|
||||||
end
|
end
|
||||||
|
|
||||||
miner.robot.turnRight()
|
miner.robot.turnRight()
|
||||||
-- mine the next stage
|
-- mine the next stage
|
||||||
miner.robot.up()
|
robotForceUp()
|
||||||
miner.targetY = miner.targetY + 1
|
miner.targetY = miner.targetY + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -197,6 +285,10 @@ local function mineProcedure()
|
|||||||
print('> Starting return home procedure because inventory is almost full...')
|
print('> Starting return home procedure because inventory is almost full...')
|
||||||
miner.mission = 'return-home'
|
miner.mission = 'return-home'
|
||||||
miner.lastPositionState = miner.robot.getState()
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -210,7 +302,7 @@ local function returnHomeProcedure()
|
|||||||
miner.robot.turnRight()
|
miner.robot.turnRight()
|
||||||
end
|
end
|
||||||
|
|
||||||
miner.robot.up()
|
robotForceUp()
|
||||||
elseif robotState.x > 0 then
|
elseif robotState.x > 0 then
|
||||||
if robotState.dir == 'FORWARD' then
|
if robotState.dir == 'FORWARD' then
|
||||||
miner.robot.turnLeft()
|
miner.robot.turnLeft()
|
||||||
@ -220,6 +312,7 @@ local function returnHomeProcedure()
|
|||||||
|
|
||||||
for i=1, robotState.x, 1 do
|
for i=1, robotState.x, 1 do
|
||||||
robotForceForward()
|
robotForceForward()
|
||||||
|
saveMinerState()
|
||||||
end
|
end
|
||||||
|
|
||||||
miner.robot.turnLeft()
|
miner.robot.turnLeft()
|
||||||
@ -233,11 +326,31 @@ local function returnHomeProcedure()
|
|||||||
end
|
end
|
||||||
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()
|
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
|
while not isProgramFinished() do
|
||||||
if miner.mission == 'unload' then
|
if miner.mission == 'unload' then
|
||||||
@ -246,11 +359,14 @@ while not isProgramFinished() do
|
|||||||
refuelProcedure()
|
refuelProcedure()
|
||||||
elseif miner.mission == 'mine' then
|
elseif miner.mission == 'mine' then
|
||||||
mineProcedure()
|
mineProcedure()
|
||||||
|
saveMinerState()
|
||||||
elseif miner.mission == 'return-home' then
|
elseif miner.mission == 'return-home' then
|
||||||
returnHomeProcedure()
|
returnHomeProcedure()
|
||||||
|
saveMinerState()
|
||||||
elseif miner.mission == 'return-mine' then
|
elseif miner.mission == 'return-mine' then
|
||||||
returnMineProcedure()
|
returnMineProcedure()
|
||||||
|
saveMinerState()
|
||||||
end
|
end
|
||||||
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