Compare commits

...

238 Commits
v1 ... master

Author SHA1 Message Date
Guillaume ARM
1b0b172e9a fix(inferium-harvester): several crashes on retrieveFertilizedEssences function 2024-06-01 17:05:45 +02:00
Guillaume ARM
1784bfb93e refactor(light-server): add generic applyCommand and createLoop 2024-05-28 02:11:45 +02:00
Guillaume ARM
1d882ada6d refactor(light-server): rename mainOutput and secondaryOutput into bundledOutput 2024-05-28 01:59:27 +02:00
Guillaume ARM
52970328c6 feat(light-server): change default delays 2024-05-28 01:52:39 +02:00
Guillaume ARM
56f860a282 fix(light-server): bad loop queue used for secondary room 2024-05-28 01:52:21 +02:00
Guillaume ARM
37c2491422 fix(light-server): last fix 2024-05-28 01:48:57 +02:00
Guillaume ARM
aaf40e2e77 fix(light-server): remove old code that break the new implementation 2024-05-28 01:46:18 +02:00
Guillaume ARM
2c224f29c6 fix(light-server): try to separate main loop and secondary loop 2024-05-28 01:38:59 +02:00
Guillaume ARM
9950ff6883 fix: bad secondary output for light-server 2024-05-28 01:27:25 +02:00
Guillaume ARM
b4a3abb210 fix(light-server): bad lib rs import 2024-05-28 01:26:30 +02:00
Guillaume ARM
ee8a5da868 fix(light-server): add mainLoopQueue
and try to process events after the animation execution
2024-05-28 01:22:59 +02:00
Guillaume ARM
c00d495460 fix(light-server): missing rs lib import 2024-05-28 01:11:48 +02:00
Guillaume ARM
7285c63b57 fix: broken rs lib 2024-05-28 01:05:40 +02:00
Guillaume ARM
46635141da feat: introduce basic light-server program 2024-05-28 00:43:31 +02:00
Guillaume ARM
ce90d6c69c feat(rs): introduce basic redstone library 2024-05-28 00:41:12 +02:00
Guillaume ARM
a4cc3d4c8e fix(tunnels-miner): missing config arg passed to ensureEnoughSpaceInInventory 2024-05-27 12:59:56 +02:00
Guillaume ARM
31846cf5c1 feat(notify): add notify-server 2024-05-26 20:37:25 +02:00
Guillaume ARM
b5aeb3532e feat(notify): add libs/notify 2024-05-26 20:30:13 +02:00
Guillaume ARM
154f96b676 feat(tunnels): add magnetite block to the whitelist 2024-05-26 19:56:59 +02:00
Guillaume ARM
d3946ad589 chore: change some default value on coal-crafter and mystical-upgrader 2024-05-26 19:54:50 +02:00
Guillaume ARM
3eca913146 fix: dmesg 2024-05-26 19:50:12 +02:00
Guillaume ARM
98bf2b2f31 feat(tunnels-miner): add onWaitCb on waitForInventory util + prepare for notification 2024-05-26 19:44:20 +02:00
Guillaume ARM
d257aa382c feat: add dmesg program utils 2024-05-26 19:33:22 +02:00
Guillaume ARM
abb746e2a6 feat: add refuel program 2024-05-26 19:31:46 +02:00
Guillaume ARM
c987dd1f5f fix(inferium-server): more robust stringify/parse processes 2024-05-26 18:46:24 +02:00
Guillaume ARM
8a2b7d210a fix(tunnels): bad config 2024-05-26 14:11:07 +02:00
Guillaume ARM
cc817f53ea feat(tunnels-miner): use turtleUtils.compactInventory 2024-05-26 14:07:41 +02:00
Guillaume ARM
0b2e412ad9 fix(tunnels-miner): try to select a non-empty slot when possible + use ensureWnoughSpaceInInventory everywhere 2024-05-26 14:06:17 +02:00
Guillaume ARM
7a8636f054 feat(tunnels-miner): drop items from inventory when full 2024-05-26 14:00:33 +02:00
Guillaume ARM
15764993e0 feat(tunnels): vein mode enabled by default 2024-05-26 13:52:49 +02:00
Guillaume ARM
834202e4cb feat(tunnels): add nether and end prosperity ore to the whitelist 2024-05-26 13:52:12 +02:00
Guillaume ARM
692b24d96d fix(tunnels-miner): better min fuel calculation with vein mode 2024-05-26 13:51:50 +02:00
Guillaume ARM
c81c15d427 feat(tunnels): add deepslate prosperity ore in config + remove cobblestone 2024-05-26 13:42:17 +02:00
Guillaume ARM
432bdddd4e fix(turtle-utils): forceUp and forceDown 2024-05-26 13:38:31 +02:00
Guillaume ARM
5e887959b0 feat(tunnels-miner): introduce vein miner mode 2024-05-26 13:28:34 +02:00
Guillaume ARM
6b6fcac1d1 fix(robot): loadRobotState should set the state using onLoad result 2024-05-26 05:15:16 +02:00
Guillaume ARM
37bfb2965a fix(tunnels-miner): goToNextMineProcedures should not use digForward function 2024-05-26 04:13:12 +02:00
Guillaume ARM
32783f7ebc fix(tunnels-miner): try to avoid torches + do not check for empty inventory each loop 2024-05-26 04:10:23 +02:00
Guillaume ARM
12a50ebf6a fix(tunnels-miner): bad orientation + crash 2024-05-26 04:05:00 +02:00
Guillaume ARM
4091084bea fix(tunnels-miner): refuel all from inventory at start 2024-05-26 03:54:09 +02:00
Guillaume ARM
1747263d26 fix(tunnels-miner): crash at first start 2024-05-26 03:50:55 +02:00
Guillaume ARM
c2e63d427a feat: implement first version of tunnels-miner 2024-05-26 03:34:10 +02:00
Guillaume ARM
edcab83d4e feat(robot): add turnBack method 2024-05-26 01:35:12 +02:00
Guillaume ARM
69a04a19d5 feat(turtle-utils): better getFuelPercentage for display 2024-05-26 01:21:00 +02:00
Guillaume ARM
c030b03a11 feat(tutle-utils): add digAllUp method 2024-05-26 00:55:28 +02:00
Guillaume ARM
c639157bdb fix(robot): custom onSave + onLoad 2024-05-26 00:35:32 +02:00
Guillaume ARM
a78ed6dba3 feat(tutle-utils): add isInventoryFull method 2024-05-26 00:34:02 +02:00
Guillaume ARM
c5dcfaf2b7 feat(tutle-utils): digAll return value 2024-05-26 00:07:09 +02:00
Guillaume ARM
83a819c038 feat(inferium-harvester): add 3s delay at start 2024-05-25 23:58:37 +02:00
Guillaume ARM
50cecdee4d feat(miner): add 3s delay at start 2024-05-25 23:57:21 +02:00
Guillaume ARM
ffccce4700 feat(robot): add config.onSave function 2024-05-25 23:43:45 +02:00
Guillaume ARM
f6ab6d6f77 feat(robot): add config with autoload/autosave props 2024-05-25 23:38:25 +02:00
Guillaume ARM
4062c87f48 fix(robot): bug in mutateRobotPosition due to typo 2024-05-25 23:31:38 +02:00
Guillaume ARM
494c32faf6 feat(robot): add saveRobotState and loadRobotState methods 2024-05-25 23:22:05 +02:00
Guillaume ARM
022f778f0b chore: add missing robot.back method
and keep robot.backward as an alias
2024-05-25 23:13:00 +02:00
Guillaume ARM
3181d68839 feat(mystical-upgrader): improve moves 2024-05-25 22:35:15 +02:00
Guillaume ARM
55c8e44a27 feat(mystical-upgrader): more compact printed report 2024-05-25 22:30:23 +02:00
Guillaume ARM
12c6f1d2c0 fix(mystical-upgrader): multiple rows loop 2024-05-25 22:16:37 +02:00
Guillaume ARM
744cf941ad fix(mystical-upgrader): assertEnoughFuel 2024-05-25 22:13:06 +02:00
Guillaume ARM
3df6cfa706 fix(mystical-upgrader): replace the first picked block correctly 2024-05-25 22:08:27 +02:00
Guillaume ARM
c7d0f5681a feat(mystical-upgrader): replace first picked item by the correct block 2024-05-25 22:02:27 +02:00
Guillaume ARM
1da3d12cdc feat(mystical-upgrader): multiple rows + refactor 2024-05-25 21:48:34 +02:00
Guillaume ARM
8e053ddacf chore: rename inferium-upgrader into mystical-upgrader 2024-05-25 21:31:14 +02:00
Guillaume ARM
b00180d780 chore: cleanup old/simple-harvester 2024-05-25 21:29:55 +02:00
Guillaume ARM
aba45e6b47 fix: set default firstCropZ to 1 in inferium config 2024-05-25 21:25:31 +02:00
Guillaume ARM
1183442712 fix(CountersSelectors): display selected line on other pages 2024-05-25 13:06:14 +02:00
Guillaume ARM
cd2e624182 fix(CountersSelectors): fix dropN function 2024-05-25 12:52:14 +02:00
Guillaume ARM
626a195bb4 fix(inferium-harvester): dropAllProcedure 2024-05-24 19:05:44 +02:00
Guillaume ARM
1748b29bbd feat(coal-creafter): dropAll should stop try dropping when a slot is empty 2024-05-24 19:02:33 +02:00
Guillaume ARM
a1364a4bff fix(coal-creafter): be sure to select slot 1 + fix dropAll 2024-05-24 18:46:17 +02:00
Guillaume ARM
6a50fef10a feat(coal-creafter): dropAll at start 2024-05-24 18:29:01 +02:00
Guillaume ARM
802c8e2a43 fix(coal-crafter): bad findStorageChestOrientation implementation 2024-05-24 18:15:24 +02:00
Guillaume ARM
201831b16c feat(coal-crafter)!: buffer chest under the turtle 2024-05-24 18:09:41 +02:00
Guillaume ARM
43d931994a refactor: introduce new shared inferium lib 2024-05-24 17:27:46 +02:00
Guillaume ARM
58bb9d6d24 refactor(inferium-harvester): add turtleUtils.isSeedInSlot 2024-05-24 17:15:39 +02:00
Guillaume ARM
702dd4833a feat(inferium-server): final implemention of listAvailableSeeds 2024-05-24 17:09:42 +02:00
Guillaume ARM
6b36dd3c92 perf(inferium-harvester): do not continue when all needed seeds are removed 2024-05-24 16:35:26 +02:00
Guillaume ARM
4502db1ba1 perf(inferium-harvester): do not continue replant when inventory is empty 2024-05-24 16:33:56 +02:00
Guillaume ARM
88d7b6dcc8 refactor(inferium)!: replace get-config by register-and-get-config 2024-05-24 02:24:39 +02:00
Guillaume ARM
9b9b312a2e feat(inferium-gui): list available seeds and prepare initial countersMap 2024-05-24 02:07:21 +02:00
Guillaume ARM
8687ec057b feat(inferium-server): add list-seeds route 2024-05-24 01:56:54 +02:00
Guillaume ARM
2febed4d6e fix(inferium-gui): saveAllConfigs error handling 2024-05-24 01:40:47 +02:00
Guillaume ARM
61ec9d5d59 fix(inferium-gui): error message when server cannot save the config 2024-05-24 01:36:56 +02:00
Guillaume ARM
523f4b7775 fix(inferium-gui): crash on exit 2024-05-24 01:32:57 +02:00
Guillaume ARM
99d9551a68 fix(inferium-gui): broken parseSeedName when value is falsy 2024-05-24 01:28:07 +02:00
Guillaume ARM
566df84c75 fix(inferium-server): broken isValidConfig that never return true 2024-05-24 01:26:58 +02:00
Guillaume ARM
1d36a684bf fix(CountersSelector): reset cursor position 2024-05-24 01:26:10 +02:00
Guillaume ARM
7fbbf6f028 feat(inferium-gui): implement saveAllConfigs 2024-05-24 01:15:44 +02:00
Guillaume ARM
a8ef9f7d32 fix(inferium-gui): broken preparePayloads function 2024-05-24 01:10:56 +02:00
Guillaume ARM
472415a59f feat(inferium-gui): implement preparePayloads 2024-05-24 01:08:25 +02:00
Guillaume ARM
3ef5930f4f feat(libs): add utils.rep 2024-05-24 01:08:03 +02:00
Guillaume ARM
f9fb8e26df feat(libs): add utils.merge and utils.concat 2024-05-24 00:26:28 +02:00
Guillaume ARM
f1174fb6a2 fix(inferium-gui): fix createCountersMap function 2024-05-24 00:19:05 +02:00
Guillaume ARM
309cc3aae4 fix(inferium-gui): handle get-config/response correctly 2024-05-24 00:08:56 +02:00
Guillaume ARM
491d85c951 feat(inferium-gui): fetch all configs 2024-05-23 23:59:41 +02:00
Guillaume ARM
28ea977b86 refactor(ui): cleanup + prepare inferium-gui final implementation 2024-05-23 23:34:36 +02:00
Guillaume ARM
c0ea32bf17 fix(inferium-gui): missing width for centerString 2024-05-23 23:22:40 +02:00
Guillaume ARM
8ece6948e5 fix(CountersSelector): better window clear strategy 2024-05-23 23:19:56 +02:00
Guillaume ARM
bd843779e2 feat(inferium-gui): centered title 2024-05-23 22:54:16 +02:00
Guillaume ARM
598bf7bfa5 fix(CountersSelector): broken pageDown 2024-05-23 22:47:41 +02:00
Guillaume ARM
4b7a38ebf0 feat(CountersSelectors): add pageup/pagedown/home/end shortcuts 2024-05-23 22:46:24 +02:00
Guillaume ARM
64176024f3 feat(CountersSelectors): add 'delete' shortcut + change 'm' 2024-05-23 22:38:29 +02:00
Guillaume ARM
642604da9d fix(CountersSelector): 'q' shortcut last fixes 2024-05-23 22:34:46 +02:00
Guillaume ARM
f45d979312 fix(CountersSelector): fix broken 'q' shortcut 2024-05-23 22:30:40 +02:00
Guillaume ARM
8d4768813b feat(inferium-gui): add basic titleFn 2024-05-23 22:27:18 +02:00
Guillaume ARM
69b31f24a9 feat(CountersSelector): add 'm' shortcut 2024-05-23 22:19:38 +02:00
Guillaume ARM
56558fad7a feat(CountersSelector): add 'r' and 'q' + refactor 2024-05-23 22:10:57 +02:00
Guillaume ARM
4903f0a96c fix(CountersSelector): bad writeLine function 2024-05-23 21:58:41 +02:00
Guillaume ARM
7f7d3e801e feat(CountersSelector): refactor + better title bar 2024-05-23 21:55:36 +02:00
Guillaume ARM
ab17e80fa5 fix(CountersSelectors): createWriteWithColor 2024-05-23 21:19:28 +02:00
Guillaume ARM
5501e06844 fix(CountersSelector): bad selectedPage calculation 2024-05-23 21:02:20 +02:00
Guillaume ARM
b2beb2fdbb fix(ui): better rendering result for CountersSelector 2024-05-23 20:58:01 +02:00
Guillaume ARM
b8c83bc959 fix(ui): create a window for the CountersSelector 2024-05-23 20:43:09 +02:00
Guillaume ARM
ddea713ae4 chore: test inferium-gui 2024-05-23 20:17:36 +02:00
Guillaume ARM
e451631810 fix(ui): broken getTotalCount function 2024-05-23 20:14:47 +02:00
Guillaume ARM
be9e57ebd7 fix(ui): CountersSelectors add 'name' and 'count' in countersMap payload 2024-05-23 20:11:35 +02:00
Guillaume ARM
b1c24ee6a2 fix(ui): bad omit implementation 2024-05-23 19:15:44 +02:00
Guillaume ARM
11831c9054 fix(ui): CountersSelector selectedPage calculation 2024-05-23 19:00:10 +02:00
Guillaume ARM
6af0669b99 feat: introduce libs/ui/CountersSelector and try it in inferium-gui 2024-05-23 18:04:45 +02:00
Guillaume ARM
30c1da50e5 fix(inferium-server): crash with shallowClone when a config does not exist 2024-05-22 21:39:06 +02:00
Guillaume ARM
9e09ac85c3 fix(inferium-server): delete-config should work with a number id 2024-05-22 21:35:41 +02:00
Guillaume ARM
dd79ecb941 fix(inferium-server): do not consider new harvester if a computer id is given 2024-05-22 21:31:47 +02:00
Guillaume ARM
a28ccbc920 feat(inferium-server): add delete-config endpoint 2024-05-22 21:10:57 +02:00
Guillaume ARM
b88bc8a023 fix(inferium)!: rename getconfig in get-config 2024-05-22 21:07:13 +02:00
Guillaume ARM
49669c6e59 feat(inferium-server): add setconfig 2024-05-22 21:05:55 +02:00
Guillaume ARM
3bdfec3eda feat(inferium-server): add listharvesters 2024-05-22 20:54:14 +02:00
Guillaume ARM
877773bb5a chore: change install script and split different install files
and add empty inferium-gui program
2024-05-22 20:44:36 +02:00
Guillaume ARM
b2658117fd fix(inferium-server): length of the plan is automatically calculated 2024-05-22 19:41:26 +02:00
Guillaume ARM
c555e4e0fc chore: better logs when a new harvester is detected 2024-05-22 19:37:58 +02:00
Guillaume ARM
43c936bf3b fix(inferium-server): create persisted configs file if don't exist 2024-05-22 19:34:54 +02:00
Guillaume ARM
baba577b62 perf(net): compact data sent to the network 2024-05-22 19:32:57 +02:00
Guillaume ARM
24ffcce68d feat(inferium-server): configs are persisted 2024-05-22 19:31:53 +02:00
Guillaume ARM
77210784ff fix(inferium-harvester): handle when a turtle is blocked during forward 2024-05-22 19:31:17 +02:00
Guillaume ARM
f38f3dc7bf fix(inferium-harvester): forget to pass config 2024-05-22 18:54:21 +02:00
Guillaume ARM
90a3ac864e feat(inferium): replace getplan by getconfig 2024-05-22 18:40:00 +02:00
Guillaume ARM
38b9c24bd6 refactor(inferium-harvester): dynamic config (but still local) 2024-05-22 18:24:33 +02:00
Guillaume ARM
f988356750 feat(install): remove listed old filed during installation 2024-05-22 18:04:32 +02:00
Guillaume ARM
ddf028b4a4 chore: update README 2024-05-22 01:17:36 +02:00
Guillaume ARM
6ca8bacaf6 chore: update default startup.lua file 2024-05-22 01:16:21 +02:00
Guillaume ARM
467378f0c9 feat(inferium-harvester): add config.fertilizedBoost 2024-05-22 01:15:20 +02:00
Guillaume ARM
a29f8ac5cb fix(inferium-harvester): display of the harvest state 2024-05-22 00:17:49 +02:00
Guillaume ARM
0a9294310a feat(inferium-harvester): better logs on refuel, harvest and remove seeds 2024-05-22 00:15:29 +02:00
Guillaume ARM
b8fb2390f2 fix(inferium-harvester): better error handling for fetchRemotePlan 2024-05-21 23:52:09 +02:00
Guillaume ARM
9bd5bbc127 fix(inferium-server): bug when evaluating the type of a message 2024-05-21 23:49:11 +02:00
Guillaume ARM
7233ed7eb5 fix(inferium-harvester): avoid printing the same error message each time 2024-05-21 23:45:44 +02:00
Guillaume ARM
b9144d6847 fix(net): QUERY_PROTO was not used by sendQuery 2024-05-21 23:44:24 +02:00
Guillaume ARM
97bb32b544 chore(inferium-harvester): better debug messages 2024-05-21 23:41:43 +02:00
Guillaume ARM
6071fdb6b4 feat(inferium-harvester): fetch the remote plan before getting the local plan 2024-05-21 23:39:12 +02:00
Guillaume ARM
11b6f140e1 chore: add some debug messages 2024-05-21 23:37:35 +02:00
Guillaume ARM
cf39e16fa3 fix(inferium-server): server crash at start 2024-05-21 23:35:30 +02:00
Guillaume ARM
a4a33d1eef feat(inferium-harvester): improve printed messages + fuel management at start 2024-05-21 23:27:57 +02:00
Guillaume ARM
9dd106763e feat! migrate inferium-harvester and inferium-server to rednet 2024-05-21 23:08:12 +02:00
Guillaume ARM
cfea9f575f feat(net): add openRednet and closeRednet utils 2024-05-21 22:51:47 +02:00
Guillaume ARM
4f16cef9f5 fix(net): bad channel + change return of sendQuery 2024-05-21 22:34:50 +02:00
Guillaume ARM
5305afe203 feat(net): stopServer function for net.listenQuery 2024-05-21 21:58:49 +02:00
Guillaume ARM
3f92309ac6 feat: add new lib net.lua 2024-05-21 21:51:02 +02:00
Guillaume ARM
6294b38806 feat(inferium-server): add a warning when computerId is not known by the system 2024-05-21 20:24:09 +02:00
Guillaume ARM
cdad0204ba fix(inferium-harvester): replant only when needed 2024-05-21 20:08:07 +02:00
Guillaume ARM
cc1d083aa7 fix(inferium-server): computerId should be stringified 2024-05-21 19:12:03 +02:00
Guillaume ARM
efeba6f4cb fix(inferium-plans): default plan 2024-05-21 19:09:11 +02:00
Guillaume ARM
e628ec2153 feat(inferium-server)!: support of multi computer plans 2024-05-21 18:52:07 +02:00
Guillaume ARM
b075b953ab feat(inferium-harvester): crash the process when no response from the inferium server 2024-05-21 18:51:02 +02:00
Guillaume ARM
d4cbe6e808 feat(inferium-harvester): do not block harvest procedure when no crop 2024-05-21 18:02:27 +02:00
Guillaume ARM
4af7ae8cfe feat(inferium-harvester): do not wait for crops 2024-05-21 17:56:27 +02:00
Guillaume ARM
d16a9b2587 feat(inferium-harvester): update the local plan on the go 2024-05-21 17:48:59 +02:00
Guillaume ARM
be9c2c10a1 fix(inferium-harvester): shallow clone of the local plan to avoid errors 2024-05-21 17:43:22 +02:00
Guillaume ARM
f483c41c88 chore: remove energySaving mode 2024-05-21 17:42:58 +02:00
Guillaume ARM
8f14f48914 chore(inferium-server): change printed messages 2024-05-21 00:59:28 +02:00
Guillaume ARM
87188093f4 chore(inferium-server): print a message to the user 2024-05-21 00:57:21 +02:00
Guillaume ARM
e883deba5a feat(inferium-harvester): implement fetchRemotePlan 2024-05-20 22:26:53 +02:00
Guillaume ARM
cdb0e6e84f feat: implement basic inferium server 2024-05-20 22:19:27 +02:00
Guillaume ARM
5e2234da78 feat(inferium-harvester): add defaultRemotePlan to the static config 2024-05-20 21:08:10 +02:00
Guillaume ARM
340d5f4cff chore(inferium-harvester): remove print messages 2024-05-20 21:05:10 +02:00
Guillaume ARM
930b104522 fix: replace utils.difference by utils.shallowDiff 2024-05-20 20:51:37 +02:00
Guillaume ARM
e9ebf63c55 feat: create utils lib 2024-05-20 20:26:43 +02:00
Guillaume ARM
b1c3eac1b9 chore: retry with old implementation of difference 2024-05-20 20:21:09 +02:00
Guillaume ARM
a8788ca3b1 fix(inferium-harvester): set local plan to remote plan at the end of the replant procedure 2024-05-20 20:08:13 +02:00
Guillaume ARM
86baeb83f0 chore: try with a new implementation of difference 2024-05-20 19:58:11 +02:00
Guillaume ARM
997fe76fd1 fix(inferium-harvester): add sizeof util to compute the size of a table 2024-05-20 19:54:31 +02:00
Guillaume ARM
586b656fd7 chore: debug 2024-05-20 19:49:00 +02:00
Guillaume ARM
0f78c1c994 chore(inferium-harvester): add some print debug messages 2024-05-20 19:47:45 +02:00
Guillaume ARM
6c38ea6845 fix(inferium-harvester): isSeed 2024-05-20 19:43:23 +02:00
Guillaume ARM
c42b8472dc fix: fakeRemotePlan 2024-05-20 19:41:40 +02:00
Guillaume ARM
865d6b4f5b fix(inferium-harvester): removeFirst util 2024-05-20 19:33:14 +02:00
Guillaume ARM
f7a83ef911 fix(inferium-harvester): retrieveHomePositionProcedure 2024-05-20 19:32:04 +02:00
Guillaume ARM
6b4e4f248d fix(inferium-harvester): replant procedure 2024-05-20 19:26:32 +02:00
Guillaume ARM
daa36c82de fix(inferium-harvester): syntax errors 2024-05-20 19:15:57 +02:00
Guillaume ARM
0b724c7334 feat(inferium-harvester)!: add replant procedure 2024-05-20 19:03:34 +02:00
Guillaume ARM
51e93081a1 chore: remove old TODO comment 2024-05-20 04:47:53 +02:00
Guillaume ARM
a4f2d4d27c fix(coal-crafter): printed message 2024-05-20 04:25:35 +02:00
Guillaume ARM
381b438da2 fix(coal-crafter): bad printed message 2024-05-20 04:24:24 +02:00
Guillaume ARM
3631dfcea1 fix(coal-crafter): reduce NB_NEEDED_COAL threshold 2024-05-20 02:19:35 +02:00
Guillaume ARM
cd0e42e34c fix(coal-crafter): waitForNotEnoughCoal 2024-05-20 02:14:38 +02:00
Guillaume ARM
694a23fb68 feat(coal-crafter): stop crafting coal when a certain threshold is reached 2024-05-20 02:04:25 +02:00
Guillaume ARM
d971f0acf3 fix(coal-crafter): add missing suck item 2024-05-20 01:55:28 +02:00
Guillaume ARM
e02a7a70c2 fix(coal-crafter): implement strictTransferOne 2024-05-20 01:53:31 +02:00
Guillaume ARM
98cb51510f fix(coal-crafter): transfer exactly 8 essences 2024-05-20 01:53:14 +02:00
Guillaume ARM
b81c0b6921 fix(coal-crafter): bad itemName 2024-05-20 01:48:14 +02:00
Guillaume ARM
eee19c8a48 fix(coal-crafter): syntax error + bad import 2024-05-20 01:46:15 +02:00
Guillaume ARM
084b6d941f fix(coal-crafter): storage inventory should be on the back side 2024-05-20 01:38:59 +02:00
Guillaume ARM
115ffd2e78 feat: add coal-crafter 2024-05-20 01:28:22 +02:00
Guillaume ARM
e5e63f4223 fix(inferium-harvester): wait for bottom inventory before dropping items 2024-05-20 00:07:06 +02:00
Guillaume ARM
ad14b3f521 fix(inferium-harvester): fuel chest should be bottom 2024-05-19 23:59:40 +02:00
Guillaume ARM
5216041e76 feat(inferium-harvester): better refuel procedure using buffer chest 2024-05-19 23:53:50 +02:00
Guillaume ARM
ea4f132f2e fix(turtle-utils): refuelWithBuffer check if fuelSlot is nil 2024-05-19 23:51:20 +02:00
Guillaume ARM
799d1654c2 fix(turtle-utils): refuelWithBuffer 2024-05-19 23:47:35 +02:00
Guillaume ARM
3b674110a3 fix(turtle-utils): syntax error 2024-05-19 23:45:59 +02:00
Guillaume ARM
e7a95dcb44 feat(turtle-utils): add refuelWithBuffer 2024-05-19 23:45:22 +02:00
Guillaume ARM
2ee725388a feat(turtle-utils): add isFuelItem 2024-05-19 23:42:57 +02:00
Guillaume ARM
fd72a34ecb docs: update README 2024-05-19 22:07:40 +02:00
Guillaume ARM
710b681738 docs: update README 2024-05-19 22:05:59 +02:00
Guillaume ARM
be8ab78425 perf: compact only if needed 2024-05-19 22:04:27 +02:00
Guillaume ARM
66968b0186 fix: install script 2024-05-19 20:20:22 +02:00
Guillaume ARM
f322b42af2 feat: add upgrade script 2024-05-19 20:17:27 +02:00
Guillaume ARM
b1cbacc920 feat: add harvesting config file 2024-05-19 20:12:25 +02:00
Guillaume ARM
70854f7a75 fix(turtle-utils): getItemName and compactInventory 2024-05-19 19:48:46 +02:00
Guillaume ARM
f76877b0ab feat(turtle-utils): compactInventory now returnthe number of freed slots 2024-05-19 19:42:17 +02:00
Guillaume ARM
8483ffc696 feat(turtle-utils): add compactInventory 2024-05-19 19:37:16 +02:00
Guillaume ARM
66e9eff8ac feat(inferium-harvester): add energySavingMode 2024-05-19 18:35:01 +02:00
Guillaume ARM
9770321c20 fix(inferium-harvester): bug on harvest procedure 2024-05-19 17:20:44 +02:00
Guillaume ARM
467a1c9168 fix(inferium-harvester): use placeDown from turtle api 2024-05-19 17:15:35 +02:00
Guillaume ARM
e20c7fd073 fix(turtle-utils): getMatureCrop implementation 2024-05-19 17:14:07 +02:00
Guillaume ARM
1438db9973 fix(inferium-harvester): get seedName from cropName 2024-05-19 17:10:21 +02:00
Guillaume ARM
88d8fae296 fix(turtle-utils): waitForMatureCrop 2024-05-19 17:04:37 +02:00
Guillaume ARM
1e9536b089 fix(inferium-harvester): crash with waitFor util + better refuel procedure 2024-05-19 17:00:59 +02:00
Guillaume ARM
f811031db5 feat: first version of inferium harvester 2024-05-19 16:42:52 +02:00
Guillaume ARM
0c2b64beb8 fix: move simple-harvester.lua in old directory 2024-05-19 14:19:06 +02:00
Guillaume ARM
0338aeae67 fix: config/mining.lua 2024-05-19 13:24:24 +02:00
Guillaume ARM
8a3e422c9d fix: install script 2024-05-19 13:23:32 +02:00
Guillaume ARM
27afb3eade refactor: better install script 2024-05-19 13:22:34 +02:00
Guillaume ARM
7dc979f8c4 feat: add inferium-upgrader 2024-05-19 13:22:24 +02:00
Guillaume ARM
6b8cec89e8 feat: add simple-harvester 2024-05-19 13:18:55 +02:00
Guillaume ARM
e8ce29bad6 refactor: create libs and config folders 2024-05-19 13:15:54 +02:00
Guillaume ARM
7a7542e851 feat: add startup.lua to automatically start the mining process 2024-05-11 22:42:57 +02:00
Guillaume ARM
84e497300e fix: change user confirmation "MINE" to "yes" 2024-05-11 16:54:16 +02:00
Guillaume ARM
809f2a78c0 fix: miner serialize/unserialize 2024-05-11 16:49:25 +02:00
Guillaume ARM
9e3a356eca feat: add confirmation by the user + persist started state 2024-05-11 15:31:51 +02:00
Guillaume ARM
bedd5adbcb feat!: persist miner state 2024-05-11 15:12:18 +02:00
28 changed files with 3670 additions and 218 deletions

View File

@ -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
View 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
View 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
View 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
}
}

3
dmesg.lua Normal file
View File

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

251
inferium-gui.lua Normal file
View 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
View 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
View 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')

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View File

@ -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
View 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
View 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
View 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
View File

@ -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
View File

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

303
tunnels-miner.lua Normal file
View 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)

View File

@ -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
View 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()