· 6 years ago · Dec 21, 2019, 08:00 AM
1local bit = require("bit")
2local fs = require("fs")
3local shell = require("shell")
4
5local brshift, blshift, band, bor = bit.blogic_rshift, bit.blshift, bit.band, bit.bor
6
7-- Written by funnbot on 12/20/19
8-- TODO: check if a modem is present and send display output through it
9
10--[[ Setup:
11 Place the turtle on the top-left in the area you are going to quarry.
12 Place a deposit chest behind the turtle.
13 Run the program with dimensions.
14
15 If you are reseting or moving a labelled turtle then you must run the reset command before starting again.
16]]
17
18--[[ Optional Setup:
19 Automatic restore
20 Write `edit startup` in the turtle cli, write `shell.run("quarry", "10", "10", "10")`, you can still use
21 optional arguments ie. `shell.run("quarry", "-whitelist", "-refuel", "10", "10", "10")`, This setup will allow
22 for automatic restores if the server is restarted or the chunk gets unloaded. Otherwise restore will still work if
23 you run the command yourself with the same dimensions.
24 -blacklist
25 The default blacklist is minecraft:stone minecraft:dirt
26 Create a file called blacklist.txt by writing `edit blacklist.txt` in the turtle cli,
27 then on each newline you can place a full block id ie. minecraft:stone,
28 Make sure you save the file.
29 The turtle will still have to mine any block that is in its way, however it will avoid blocks in the blacklist.
30 -whitelist
31 Create a file called whitelist.txt by writing `edit whitelist.txt` in the turtle cli,
32 then on each newline you can place a full block id ie. minecraft:stone,
33 Make sure you save the file.
34 The turtle will still have to mine any block that is in its way, however it will avoid blocks NOT in the whitelist.
35 -refuel
36 Place a refuel chest on the left side of the turtles starting position filled with coal.
37 -enderchest
38 The default whitelist is minecraft:stone
39 Place a ender chest (from ender storage) in the 16th item slot (bottom-right)
40 The turtle will place and break this block to unload its items, and does not require a chest placed behind its
41 starting position for usual unload
42]]
43
44--[[ Examples:
45 Quarry a 10x10x10 square> quarry 10 10 10
46 Quarry a 10x10x10 square and ignore block ids in the blacklist.txt file> quarry -blacklist 10 10 10
47 Quarry a 32x2x23 area, only mine block ids in the whitelist.txt file, and attempt to refuel from a chest to the left of the turtle
48 > quarry -whitelist -refuel 32 2 23
49 Quarry a 5x3x23 area, but place the turtle at the bottom and have it mine upwards
50 > quarry 5 -3 23
51]]
52
53-- Constants
54-- File names
55local stateFilename = "state.bin"
56local blacklistFilename = "blacklist.txt"
57local whitelistFilename = "whitelist.txt"
58
59-- Default tables
60local blacklist = { ["minecraft:stone"] = true, ["minecraft:dirt"] = true }
61local whitelist = { ["minecraft:stone"] = true }
62
63-- Global state
64-- Local position in relation to the starting position,
65-- dont want to require a GPS so no absolute positioning
66local pos = { x = 0, y = 0, z = 0 }
67-- Dimension parameters
68local dim = { x = 0, y = 0, z = 0 }
69-- optional parameters
70local optBlacklist, optWhitelist, optRefuel, optEnderChest = false,false,false,false
71
72-- {...} fills the args table with the arguments passed to the program
73local argv = { ... }
74local argc = #argv
75
76
77-- Print the usage for invalid commands
78if (argc ~= 1 and (argc < 3 or argc > 7)) or argv[1] == "help" then
79 print("Usage: "..shell.getRunningProgram().." [-blacklist] [-whitelist] [-refuel] [-enderchest] <width> <depth> <length>")
80 print(" [] - optional argument, <> - required argument. Do not include the brackets ([], <>) when actually running the command.")
81 print(" <width> - An int greater than zero, how far the quarry stretches to the right of the turtle")
82 print(" <depth> - An int greater than zero, how far the quarry stretches below the turtle. Make this value negative to quarry upwards.")
83 print(" -blacklist - Avoids breaking blocks in the blacklist.txt file, create this file in the root dir, and fill with block ids ie. minecraft:stone, each id on a newline.")
84 print(" -whitelist - Avoids breaking blocks NOT in the whitelist.txt file, creation and usage is the same as blacklist.txt except filename is whitelist.txt.")
85 print(" -refuel - Tries to pull fuel (coal) from a chest placed to the left of the turtles starting position.")
86 print("")
87 print("Reset Command: "..shell.getRunningProgram().." reset")
88 print(" Reset the saved state to start a new quarry, only necessary if the turtle is labelled")
89 return
90end
91
92if argv[1] == "reset" then
93 -- Deleting state file should be the only thing to reset
94 fs.delete(stateFilename)
95 print("Quarry state reset.")
96 return
97end
98
99-- Only gets run if there is no state to load, so the params are ignored.
100-- Return a bool for success
101local function loadAndValidateParameters()
102 -- Extra optional args to parse
103 if (argc - 3) > 0 then
104 -- loop from 1st element to argc-3
105 for i=1,argc-3 do
106 if argv[i] == "-blacklist" then optBlacklist = true
107 elseif argv[i] == "-whitelist" then optWhitelist = true
108 elseif argv[i] == "-refuel" then optRefuel = true
109 elseif argv[i] == "-enderchest" then optEnderChest = true
110 else
111 print("Invalid argument: "..argv[i]);
112 return false
113 end
114 end
115 end
116 -- Then parse the dimensions
117 dim.x = tonumber(argv[argc - 2])
118 dim.y = tonumber(argv[argc - 1])
119 dim.z = tonumber(argv[argc - 0])
120 -- validate dimensions
121 if dim.x <= 0 or dim.z <= 0 then
122 print("Invalid dimensions: must be greater than zero.")
123 return false
124 end
125
126 -- modf returns the int and fractional parts, ignore the int part, and test if fractional isnt zero
127 local _, xf, _, yf, _, zf = math.modf(dim.x), math.modf(dim.y), math.modf(dim.z)
128 if xf ~= 0 or yf ~= 0 or zf ~= 0 then
129 print("Invalid dimensions: floating point not allowed.")
130 return false
131 end
132 -- Success
133 return true
134end
135
136--# Read Text files
137
138-- Opens the file name, if it succeeds then it clears the outlist and writes new values = split(fileText, "\n")
139-- otherwise it won't edit the outList, so keep its default value
140local function readList(fileName, outList)
141
142end
143
144--# Restore State Functions
145
146-- writes num to filehandle
147local lowerByteMask = bit.bnot(255)
148local function writeInt(fileHandle, num)
149 -- cautionary, however its use in saveState means it will always be open
150 if fileHandle == nil then return end
151
152 -- Starting with the first 8 bits, mask then shift right
153 -- TEST: band might be unnecessary if the char function chops instead of wraps or something else wierd
154 local b1 = band(num, lowerByteMask)
155 num = brshift(num, 8)
156 local b2 = band(num, lowerByteMask)
157 num = brshift(num, 8)
158 local b3 = band(num, lowerByteMask)
159 num = brshift(num, 8)
160 local b4 = band(num, lowerByteMask)
161 num = brshift(num, 8)
162
163 fileHandle.write(string.char(b4, b3, b2, b1))
164end
165
166-- Save the current pos of the turtle, and the params given for a restore
167local function saveState()
168 -- w to overwrite if it exists or not
169 local stateFD = fs.open(stateFilename, "w");
170
171 if stateFD == nil then
172 print("Error: failed to create a file to save state. Filename: " .. stateFilename)
173 return
174 end
175
176 -- Order is constant and important
177 writeInt(stateFD, pos.x);
178 writeInt(stateFD, pos.y);
179 writeInt(stateFD, pos.z);
180 writeInt(stateFD, dim.x);
181 writeInt(stateFD, dim.y);
182 writeInt(stateFD, dim.z);
183
184 -- TEST: ternary operation is jank
185 local opts = 0
186 opts = bor(opts, optBlacklist and 0 or 2^0)
187 opts = bor(opts, optWhitelist and 0 or 2^1)
188 opts = bor(opts, optRefuel and 0 or 2^2)
189 opts = bor(opts, optEnderChest and 0 or 2^3)
190
191 -- Mask is just one byte
192 stateFD.write(opts)
193
194 stateFD.close()
195end
196
197-- Reads an int from the file handle (probably works for doubles aswell as they are both 64bit)
198local function readInt(fileHandle)
199 -- cautionary, however its use in loadState means it will always be open
200 if fileHandle == nil then return 0 end
201
202 -- TEST: first time working with lua bitwise no idea how funky it is
203 local b4, b3, b2, b1 = fileHandle.read(), fileHandle.read(), fileHandle.read(), fileHandle.read()
204 local num = blshift.byte(b4, 8 * 3)
205 num = bor(num, blshift(b3, 8* 2))
206 num = bor(num, blshift(b2, 8* 1))
207 num = bor(num, b1)
208
209 return num
210end
211
212-- Load the current pos and params from the state file if it exists
213-- Returns a boolean for success
214local function loadState()
215 -- Open file as read only to test if it exists
216 local stateFD = fs.open(stateFilename, "r")
217 -- Don't load state and instead keep pos at 0,0,0, and params as what was passed to the program
218 if stateFD == nil then return false end;
219
220 -- state ordering is constant
221 pos.x = readInt(stateFD)
222 pos.y = readInt(stateFD)
223 pos.z = readInt(stateFD)
224 dim.x = readInt(stateFD)
225 dim.y = readInt(stateFD)
226 dim.z = readInt(stateFD)
227
228 -- mask ordering is constant
229 local opts = stateFD.read();
230 optBlacklist = band(opts, 2^0)
231 optWhitelist = band(opts, 2^1)
232 optRefuel = band(opts, 2^2)
233 optEnderChest = band(opts, 2^3)
234
235 --TEST: is it necessary to close?
236 stateFD.close()
237 return true
238end
239
240--# Turtle Control Functions
241
242
243-- Main quarry section
244
245-- If no state to load then validate options provided
246--[[if not loadState() then
247 -- If validate fails then return early
248 if not loadAndValidateParameters() then return end
249end]]
250
251optEnderChest = true
252dim.x = 10
253saveState()
254optEnderChest = false
255dim.y = 10
256dim.x = 3
257optBlacklist = true
258loadState()
259print(optEnderChest)
260print(optBlacklist)
261print(dim.x)
262print(dim.y)