· 5 years ago · May 31, 2020, 10:12 AM
1local tArg = {...}
2local outputPath, file = tArg[1] and fs.combine(shell.dir(), tArg[1]) or shell.getRunningProgram()
3local safeColorList = {[colors.white] = true,[colors.lightGray] = true,[colors.gray] = true,[colors.black] = true}
4local stc = function(color) if (term.isColor() or safeColorList[color]) then term.setTextColor(color) end end
5local archive = textutils.unserialize("{\
6 mainFile = false,\
7 compressed = false,\
8 data = {\
9 [ \"games/etc/apps.db\" ] = \"{\\\
10 [ \\\"462e1d985e9adb99f4bbb4455d99329900450224\\\" ] = {\\\
11 title = \\\"SameGame\\\",\\\
12 category = \\\"Games\\\",\\\
13 run = \\\"sameGame\\\",\\\
14 iconExt = \\\"\\\\132\\\\132\\\\132\\\\132\\\\132\\\\132\\\\132\\\\132\\\\010\\\\030b\\\\031e\\\\148\\\\030a\\\\031d\\\\139\\\\030b\\\\031a\\\\154\\\\030c\\\\031b\\\\139\\\\030e\\\\031c\\\\138\\\\030d\\\\140\\\\030e\\\\031a\\\\139\\\\030f\\\\031b\\\\149\\\\010\\\\030a\\\\031e\\\\145\\\\030e\\\\031d\\\\143\\\\130\\\\030c\\\\149\\\\030b\\\\031e\\\\156\\\\030d\\\\031b\\\\149\\\\030a\\\\031d\\\\132\\\\030f\\\\031e\\\\149\\\",\\\
15 },\\\
16 [ \\\"f6a5201214fb1981e6d46a39303bb676325eb59b-ME2\\\" ] = {\\\
17 title = \\\"Othello\\\",\\\
18 category = \\\"Games\\\",\\\
19 run = \\\"Othello\\\",\\\
20 iconExt = \\\"\\\\030 \\\\031 \\\\128\\\\030d\\\\151\\\\031d\\\\128\\\\031f\\\\144\\\\031 \\\\131\\\\131\\\\143\\\\143\\\\010\\\\030d\\\\031 \\\\151\\\\0300\\\\031d\\\\159\\\\030d\\\\031f\\\\130\\\\0300\\\\031d\\\\153\\\\030f\\\\139\\\\030d\\\\0310\\\\132\\\\031f\\\\132\\\\030 \\\\031d\\\\133\\\\010\\\\030 \\\\031d\\\\131\\\\131\\\\143\\\\143\\\\030d\\\\031f\\\\129\\\\031d\\\\128\\\\030 \\\\133\\\\031 \\\\128\\\",\\\
21 },\\\
22 [ \\\"785af2a4ad3c4ee912623c6e0b6d4299ea305bf6\\\" ] = {\\\
23 title = \\\"Pipes\\\",\\\
24 category = \\\"Games\\\",\\\
25 run = \\\"Pipes\\\",\\\
26 iconExt = \\\"\\\\030 \\\\031 \\\\128\\\\0300\\\\0310\\\\128\\\\030 \\\\031 \\\\128\\\\0310\\\\143\\\\0300\\\\031 \\\\130\\\\030 \\\\128\\\\0300\\\\0310\\\\128\\\\010\\\\0300\\\\031 \\\\131\\\\0310\\\\128\\\\031 \\\\131\\\\131\\\\030 \\\\0310\\\\159\\\\031 \\\\128\\\\0310\\\\143\\\\010\\\\030 \\\\031 \\\\128\\\\0315\\\\143\\\\031 \\\\128\\\\128\\\\128\\\\128\\\\0300\\\\131\\\",\\\
27 },\\\
28 [ \\\"a2accffe95b2c8be30e8a05e0c6ab7e8f5966f43\\\" ] = {\\\
29 title = \\\"Strafe\\\",\\\
30 category = \\\"Games\\\",\\\
31 icon = \\\"\\\\0308\\\\031f \\\\0300 \\\\0308 \\\\010\\\\0308\\\\031f \\\\0300 \\\\030f \\\\010\\\\0300\\\\031f \\\\030f \\\",\\\
32 iconExt = \\\"\\\\0308\\\\0318\\\\128\\\\0300\\\\159\\\\129\\\\0310\\\\128\\\\0308\\\\159\\\\129\\\\0318\\\\128\\\\010\\\\0300\\\\0318\\\\135\\\\0310\\\\128\\\\128\\\\030f\\\\135\\\\0300\\\\031f\\\\143\\\\159\\\\030f\\\\0310\\\\144\\\\010\\\\0300\\\\128\\\\030f\\\\159\\\\129\\\\138\\\\0300\\\\031f\\\\143\\\\149\\\\030f\\\\0310\\\\134\\\",\\\
33 run = \\\"Strafe\\\",\\\
34 },\\\
35 [ \\\"48d6857f6b2869d031f463b13aa34df47e18c548\\\" ] = {\\\
36 title = \\\"Breakout\\\",\\\
37 category = \\\"Games\\\",\\\
38 icon = \\\"\\\\0301\\\\031f \\\\0309 \\\\030c \\\\030b \\\\030e \\\\030c \\\\0306 \\\\010\\\\030 \\\\031f \\\\010\\\\030 \\\\031f \\\\0300 \\\\0310 \\\",\\\
39 iconExt = \\\"\\\\030 \\\\0319\\\\144\\\\030d\\\\031 \\\\159\\\\030b\\\\159\\\\030 \\\\0311\\\\144\\\\031b\\\\144\\\\030c\\\\031 \\\\159\\\\030 \\\\0311\\\\144\\\\010\\\\030 \\\\0311\\\\130\\\\031b\\\\129\\\\0319\\\\130\\\\031e\\\\130\\\\0310\\\\144\\\\031d\\\\129\\\\0316\\\\129\\\\010\\\\030 \\\\136\\\\140\\\\140\\\\031 \\\\128\\\\128\\\\128\\\\128\\\",\\\
40 run = \\\"Breakout\\\",\\\
41 },\\\
42 [ \\\"53a5d150062b1e03206b9e15854b81060e3c7552\\\" ] = {\\\
43 title = \\\"Minesweeper\\\",\\\
44 category = \\\"Games\\\",\\\
45 icon = \\\"\\\\030f\\\\031f \\\\03131\\\\0308\\\\031f \\\\030f\\\\031d2\\\\010\\\\030f\\\\031f \\\\031d2\\\\03131\\\\0308\\\\031f \\\\030f\\\\03131\\\\010\\\\030f\\\\03131\\\\0308\\\\031f \\\\030f\\\\03131\\\\031e3\\\",\\\
46 run = \\\"Minesweeper\\\",\\\
47 },\\\
48 [ \\\"4e404681d4fa9e04ae2bb63d82f58a9733fa605a\\\" ] = {\\\
49 title = \\\"Tron\\\",\\\
50 category = \\\"Games\\\",\\\
51 iconExt = \\\"\\\\030 \\\\031f\\\\030b\\\\031f\\\\143\\\\030f\\\\128\\\\128\\\\030b\\\\143\\\\143\\\\143\\\\030f\\\\128\\\\128\\\\010\\\\030 \\\\031f\\\\0309\\\\031b\\\\140\\\\030b\\\\031f\\\\151\\\\030f\\\\031b\\\\131\\\\0307\\\\148\\\\0317\\\\128\\\\030b\\\\151\\\\030f\\\\031b\\\\131\\\\148\\\\010\\\\030 \\\\031f\\\\030f\\\\031b\\\\131\\\\031f\\\\128\\\\031b\\\\131\\\\0317\\\\131\\\\031f\\\\128\\\\0317\\\\131\\\\031b\\\\131\\\\031f\\\\128\\\",\\\
52 run = \\\"Tron\\\",\\\
53 },\\\
54}\",\
55 [ \"common/etc/scripts/summon\" ] = \"local function summon(id)\\\
56 local GPS = require('opus.gps')\\\
57 local Point = require('opus.point')\\\
58 local Socket = require('opus.socket')\\\
59\\\
60 turtle.setStatus('GPSing')\\\
61 turtle.setPoint({ x = 0, y = 0, z = 0, heading = 0 })\\\
62\\\
63 local pts = {\\\
64 [ 1 ] = { x = 0, z = 0, y = 0 },\\\
65 [ 2 ] = { x = 4, z = 0, y = 0 },\\\
66 [ 3 ] = { x = 2, z = -2, y = 2 },\\\
67 [ 4 ] = { x = 2, z = 2, y = 2 },\\\
68 }\\\
69 local tFixes = { }\\\
70\\\
71 local socket = Socket.connect(id, 161)\\\
72\\\
73 if not socket then\\\
74 error('turtle: Unable to connect to ' .. id)\\\
75 end\\\
76\\\
77 local function getDistance()\\\
78 socket:write({ type = 'ping' })\\\
79 local _, d = socket:read(5)\\\
80 return d\\\
81 end\\\
82\\\
83 local function doGPS()\\\
84 tFixes = { }\\\
85 for i = 1, 4 do\\\
86 if not turtle.go(pts[i]) then\\\
87 error('turtle: Unable to perform GPS maneuver')\\\
88 end\\\
89 local distance = getDistance()\\\
90 if not distance then\\\
91 error('turtle: No response from ' .. id)\\\
92 end\\\
93 table.insert(tFixes, {\\\
94 position = vector.new(turtle.point.x, turtle.point.y, turtle.point.z),\\\
95 distance = distance\\\
96 })\\\
97 end\\\
98 return true\\\
99 end\\\
100\\\
101 if not doGPS() then\\\
102 turtle.turnAround()\\\
103 turtle.setPoint({ x = 0, y = 0, z = 0, heading = 0})\\\
104 if not doGPS() then\\\
105 socket:close()\\\
106 return false\\\
107 end\\\
108 end\\\
109\\\
110 socket:close()\\\
111\\\
112 local pos = GPS.trilaterate(tFixes)\\\
113\\\
114 if pos then\\\
115 local pt = { x = pos.x, y = pos.y, z = pos.z }\\\
116 local _, h = Point.calculateMoves(turtle.getPoint(), pt)\\\
117 local hi = turtle.getHeadingInfo(h)\\\
118 turtle.setStatus('recalling')\\\
119 turtle.pathfind({ x = pt.x - hi.xd, z = pt.z - hi.zd, y = pt.y - hi.yd, heading = h })\\\
120 else\\\
121 error(\\\"turtle: Could not determine position\\\")\\\
122 end\\\
123end\\\
124\\\
125turtle.run(function() summon({COMPUTER_ID}) end)\",\
126 [ \"common/DiskCopy.lua\" ] = \"local Ansi = require('opus.ansi')\\\
127local Config = require('opus.config')\\\
128local Event = require('opus.event')\\\
129local UI = require('opus.ui')\\\
130local Util = require('opus.util')\\\
131\\\
132local colors = _G.colors\\\
133local fs = _G.fs\\\
134local peripheral = _G.peripheral\\\
135\\\
136local drives = { }\\\
137if peripheral.getType('left') == 'drive' then\\\
138 drives.left = Util.shallowCopy(peripheral.wrap('left'))\\\
139 drives.left.name = 'left'\\\
140end\\\
141if peripheral.getType('right') == 'drive' then\\\
142 drives.right = Util.shallowCopy(peripheral.wrap('right'))\\\
143 drives.right.name = 'right'\\\
144end\\\
145\\\
146peripheral.find('drive', function(n, v)\\\
147 if not drives.left then\\\
148 drives.left = Util.shallowCopy(v)\\\
149 drives.left.name = n\\\
150 elseif not drives.right then\\\
151 drives.right = Util.shallowCopy(v)\\\
152 drives.right.name = n\\\
153 end\\\
154end)\\\
155\\\
156if not (drives.left and drives.right) then\\\
157 error('Two drives are required')\\\
158end\\\
159\\\
160local COPY_LEFT = 1\\\
161local COPY_RIGHT = 2\\\
162local directions = {\\\
163 [ COPY_LEFT ] = { text = '-->>' },\\\
164 [ COPY_RIGHT ] = { text = '<<--' },\\\
165}\\\
166\\\
167local config = Config.load('DiskCopy', {\\\
168 eject = true,\\\
169 automatic = false,\\\
170 copyDir = COPY_LEFT\\\
171})\\\
172\\\
173local page = UI.Page {\\\
174 linfo = UI.Window {\\\
175 x = 2, y = 2, ey = 5, width = 18,\\\
176 },\\\
177 rinfo = UI.Window {\\\
178 x = -19, y = 2, ey = 5, width = 18,\\\
179 },\\\
180 dir = UI.Button {\\\
181 x = 17, y = 6, width = 6,\\\
182 event = 'change_dir',\\\
183 },\\\
184 progress = UI.ProgressBar {\\\
185 x = 2, ex = -2, y = -4,\\\
186 backgroundColor = colors.black,\\\
187 },\\\
188 ejectText = UI.Text {\\\
189 x = 2, y = -2,\\\
190 value = 'Eject'\\\
191 },\\\
192 eject = UI.Checkbox {\\\
193 x = 8, y = -2,\\\
194 },\\\
195 automaticText = UI.Text {\\\
196 x = 12, y = -2,\\\
197 value = 'Copy automatically'\\\
198 },\\\
199 automatic = UI.Checkbox {\\\
200 x = 31, y = -2,\\\
201 },\\\
202 copyButton = UI.Button {\\\
203 x = -7, y = -2,\\\
204 text = 'Copy',\\\
205 event = 'copy',\\\
206 inactive = true,\\\
207 },\\\
208 warning = UI.Text {\\\
209 x = 2, ex = -2, y = -1,\\\
210 align = 'center',\\\
211 textColor = colors.orange,\\\
212 },\\\
213 notification = UI.Notification { },\\\
214}\\\
215\\\
216function page:enable()\\\
217 Util.merge(self.dir, directions[config.copyDir])\\\
218\\\
219 self.eject.value = config.eject\\\
220 self.automatic.value = config.automatic\\\
221\\\
222 self.dir:move(math.floor((self.width / 2) - 3) + 1, self.dir.y)\\\
223\\\
224 UI.Page.enable(self)\\\
225end\\\
226\\\
227local function isValid(drive)\\\
228 return drive.isDiskPresent() and drive.getMountPath()\\\
229end\\\
230\\\
231local function needsLabel(drive)\\\
232 return drive.isDiskPresent() and not drive.getMountPath() and not drive.getAudioTitle()\\\
233end\\\
234\\\
235function page:drawInfo(drive, textArea)\\\
236 local function getLabel()\\\
237 return not drive.isDiskPresent() and 'empty' or\\\
238 not drive.getMountPath() and 'invalid' or\\\
239 drive.getDiskLabel() or 'unlabeled'\\\
240 end\\\
241\\\
242 local function getUsed()\\\
243 return isValid(drive) and fs.getSize(drive.getMountPath(), true) or 0\\\
244 end\\\
245\\\
246 local function getFree()\\\
247 return isValid(drive) and fs.getFreeSpace(drive.getMountPath()) or 0\\\
248 end\\\
249\\\
250 textArea:print(string.format('Drive: %s%s%s\\\\nLabel: %s%s%s\\\\nUsed: %s%s%s\\\\nFree: %s%s%s',\\\
251 Ansi.yellow, drive.name, Ansi.reset,\\\
252 isValid(drive) and Ansi.yellow or Ansi.orange, getLabel():sub(1, 10), Ansi.reset,\\\
253 Ansi.yellow, Util.toBytes(getUsed()), Ansi.reset,\\\
254 Ansi.yellow, Util.toBytes(getFree()), Ansi.reset))\\\
255end\\\
256\\\
257function page:scan()\\\
258 local showWarning = needsLabel(drives.left) or needsLabel(drives.right)\\\
259 local valid = isValid(drives.left) and isValid(drives.right)\\\
260\\\
261 self.warning.value = showWarning and 'Computers must be labeled'\\\
262 self.copyButton.inactive = not valid\\\
263\\\
264 self:draw()\\\
265 self.progress:clear()\\\
266 self.progress:centeredWrite(1, 'Analyzing Disks..')\\\
267 self.progress:sync()\\\
268\\\
269 self:drawInfo(drives.left, self.linfo)\\\
270 self:drawInfo(drives.right, self.rinfo)\\\
271\\\
272 self.progress:clear()\\\
273end\\\
274\\\
275function page:copy()\\\
276 local sdrive = config.copyDir == COPY_LEFT and drives.left or drives.right\\\
277 local tdrive = config.copyDir == COPY_LEFT and drives.right or drives.left\\\
278\\\
279 local throttle = Util.throttle()\\\
280 local sourceFiles, targetFiles = { }, { }\\\
281\\\
282 local function getListing(mountPath, path, files)\\\
283 for _,f in pairs(fs.list(path)) do\\\
284 local file = fs.combine(path, f)\\\
285 if not fs.isReadOnly(file) then\\\
286 files[string.sub(file, #mountPath + 1)] = true\\\
287 if fs.isDir(file) then\\\
288 getListing(mountPath, file, files)\\\
289 end\\\
290 end\\\
291 end\\\
292 throttle()\\\
293 end\\\
294\\\
295 self.progress:clear()\\\
296 self.progress:centeredWrite(1, 'Computing..')\\\
297 self.progress:sync()\\\
298\\\
299 getListing(sdrive.getMountPath(), sdrive.getMountPath(), sourceFiles)\\\
300 getListing(tdrive.getMountPath(), tdrive.getMountPath(), targetFiles)\\\
301\\\
302 local copied = 0\\\
303 local totalFiles = Util.size(sourceFiles)\\\
304\\\
305 local function rawCopy(source, target)\\\
306 if fs.isDir(source) then\\\
307 copied = copied + 1\\\
308 if not fs.exists(target) then\\\
309 fs.makeDir(target)\\\
310 end\\\
311 for _,f in pairs(fs.list(source)) do\\\
312 rawCopy(fs.combine(source, f), fs.combine(target, f))\\\
313 end\\\
314\\\
315 else\\\
316 if fs.exists(target) then\\\
317 fs.delete(target)\\\
318 end\\\
319\\\
320 fs.copy(source, target)\\\
321 copied = copied + 1\\\
322 self.progress.value = copied * 100 / totalFiles\\\
323 self.progress:draw()\\\
324 self.progress:sync()\\\
325 end\\\
326 throttle()\\\
327 end\\\
328\\\
329 local function cleanup()\\\
330 for k in pairs(targetFiles) do\\\
331 if not sourceFiles[k] then\\\
332 fs.delete(fs.combine(tdrive.getMountPath(), k))\\\
333 end\\\
334 end\\\
335 end\\\
336\\\
337 self.progress:clear()\\\
338 rawCopy(sdrive.getMountPath(), tdrive.getMountPath())\\\
339 cleanup()\\\
340\\\
341 self.progress:clear()\\\
342 self.progress:centeredWrite(1, 'Copy Complete', colors.lime, colors.black)\\\
343 self.progress:sync()\\\
344\\\
345 self.progress.value = 0\\\
346-- self.progress:clear()\\\
347\\\
348 self:scan()\\\
349\\\
350 if config.eject then\\\
351 tdrive.ejectDisk()\\\
352 end\\\
353end\\\
354\\\
355function page:eventHandler(event)\\\
356 if event.type == 'change_dir' then\\\
357 config.copyDir = (config.copyDir) % 2 + 1\\\
358 Util.merge(self.dir, directions[config.copyDir])\\\
359 Config.update('DiskCopy', config)\\\
360 self.dir:draw()\\\
361\\\
362 elseif event.type == 'copy' then\\\
363 self:copy()\\\
364\\\
365 elseif event.type == 'checkbox_change' then\\\
366 if event.element == self.eject then\\\
367 config.eject = not not event.checked\\\
368 elseif event.element == self.automatic then\\\
369 config.automatic = not not event.checked\\\
370 end\\\
371\\\
372 Config.update('DiskCopy', config)\\\
373 event.element:draw()\\\
374\\\
375 else\\\
376 return UI.Page.eventHandler(self, event)\\\
377 end\\\
378 return true\\\
379end\\\
380\\\
381Event.on(\\\"disk\\\", function()\\\
382 page:scan()\\\
383 page:sync()\\\
384\\\
385 if config.automatic and not page.copyButton.inactive then\\\
386 page:copy()\\\
387 end\\\
388end)\\\
389\\\
390Event.on(\\\"disk_eject\\\", function()\\\
391 page:scan()\\\
392 page:sync()\\\
393end)\\\
394\\\
395Event.onTimeout(.2, function()\\\
396 page:scan()\\\
397 page:sync()\\\
398end)\\\
399\\\
400UI:setPage(page)\\\
401UI:start()\",\
402 [ \"games/sameGame.lua\" ] = \"--Same Game for CraftOS 1.0.0 (ShinyCube) (Advanced Computer)\\\
403\\\
404-- slight modifications to run on a kiosk\\\
405local score, A, B, C, D, E\\\
406local board = {}\\\
407local selected = {}\\\
408local backup_board = {}\\\
409local backup_score\\\
410local backup_exists = false\\\
411local cnt_selected\\\
412local selected_color\\\
413local is_gameover\\\
414local best_scores = {}\\\
415local best_score_names = {}\\\
416local best_score_view = false\\\
417function init()\\\
418 loadScore()\\\
419 term.setBackgroundColor(colors.black)\\\
420 term.setTextColor(colors.white)\\\
421 term.clear()\\\
422 for i = 1, 10 do\\\
423 board[i] = {}\\\
424 selected[i] = {}\\\
425 backup_board[i] = {}\\\
426 end\\\
427 newGame()\\\
428 eventLoop()\\\
429end\\\
430function eventLoop()\\\
431 while true do\\\
432 local event, button, x, y = os.pullEvent()\\\
433 if event == \\\"mouse_click\\\" then\\\
434 if best_score_view then\\\
435 if y == 1 and 44 <= x and x <= 51 then\\\
436 best_score_view = false\\\
437 redraw()\\\
438 end\\\
439 else\\\
440 if x >= 7 and x <= 46 and y >=8 and y <= 17 then\\\
441 local j = math.floor((x-7)/2) + 1\\\
442 local i = (y-8) + 1\\\
443 clicked(i,j)\\\
444 end\\\
445 if y == 1 and 1 <= x and x <= 7 then\\\
446 newGame()\\\
447 end\\\
448 if y == 1 and 9 <= x and x <= 16 then\\\
449 undo()\\\
450 redraw()\\\
451 end\\\
452 if y == 1 and 18 <= x and x <= 31 then\\\
453 showBestScore()\\\
454 end\\\
455 if y == 1 and 33 <= x and x <= 42 then\\\
456 redraw()\\\
457 end\\\
458 if y == 1 and 44 <= x and x <= 51 then\\\
459 term.clear()\\\
460 term.setCursorPos(1,1)\\\
461 break\\\
462 end\\\
463 end\\\
464 end\\\
465 end\\\
466end\\\
467function newGame()\\\
468 score = 0\\\
469 A = 0\\\
470 B = 0\\\
471 C = 0\\\
472 D = 0\\\
473 E = 0\\\
474 cnt_selected = 0\\\
475 is_gameover = false\\\
476 backup_exists = false\\\
477 for i = 1, 10 do\\\
478 for j = 1, 20 do\\\
479 board[i][j] = math.random(5)\\\
480 if(board[i][j] == 1) then A = A + 1 end\\\
481 if(board[i][j] == 2) then B = B + 1 end\\\
482 if(board[i][j] == 3) then C = C + 1 end\\\
483 if(board[i][j] == 4) then D = D + 1 end\\\
484 if(board[i][j] == 5) then E = E + 1 end\\\
485 selected[i][j] = false\\\
486 end\\\
487 end\\\
488 redraw()\\\
489end\\\
490function redraw()\\\
491 if best_score_view then\\\
492 term.setCursorPos(1,1) term.write(\\\" [ BACK ]\\\")\\\
493 else\\\
494 term.setCursorPos(1,1) term.write(\\\"[ NEW ] [ UNDO ] [ HIGH SCORE ] [ SCREEN ] [ EXIT ]\\\")\\\
495 end\\\
496 term.setCursorPos(16,3) term.write(\\\"Same Game for Craft OS\\\")\\\
497 term.setCursorPos(15,5) term.write(\\\"Implemented by ShinyCube\\\")\\\
498 term.setCursorPos(3,19) term.write(\\\"Score: A: B: C: D: E: \\\")\\\
499 if best_score_view then\\\
500 for i = 1, 10 do\\\
501 term.setTextColor(colors.white)\\\
502 term.setBackgroundColor(colors.black)\\\
503 term.setCursorPos(7,8+(i-1))\\\
504 term.write(string.format(\\\"%2d. ...............................%5d\\\",i,best_scores[i]))\\\
505 term.setCursorPos(11,8+(i-1))\\\
506 term.write(best_score_names[i])\\\
507 end\\\
508 else\\\
509 for i = 1, 10 do\\\
510 for j = 1, 20 do\\\
511 term.setCursorPos(7+(j-1)*2,8+(i-1))\\\
512 if board[i][j] == 0 then\\\
513 term.blit(\\\". \\\",\\\"00\\\",\\\"ff\\\")\\\
514 elseif board[i][j] == 1 then\\\
515 if selected[i][j] then\\\
516 term.blit(\\\"A \\\",\\\"aa\\\",\\\"00\\\")\\\
517 else\\\
518 term.blit(\\\"A \\\",\\\"00\\\",\\\"aa\\\")\\\
519 end\\\
520 elseif board[i][j] == 2 then\\\
521 if selected[i][j] then\\\
522 term.blit(\\\"B \\\",\\\"bb\\\",\\\"00\\\")\\\
523 else\\\
524 term.blit(\\\"B \\\",\\\"00\\\",\\\"bb\\\")\\\
525 end\\\
526 elseif board[i][j] == 3 then\\\
527 if selected[i][j] then\\\
528 term.blit(\\\"C \\\",\\\"cc\\\",\\\"00\\\")\\\
529 else\\\
530 term.blit(\\\"C \\\",\\\"00\\\",\\\"cc\\\")\\\
531 end\\\
532 elseif board[i][j] == 4 then\\\
533 if selected[i][j] then\\\
534 term.blit(\\\"D \\\",\\\"dd\\\",\\\"00\\\")\\\
535 else\\\
536 term.blit(\\\"D \\\",\\\"00\\\",\\\"dd\\\")\\\
537 end\\\
538 elseif board[i][j] == 5 then\\\
539 if selected[i][j] then\\\
540 term.blit(\\\"E \\\",\\\"ee\\\",\\\"00\\\")\\\
541 else\\\
542 term.blit(\\\"E \\\",\\\"00\\\",\\\"ee\\\")\\\
543 end\\\
544 end\\\
545 end\\\
546 end\\\
547 end\\\
548 term.setTextColor(colors.white)\\\
549 term.setBackgroundColor(colors.black)\\\
550 term.setCursorPos(22,7)\\\
551 if is_gameover then\\\
552 term.write(\\\"GAME OVER\\\")\\\
553 else\\\
554 term.write(\\\" \\\")\\\
555 end\\\
556 term.setCursorPos(9,19)\\\
557 term.write(\\\" \\\")\\\
558 term.setCursorPos(9,19)\\\
559 term.write(score)\\\
560 if cnt_selected > 0 then\\\
561 term.write(\\\"+\\\" .. cnt_selected*cnt_selected-3*cnt_selected+4)\\\
562 end\\\
563 term.setCursorPos(23,19)\\\
564 term.write(A)\\\
565 term.setCursorPos(29,19)\\\
566 term.write(B)\\\
567 term.setCursorPos(35,19)\\\
568 term.write(C)\\\
569 term.setCursorPos(41,19)\\\
570 term.write(D)\\\
571 term.setCursorPos(47,19)\\\
572 term.write(E)\\\
573end\\\
574function deselectAll()\\\
575 for i = 1, 10 do\\\
576 for j = 1, 20 do\\\
577 selected[i][j] = false\\\
578 end\\\
579 end\\\
580 cnt_selected = 0\\\
581end\\\
582function rec_selection(i,j)\\\
583 if not selected[i][j] then\\\
584 selected[i][j] = true\\\
585 cnt_selected = cnt_selected + 1\\\
586 if i-1 >= 1 and board[i][j] == board[i-1][j] then rec_selection(i-1,j) end\\\
587 if i+1 <= 10 and board[i][j] == board[i+1][j] then rec_selection(i+1,j) end\\\
588 if j-1 >= 1 and board[i][j] == board[i][j-1] then rec_selection(i,j-1) end\\\
589 if j+1 <= 20 and board[i][j] == board[i][j+1] then rec_selection(i,j+1) end\\\
590 end\\\
591end\\\
592function backup()\\\
593 for i = 1, 10 do\\\
594 for j = 1, 20 do\\\
595 backup_board[i][j] = board[i][j]\\\
596 backup_score = score\\\
597 end\\\
598 end\\\
599 backup_exists = true\\\
600end\\\
601function removeSelected()\\\
602 local di, dj\\\
603 dj = 1\\\
604 for sj = 1, 20 do\\\
605 di = 10\\\
606 for si = 10, 1, -1 do\\\
607 if not selected[si][sj] then\\\
608 board[di][dj] = board[si][sj]\\\
609 di = di - 1\\\
610 end\\\
611 end\\\
612 for di = di, 1, -1 do\\\
613 board[di][dj] = 0\\\
614 end\\\
615 if board[10][dj] ~= 0 then dj = dj + 1 end\\\
616 end\\\
617 for dj = dj, 20 do\\\
618 for di = 1, 10 do\\\
619 board[di][dj] = 0\\\
620 end\\\
621 end\\\
622end\\\
623function checkGameOver()\\\
624 for i = 1, 10 do\\\
625 for j = 1, 20 do\\\
626 if i-1>=1 and board[i][j] > 0 and board[i][j] == board[i-1][j] then return false end\\\
627 if i+1<=10 and board[i][j] > 0 and board[i][j] == board[i+1][j] then return false end\\\
628 if j-1>=1 and board[i][j] > 0 and board[i][j] == board[i][j-1] then return false end\\\
629 if j+1<=20 and board[i][j] > 0 and board[i][j] == board[i][j+1] then return false end\\\
630 end\\\
631 end\\\
632 return true\\\
633end\\\
634function loadScore()\\\
635 local file = fs.open(\\\"same.dat\\\",\\\"r\\\")\\\
636 if file then\\\
637 for i = 1, 10 do\\\
638 best_score_names[i] = file.readLine() or \\\"NONAME\\\"\\\
639 best_scores[i] = tonumber(file.readLine()) or 0\\\
640 end\\\
641 file.close()\\\
642 else\\\
643 for i = 1, 10 do\\\
644 best_score_names[i] = \\\"NONAME\\\"\\\
645 best_scores[i] = 0\\\
646 end\\\
647 end\\\
648end\\\
649function saveScore()\\\
650 local file = fs.open(\\\"same.dat\\\",\\\"w\\\")\\\
651 if file then\\\
652 for i = 1, 10 do\\\
653 file.writeLine(best_score_names[i])\\\
654 file.writeLine(best_scores[i])\\\
655 end\\\
656 file.flush()\\\
657 end\\\
658end\\\
659function updateScore()\\\
660 local rank = 1\\\
661 for i = 10, 1, -1 do\\\
662 if best_scores[i] < score then\\\
663 best_score_names[i+1] = best_score_names[i]\\\
664 best_scores[i+1] = best_scores[i]\\\
665 else\\\
666 rank = i + 1\\\
667 break\\\
668 end\\\
669 end\\\
670 if rank <= 10 then\\\
671 best_score_names[rank] = getName(rank, score)\\\
672 best_scores[rank] = score\\\
673 saveScore()\\\
674 best_score_view = true\\\
675 redraw()\\\
676 end\\\
677end\\\
678function getName(rank, score)\\\
679 term.setTextColor(colors.white)\\\
680 term.setBackgroundColor(colors.black)\\\
681 term.clear()\\\
682 term.setCursorPos(1,1)\\\
683 print(\\\"Congratulation!\\\")\\\
684 print(\\\"You got a high score!\\\")\\\
685 print(\\\"Your score: \\\" .. score)\\\
686 print(\\\"Your rank: \\\" .. rank)\\\
687 print(\\\"Type your name. >\\\")\\\
688 local name = '...'\\\
689 name = string.sub(name, 1, 30)\\\
690 term.setTextColor(colors.white)\\\
691 term.setBackgroundColor(colors.black)\\\
692 term.clear()\\\
693 return name\\\
694end\\\
695function undo()\\\
696 if backup_exists then\\\
697 deselectAll()\\\
698 backup_exists = false\\\
699 score = backup_score\\\
700 for i = 1, 10 do\\\
701 for j = 1, 20 do\\\
702 board[i][j] = backup_board[i][j]\\\
703 end\\\
704 end\\\
705 end\\\
706end\\\
707function showBestScore()\\\
708 best_score_view = true\\\
709 redraw()\\\
710end\\\
711\\\
712function clicked(ci,cj)\\\
713 if selected[ci][cj] then\\\
714 backup()\\\
715 score = score + cnt_selected*cnt_selected-3*cnt_selected+4\\\
716 if selected_color == 1 then A = A - cnt_selected\\\
717 elseif selected_color == 2 then B = B - cnt_selected\\\
718 elseif selected_color == 3 then C = C - cnt_selected\\\
719 elseif selected_color == 4 then D = D - cnt_selected\\\
720 elseif selected_color == 5 then E = E - cnt_selected\\\
721 end\\\
722 removeSelected()\\\
723 deselectAll()\\\
724 if checkGameOver() then\\\
725 is_gameover = true\\\
726 backup_exists = false\\\
727 updateScore()\\\
728 redraw()\\\
729 else\\\
730 redraw()\\\
731 end\\\
732 else\\\
733 if cnt_selected > 0 then\\\
734 deselectAll()\\\
735 redraw()\\\
736 else\\\
737 if board[ci][cj] > 0 then\\\
738 selected_color = board[ci][cj]\\\
739 rec_selection(ci,cj)\\\
740 if cnt_selected == 1 then\\\
741 deselectAll()\\\
742 end\\\
743 redraw()\\\
744 end\\\
745 end\\\
746 end\\\
747end\\\
748init()\",\
749 [ \"common/multiMiner.lua\" ] = \"local Event = require('opus.event')\\\
750local GPS = require('opus.gps')\\\
751local itemDB = require('core.itemDB')\\\
752local Point = require('opus.point')\\\
753local Socket = require('opus.socket')\\\
754local Sound = require('opus.sound')\\\
755local Util = require('opus.util')\\\
756local UI = require('opus.ui')\\\
757\\\
758local colors = _G.colors\\\
759local device = _G.device\\\
760local gps = _G.gps\\\
761local network = _G.network\\\
762local os = _G.os\\\
763\\\
764UI:configure('multiMiner', ...)\\\
765\\\
766local glasses = device['plethora:glasses']\\\
767local scanner = device['plethora:scanner'] or\\\
768 error('Plethora scanner must be equipped')\\\
769\\\
770-- hud\\\
771local canvas = glasses and glasses.canvas()\\\
772if canvas then\\\
773 local lh\\\
774\\\
775 local function addText(x, y, text, color)\\\
776 local th = canvas.group.addText({ x, y }, text, color or 0xa0a0a0FF)\\\
777 lh = lh or th.getLineHeight()\\\
778 th.setShadow(true)\\\
779 th.setScale(.75)\\\
780 return th\\\
781 end\\\
782\\\
783 canvas.group = canvas.addGroup({ 4, 90 })\\\
784 canvas.group.bg = canvas.group.addRectangle(0, 0, 80, 10, 0x40404080)\\\
785 canvas.group.addLines(\\\
786 { 0, 0 },\\\
787 { 80, 0 },\\\
788 { 80, 10 },\\\
789 { 0, 10 },\\\
790 { 0, 0 },\\\
791 0x202020FF,\\\
792 2)\\\
793 addText(20, 2, 'Swarm Miner', 0xc0c0c0FF)\\\
794\\\
795 local y = 15\\\
796 addText(3, y, 'Turtles')\\\
797 canvas.turtles = addText(60, y, '')\\\
798 canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)\\\
799\\\
800 y = y + lh + 5\\\
801 addText(3, y, 'Queue')\\\
802 canvas.queue = addText(60, y, '')\\\
803 canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)\\\
804end\\\
805\\\
806-- container\\\
807local canvas3d = glasses and glasses.canvas3d().create()\\\
808local box, offset\\\
809\\\
810local paused = false\\\
811\\\
812local function inBox(pt)\\\
813 if not box or not box.ex then\\\
814 return true\\\
815 end\\\
816 return Point.inBox(pt, box)\\\
817end\\\
818\\\
819local function locate()\\\
820 for _ = 1, 3 do\\\
821 local pt = GPS.getPoint()\\\
822 if pt then\\\
823 return pt\\\
824 end\\\
825 end\\\
826end\\\
827\\\
828local spt = GPS.getPoint() or error('GPS failure')\\\
829local chestPoint -- location of chest\\\
830local blockTypes = { } -- blocks types requested to mine\\\
831local turtles = { } -- active turtles\\\
832local pool = { } -- all turtles\\\
833local queue = { } -- actual blocks to mine\\\
834local abort\\\
835\\\
836local function hijackTurtle(remoteId)\\\
837 local socket, msg = Socket.connect(remoteId, 188)\\\
838\\\
839 if not socket then\\\
840 error(msg)\\\
841 end\\\
842\\\
843 socket:write('turtle')\\\
844 local methods = socket:read()\\\
845\\\
846 local hijack = { }\\\
847 for _,method in pairs(methods) do\\\
848 hijack[method] = function(...)\\\
849 socket:write({ method, ... })\\\
850 local resp = socket:read()\\\
851 if not resp then\\\
852 error('timed out: ' .. method)\\\
853 end\\\
854 return table.unpack(resp)\\\
855 end\\\
856 end\\\
857\\\
858 return hijack, socket\\\
859end\\\
860\\\
861local function getNextPoint(turtle)\\\
862 if not paused then\\\
863 local pt = Point.closest(turtle.getPoint(), queue)\\\
864 if pt then\\\
865 turtle.pt = pt\\\
866 queue[pt.pkey] = nil\\\
867 return pt\\\
868 end\\\
869 end\\\
870end\\\
871\\\
872local function run(member, point)\\\
873 Event.addRoutine(function()\\\
874 local turtle, socket\\\
875 local _, m = pcall(function()\\\
876 member.active = true\\\
877 turtle, socket = hijackTurtle(member.id)\\\
878\\\
879 local function emptySlots(retain, pt)\\\
880 local slots = turtle.getFilledSlots()\\\
881 for _,slot in pairs(slots) do\\\
882 if not retain[slot.key] and not slot.name:find('turtle') then\\\
883 turtle.select(slot.index)\\\
884 if pt then\\\
885 turtle.dropAt(pt, 64)\\\
886 else\\\
887 turtle.dropUp(64)\\\
888 end\\\
889 end\\\
890 end\\\
891 end\\\
892\\\
893 local function dropOff()\\\
894 -- go to 2 above chest\\\
895 local topPoint = Point.copy(chestPoint)\\\
896 topPoint.y = topPoint.y + 2\\\
897 turtle.gotoY(topPoint.y)\\\
898 while not turtle.go(topPoint) do\\\
899 os.sleep(.5)\\\
900 end\\\
901\\\
902 -- path to chest\\\
903 local box = Point.makeBox(\\\
904 { x = chestPoint.x - 3, y = chestPoint.y + 3, z = chestPoint.z - 3 },\\\
905 { x = chestPoint.x + 3, y = chestPoint.y, z = chestPoint.z + 3 }\\\
906 )\\\
907 turtle.set({\\\
908 movementStrategy = 'pathing',\\\
909 pathingBox = Point.normalizeBox(box),\\\
910 digPolicy = 'digNone',\\\
911 })\\\
912 while not turtle.moveAgainst(chestPoint) do\\\
913 os.sleep(.5)\\\
914 end\\\
915 emptySlots({ }, chestPoint)\\\
916\\\
917 -- path to 3 above chest\\\
918 turtle.pathfind(Point.above(topPoint))\\\
919 turtle.set({\\\
920 movementStrategy = 'goto',\\\
921 digPolicy = 'blacklist',\\\
922 })\\\
923 end\\\
924\\\
925 if turtle then\\\
926 turtles[member.id] = turtle\\\
927\\\
928 turtle.reset()\\\
929 turtle.set({\\\
930 attackPolicy = 'attack',\\\
931 digPolicy = 'blacklist',\\\
932 blacklist = {\\\
933 'turtle',\\\
934 'chest',\\\
935 'shulker',\\\
936 },\\\
937 movementStrategy = 'goto',\\\
938 point = point,\\\
939 })\\\
940 turtle.select(1)\\\
941\\\
942 repeat\\\
943 local pt = getNextPoint(turtle)\\\
944 if pt then\\\
945 member.status = 'digging'\\\
946\\\
947 if blockTypes[pt.key] == true then\\\
948 if turtle.moveAgainst(pt) then\\\
949 local index = turtle.selectOpenSlot()\\\
950 if turtle.digAt(pt, pt.name) then\\\
951 local slot = turtle.getSlot(index)\\\
952 if slot.count > 0 then\\\
953 blockTypes[pt.key] = slot.key\\\
954 if slot.key ~= pt.key then\\\
955 blockTypes[slot.key] = true\\\
956 end\\\
957 end\\\
958 end\\\
959 end\\\
960 turtle.select(1)\\\
961 else\\\
962 turtle.digAt(pt, pt.name)\\\
963 end\\\
964\\\
965 if turtle.getItemCount(15) > 0 then\\\
966 member.status = 'ejecting trash'\\\
967 emptySlots(blockTypes)\\\
968 turtle.condense()\\\
969 if turtle.getItemCount(15) > 0 then\\\
970 member.status = 'dropping off'\\\
971 if not chestPoint then\\\
972 member.abort = true\\\
973 member.status = 'full'\\\
974 else\\\
975 dropOff()\\\
976 end\\\
977 end\\\
978 turtle.select(1)\\\
979 end\\\
980 else\\\
981 member.status = 'waiting'\\\
982 os.sleep(1)\\\
983 end\\\
984 if member.fuel < 100 then\\\
985 member.status = 'out of fuel'\\\
986 break\\\
987 end\\\
988 until member.abort\\\
989 end\\\
990\\\
991 emptySlots(blockTypes)\\\
992\\\
993 if chestPoint then\\\
994 dropOff()\\\
995 while not turtle.go(Point.above(spt)) do\\\
996 os.sleep(.5)\\\
997 end\\\
998 --if turtle.selectSlotWithQuantity(0) then\\\
999 --turtle.set({ digPolicy = 'dig' })\\\
1000 --end\\\
1001 while not turtle.go(spt) do\\\
1002 os.sleep(.5)\\\
1003 end\\\
1004 else\\\
1005 turtle.gotoY(spt.y)\\\
1006 while not turtle.go(spt) do\\\
1007 os.sleep(.5)\\\
1008 end\\\
1009 end\\\
1010 end)\\\
1011\\\
1012 turtles[member.id] = nil\\\
1013 member.status = m\\\
1014 member.active = false\\\
1015 if socket then\\\
1016 socket:close()\\\
1017 end\\\
1018 end)\\\
1019end\\\
1020\\\
1021local function drawContainer(pos)\\\
1022 if canvas3d then\\\
1023 canvas3d.clear()\\\
1024\\\
1025 local function addBox(b)\\\
1026 canvas3d.addBox(\\\
1027 b.x - offset.x + .25,\\\
1028 b.y - offset.y + .25 ,\\\
1029 b.z - offset.z + .25 ,\\\
1030 .5, .5, .5).setDepthTested(false)\\\
1031 end\\\
1032 if box and box.ex then\\\
1033 addBox({ x = box.x, y = box.y, z = box.z })\\\
1034 addBox({ x = box.x, y = box.y, z = box.ez })\\\
1035 addBox({ x = box.ex, y = box.y, z = box.z })\\\
1036 addBox({ x = box.ex, y = box.y, z = box.ez })\\\
1037 addBox({ x = box.x, y = box.ey, z = box.z })\\\
1038 addBox({ x = box.x, y = box.ey, z = box.ez })\\\
1039 addBox({ x = box.ex, y = box.ey, z = box.z })\\\
1040 addBox({ x = box.ex, y = box.ey, z = box.ez })\\\
1041 elseif box then\\\
1042 canvas3d.recenter({ -(pos.x % 1), -(pos.y % 1), -(pos.z % 1) })\\\
1043 addBox(box)\\\
1044 end\\\
1045 end\\\
1046end\\\
1047\\\
1048local pauseResume = {\\\
1049 { text = 'Pause', event = 'pause' },\\\
1050 { text = 'Resume', event = 'resume' },\\\
1051}\\\
1052local containerText = {\\\
1053 [[Set a corner to contain mining area]],\\\
1054 [[Set ending corner]],\\\
1055 [[Set again to clear]],\\\
1056}\\\
1057\\\
1058local containTab = UI.Tab {\\\
1059 title = 'Contain',\\\
1060 button = UI.Button {\\\
1061 x = 2, y = 2,\\\
1062 text = 'Set corner',\\\
1063 event = 'contain'\\\
1064 },\\\
1065 textArea = UI.TextArea {\\\
1066 x = 2, y = 4,\\\
1067 value = containerText[1],\\\
1068 },\\\
1069}\\\
1070\\\
1071local blocksTab = UI.Tab {\\\
1072 title = 'Blocks',\\\
1073 grid = UI.ScrollingGrid {\\\
1074 y = 1,\\\
1075 columns = {\\\
1076 { heading = 'Count', key = 'count', width = 6, align = 'right' },\\\
1077 { heading = 'Name', key = 'displayName' },\\\
1078 },\\\
1079 sortColumn = 'displayName',\\\
1080 },\\\
1081}\\\
1082\\\
1083local turtlesTab = UI.Tab {\\\
1084 title = 'Turtles',\\\
1085 grid = UI.ScrollingGrid {\\\
1086 y = 1,\\\
1087 values = pool,\\\
1088 columns = {\\\
1089 { heading = 'ID', key = 'id', width = 5, },\\\
1090 { heading = ' Fuel', key = 'fuel', width = 5, align = 'right' },\\\
1091 { heading = ' Dist', key = 'distance', width = 5, align = 'right' },\\\
1092 { heading = 'Status', key = 'status' },\\\
1093 },\\\
1094 sortColumn = 'label',\\\
1095 },\\\
1096}\\\
1097\\\
1098local page = UI.Page {\\\
1099 menuBar = UI.MenuBar {\\\
1100 buttons = {\\\
1101 { text = 'Scan', event = 'scan' },\\\
1102 pauseResume[1],\\\
1103 { text = 'Abort', event = 'abort', x = -7 },\\\
1104 },\\\
1105 },\\\
1106 tabs = UI.Tabs {\\\
1107 y = 2, ey = -2,\\\
1108 [1] = blocksTab,\\\
1109 [2] = turtlesTab,\\\
1110 [3] = containTab,\\\
1111 },\\\
1112 info = UI.Window {\\\
1113 y = -1,\\\
1114 backgroundColor = colors.blue,\\\
1115 }\\\
1116}\\\
1117\\\
1118function page.info:draw()\\\
1119 self:clear()\\\
1120 self:write(2, 1, 'Turtles: ' .. Util.size(turtles))\\\
1121 if not chestPoint then\\\
1122 self:write(16, 1, 'No chest')\\\
1123 end\\\
1124 self:write(28, 1, 'Queue: ' .. Util.size(queue))\\\
1125end\\\
1126\\\
1127function turtlesTab.grid:getDisplayValues(row)\\\
1128 row = Util.shallowCopy(row)\\\
1129 row.distance = row.distance and Util.round(row.distance, 1)\\\
1130 row.fuel = row.fuel and row.fuel > 0 and Util.toBytes(row.fuel) or ''\\\
1131 return row\\\
1132end\\\
1133\\\
1134function page:scan()\\\
1135 local gpt = GPS.getPoint()\\\
1136 if not gpt then\\\
1137 return\\\
1138 end\\\
1139 local rawBlocks = scanner:scan()\\\
1140 local candidates = { }\\\
1141\\\
1142 self.totals = Util.reduce(rawBlocks,\\\
1143 function(acc, b)\\\
1144 b.key = table.concat({ b.name, b.metadata }, ':')\\\
1145 local entry = acc[b.key]\\\
1146 if not entry then\\\
1147 b.displayName = itemDB:getName(b.key)\\\
1148 b.count = 1\\\
1149 acc[b.key] = b\\\
1150 else\\\
1151 entry.count = entry.count + 1\\\
1152 end\\\
1153\\\
1154 if b.name == 'computercraft:turtle_advanced' or\\\
1155 b.name == 'computercraft:turtle_expanded' or\\\
1156 b.name == 'computercraft:turtle' then\\\
1157 table.insert(candidates, b)\\\
1158 end\\\
1159\\\
1160 if b.name == 'minecraft:chest' or b.name:find('shulker') then\\\
1161 chestPoint = b\\\
1162 end\\\
1163\\\
1164 -- add relevant blocks to queue\\\
1165 b.x = gpt.x + b.x\\\
1166 b.y = gpt.y + b.y\\\
1167 b.z = gpt.z + b.z\\\
1168 b.pkey = table.concat({ b.x, b.y, b.z }, ':')\\\
1169 if blockTypes[b.key] and inBox(b) then\\\
1170 if not Util.any(turtles, function(t)\\\
1171 return t.pt and t.pt.pkey == b.pkey\\\
1172 end) then\\\
1173 queue[b.pkey] = b\\\
1174 end\\\
1175 else\\\
1176 queue[b.pkey] = nil\\\
1177 end\\\
1178 return acc\\\
1179 end,\\\
1180 { })\\\
1181\\\
1182 for _, b in pairs(candidates) do\\\
1183 local v = scanner.getBlockMeta(b.x - gpt.x, b.y - gpt.y, b.z - gpt.z)\\\
1184 if v and v.computer then\\\
1185 local member = pool[v.computer.id]\\\
1186 if not member then\\\
1187 member = {\\\
1188 id = v.computer.id,\\\
1189 label = v.computer.label,\\\
1190 }\\\
1191 pool[v.computer.id] = member\\\
1192 end\\\
1193\\\
1194 member.fuel = v.turtle.fuel\\\
1195 member.distance = 0\\\
1196\\\
1197 if not v.computer.isOn then\\\
1198 member.status = 'Powered off'\\\
1199 elseif v.turtle.fuel < 100 and not member.active then\\\
1200 member.status = 'Not enough fuel'\\\
1201 elseif not member.active and not member.abort then\\\
1202 local pt = Point.copy(b)\\\
1203 pt.heading = Point.facings[v.state.facing].heading\\\
1204 run(member, pt)\\\
1205 end\\\
1206 end\\\
1207 end\\\
1208end\\\
1209\\\
1210function blocksTab.grid:getDisplayValues(row)\\\
1211 row = Util.shallowCopy(row)\\\
1212 row.count = Util.toBytes(row.count) .. ' '\\\
1213 return row\\\
1214end\\\
1215\\\
1216function blocksTab.grid:getRowTextColor(row, selected)\\\
1217 return blockTypes[row.key] and\\\
1218 colors.yellow or\\\
1219 UI.Grid.getRowTextColor(self, row, selected)\\\
1220end\\\
1221\\\
1222function blocksTab:eventHandler(event)\\\
1223 if event.type == 'grid_select' then\\\
1224 local key = event.selected.key\\\
1225 if blockTypes[key] then\\\
1226 for k,v in pairs(queue) do\\\
1227 if v.key == key then\\\
1228 queue[k] = nil\\\
1229 end\\\
1230 end\\\
1231 blockTypes[key] = nil\\\
1232 else\\\
1233 blockTypes[key] = true\\\
1234 end\\\
1235 self.grid:draw()\\\
1236 end\\\
1237end\\\
1238\\\
1239function page:eventHandler(event)\\\
1240 if event.type == 'scan' then\\\
1241 blocksTab.grid:setValues(self.totals)\\\
1242 blocksTab.grid:draw()\\\
1243 self.tabs:selectTab(blocksTab)\\\
1244\\\
1245 elseif event.type == 'pause' then\\\
1246 paused = true\\\
1247 Util.merge(event.button, pauseResume[2])\\\
1248 event.button:draw()\\\
1249\\\
1250 elseif event.type == 'resume' then\\\
1251 paused = false\\\
1252 Util.merge(event.button, pauseResume[1])\\\
1253 event.button:draw()\\\
1254\\\
1255 elseif event.type == 'contain' then\\\
1256 local pt = { gps.locate() }\\\
1257 local pos = {\\\
1258 x = pt[1],\\\
1259 y = pt[2],\\\
1260 z = pt[3],\\\
1261 }\\\
1262\\\
1263 if not box then\\\
1264 offset = {\\\
1265 x = math.floor(pos.x),\\\
1266 y = math.floor(pos.y),\\\
1267 z = math.floor(pos.z),\\\
1268 }\\\
1269 box = {\\\
1270 x = math.floor(pos.x),\\\
1271 y = math.floor(pos.y) - 1,\\\
1272 z = math.floor(pos.z),\\\
1273 }\\\
1274 containTab.textArea.value = containerText[2]\\\
1275 elseif not box.ex then\\\
1276 box.ex = math.floor(pos.x)\\\
1277 box.ey = math.floor(pos.y) - 1\\\
1278 box.ez = math.floor(pos.z)\\\
1279 box = Point.normalizeBox(box)\\\
1280 containTab.textArea.value = containerText[3]\\\
1281 else\\\
1282 box = nil\\\
1283 containTab.textArea.value = containerText[1]\\\
1284 end\\\
1285\\\
1286 containTab.textArea:draw()\\\
1287 drawContainer(pos)\\\
1288\\\
1289 elseif event.type == 'abort' then\\\
1290 for _, v in pairs(pool) do\\\
1291 v.abort = true\\\
1292 v.status = 'aborting'\\\
1293 end\\\
1294 spt = Point.above(locate())\\\
1295 abort = true\\\
1296 end\\\
1297\\\
1298 UI.Page.eventHandler(self, event)\\\
1299end\\\
1300\\\
1301Event.onInterval(5, function()\\\
1302 if not abort and not paused then\\\
1303\\\
1304 --local meta = scanner.getMetaOwner()\\\
1305 --if meta.isSneaking then\\\
1306 page:scan()\\\
1307 -- Sound.play('entity.bobber.throw', .6)\\\
1308 --end\\\
1309 end\\\
1310end)\\\
1311\\\
1312Event.onInterval(1, function()\\\
1313 for id,v in pairs(network) do\\\
1314 if v.fuel then\\\
1315 if pool[id] then\\\
1316 pool[id].fuel = v.fuel\\\
1317 pool[id].distance = v.distance\\\
1318 end\\\
1319 end\\\
1320 end\\\
1321\\\
1322 if abort and Util.size(turtles) == 0 then\\\
1323 Event.exitPullEvents()\\\
1324 end\\\
1325\\\
1326 if turtlesTab.enabled then\\\
1327 turtlesTab.grid:update()\\\
1328 turtlesTab.grid:draw()\\\
1329 end\\\
1330\\\
1331 page.info:draw()\\\
1332 page.info:sync()\\\
1333\\\
1334 if canvas then\\\
1335 canvas.turtles.setText(tostring(Util.size(turtles)))\\\
1336 canvas.queue.setText(tostring(Util.size(queue)))\\\
1337 end\\\
1338end)\\\
1339\\\
1340Event.onTimeout(.1, function()\\\
1341 page:scan()\\\
1342 blocksTab.grid:setValues(page.totals)\\\
1343 blocksTab.grid:draw()\\\
1344 page:sync()\\\
1345end)\\\
1346\\\
1347UI:setPage(page)\\\
1348\\\
1349--[[\\\
1350Event.onTerminate(function()\\\
1351 spt = Point.above(locate())\\\
1352 for _, v in pairs(pool) do\\\
1353 v.status = 'aborting'\\\
1354 v.abort = true\\\
1355 end\\\
1356 abort = true\\\
1357end)\\\
1358]]\\\
1359\\\
1360Event.pullEvents()\\\
1361\\\
1362if canvas then\\\
1363 canvas3d.clear()\\\
1364 canvas.group.remove()\\\
1365end\",\
1366 [ \"core/.package\" ] = \"{\\\
1367 title = 'APIs used by various programs',\\\
1368 repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/core',\\\
1369 description = [[Provides APIs used by Opus applications.]],\\\
1370 license = 'MIT',\\\
1371}\",\
1372 [ \"monitor/termShare.lua\" ] = \"local Util = require('opus.util')\\\
1373\\\
1374local device = _G.device\\\
1375local multishell = _ENV.multishell\\\
1376local os = _G.os\\\
1377local term = _G.term\\\
1378\\\
1379-- list this terminal in the devices list so it's available via\\\
1380-- peripheral sharing\\\
1381\\\
1382local args = Util.parse(...)\\\
1383local name = args[1] or error('Syntax: termShare [--title=title] term_name')\\\
1384local title = args.title\\\
1385\\\
1386device[name] = term.current()\\\
1387device[name].name = name\\\
1388device[name].side = name\\\
1389device[name].type = 'terminal'\\\
1390\\\
1391if title then\\\
1392 multishell.setTitle(multishell.getCurrent(), title)\\\
1393end\\\
1394os.pullEventRaw('terminate')\\\
1395os.queueEvent('peripheral_detach', name)\",\
1396 [ \"common/.package\" ] = \"{\\\
1397 title = 'Useful applications',\\\
1398 repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/common',\\\
1399 description = [[Various applications for Opus.\\\
1400\\\
1401* App Store\\\
1402* Peripheral API viewer\\\
1403* Disk / Computer copier\\\
1404* A better editor (copy/paste/undo)\\\
1405* Turtle swarm miner\\\
1406* Turtle follow\\\
1407* Screen recorder\\\
1408* and more...\\\
1409 ]],\\\
1410 license = 'MIT',\\\
1411 required = {\\\
1412 'core',\\\
1413 },\\\
1414}\",\
1415 [ \"common/SoundPlayer.lua\" ] = \"local Sound = require('opus.sound')\\\
1416local UI = require('opus.ui')\\\
1417local Util = require('opus.util')\\\
1418\\\
1419local peripheral = _G.peripheral\\\
1420\\\
1421if not peripheral.find('speaker') then\\\
1422 error('No speaker attached')\\\
1423end\\\
1424\\\
1425local rawSounds = Util.readLines('packages/common/etc/sounds.txt') or error('Unable to read sounds file')\\\
1426local sounds = { }\\\
1427for _, s in pairs(rawSounds) do\\\
1428 table.insert(sounds, { name = s })\\\
1429end\\\
1430\\\
1431UI:configure('SoundPlayer', ...)\\\
1432\\\
1433local page = UI.Page {\\\
1434 labelText = UI.Text {\\\
1435 x = 3, y = 2,\\\
1436 value = 'Search',\\\
1437 },\\\
1438 filter = UI.TextEntry {\\\
1439 x = 10, y = 2, ex = -3,\\\
1440 limit = 32,\\\
1441 },\\\
1442 grid = UI.ScrollingGrid {\\\
1443 y = 4,\\\
1444 columns = {\\\
1445 { heading = 'Name', key = 'name' },\\\
1446 },\\\
1447 values = sounds,\\\
1448 },\\\
1449}\\\
1450\\\
1451function page:eventHandler(event)\\\
1452 if event.type == 'grid_select' then\\\
1453 Sound.play(event.selected.name)\\\
1454\\\
1455 elseif event.type == 'text_change' then\\\
1456 if not event.text then\\\
1457 self.grid.values = sounds\\\
1458 else\\\
1459 self.grid.values = { }\\\
1460 for _,f in pairs(sounds) do\\\
1461 if string.find(f.name, event.text) then\\\
1462 table.insert(self.grid.values, f)\\\
1463 end\\\
1464 end\\\
1465 end\\\
1466 self.grid:update()\\\
1467 self.grid:setIndex(1)\\\
1468 self.grid:draw()\\\
1469\\\
1470 else\\\
1471 return UI.Page.eventHandler(self, event)\\\
1472 end\\\
1473 return true\\\
1474end\\\
1475\\\
1476UI:setPage(page)\\\
1477UI:start()\",\
1478 [ \"common/etc/scripts/abort\" ] = \"turtle.abort(true)\",\
1479 [ \"common/recorder.lua\" ] = \"-- +---------------------+------------+---------------------+\\\
1480-- | | | |\\\
1481-- | | RecGif | |\\\
1482-- | | | |\\\
1483-- +---------------------+------------+---------------------+\\\
1484\\\
1485local version = \\\"Version 1.1.6\\\"\\\
1486\\\
1487-- Records your terminal and saves the result as an animating GIF.\\\
1488-- http://www.computercraft.info/forums2/index.php?/topic/24840-recgif/\\\
1489\\\
1490-- ----------------------------------------------------------\\\
1491\\\
1492-- Original code by Bomb Bloke\\\
1493-- Modified to integrate with opus os\\\
1494\\\
1495local Util = require('opus.util')\\\
1496\\\
1497local multishell = _ENV.multishell\\\
1498local os = _G.os\\\
1499\\\
1500local colours = _G.colors\\\
1501\\\
1502local args, options = Util.parse(...)\\\
1503\\\
1504local oldTerm, showInput, skipLast, lastDelay, curInput = Util.shallowCopy(_G.device.terminal), false, false, 2, \\\"\\\"\\\
1505local curBlink, oldBlink, tTerm, buffer, colourNum, xPos, yPos, oldXPos, oldYPos, tCol, bCol, xSize, ySize = false, false, {}, {}, {}, 1, 1, 1, 1, colours.white, colours.black, oldTerm.getSize()\\\
1506local greys, buttons = {[\\\"0\\\"] = true, [\\\"7\\\"] = true, [\\\"8\\\"] = true, [\\\"f\\\"] = true}, {\\\"l\\\", \\\"r\\\", \\\"m\\\"}\\\
1507local charW, charH, chars\\\
1508\\\
1509local calls = { }\\\
1510local curCalls = { delay = 0 }\\\
1511local callListCount = 0\\\
1512local callCount = 0\\\
1513\\\
1514local function showSyntax()\\\
1515 print('Gif Recorder by Bomb Bloke\\\\n')\\\
1516 print('Syntax: recGif [-i] [-s] [-ld:<delay>] filename')\\\
1517 print(' --showInput : show input')\\\
1518 print(' --skipLast : skip last')\\\
1519 print(' --lastDelay : last delay')\\\
1520 print(' --noResize : dont resize')\\\
1521end\\\
1522\\\
1523if options.showInput then\\\
1524 showInput, ySize = true, ySize + 1\\\
1525end\\\
1526\\\
1527if options.skipLast then\\\
1528 skipLast = true\\\
1529end\\\
1530\\\
1531if options.lastDelay then\\\
1532 lastDelay = options.lastDelay\\\
1533end\\\
1534\\\
1535if options.help then\\\
1536 showSyntax()\\\
1537 return\\\
1538end\\\
1539\\\
1540if options.daemon then\\\
1541 _G.device.keyboard.addHotkey('control-P', function()\\\
1542 multishell.openTab(_ENV, {\\\
1543 path = 'sys/apps/shell.lua',\\\
1544 args = { arg[0], '--noResize', '--rawOutput', 'recorder.gif' },\\\
1545 })\\\
1546 end)\\\
1547 return\\\
1548end\\\
1549\\\
1550print('Gif Recorder by Bomb Bloke')\\\
1551print(version)\\\
1552print('\\\\nPress control-p to stop recording')\\\
1553\\\
1554local filename = args[1]\\\
1555if not filename then\\\
1556 print('Enter file name:')\\\
1557 filename = read()\\\
1558end\\\
1559\\\
1560if #filename == 0 then\\\
1561 showSyntax()\\\
1562 print()\\\
1563 error('Invalid file name')\\\
1564end\\\
1565\\\
1566print('Initializing...')\\\
1567\\\
1568-- don't pollute global env\\\
1569-- convert these to require style apis\\\
1570local function loadAPI(url, env)\\\
1571 local apiEnv = Util.shallowCopy(env)\\\
1572 apiEnv.shell = nil\\\
1573 apiEnv.multishell = nil\\\
1574 setmetatable(apiEnv, { __index = _G })\\\
1575 local fn, m = Util.loadUrl(url, apiEnv)\\\
1576 if not fn then\\\
1577 error(m)\\\
1578 end\\\
1579 fn()\\\
1580 return apiEnv\\\
1581end\\\
1582\\\
1583bbpack = loadAPI('http://pastebin.com/raw/cUYTGbpb', getfenv(1))\\\
1584GIF = loadAPI('http://pastebin.com/raw/5uk9uRjC', getfenv(1))\\\
1585\\\
1586local s, m = Util.runUrl(getfenv(1), 'http://pastebin.com/raw/cUYTGbpb', 'get', 'CnLzL5fg')\\\
1587if not s then\\\
1588 error(m)\\\
1589end\\\
1590-- 'Y0eLUPtr')\\\
1591-- CnLzL5fg\\\
1592local function snooze()\\\
1593 local myEvent = tostring({})\\\
1594 os.queueEvent(myEvent)\\\
1595 os.pullEvent(myEvent)\\\
1596end\\\
1597\\\
1598local function safeString(text)\\\
1599 local newText = {}\\\
1600\\\
1601 for i = 1, #text do\\\
1602 local val = text:byte(i)\\\
1603 newText[i] = (val > 31 and val < 127) and val or 63\\\
1604 end\\\
1605\\\
1606 return string.char(unpack(newText))\\\
1607end\\\
1608\\\
1609local function safeCol(text, subst)\\\
1610 local newText = {}\\\
1611\\\
1612 for i = 1, #text do\\\
1613 local val = text:sub(i, i)\\\
1614 newText[i] = greys[val] and val or subst\\\
1615 end\\\
1616\\\
1617 return table.concat(newText)\\\
1618end\\\
1619\\\
1620-- Build a terminal that records stuff:\\\
1621\\\
1622local recTerm = _G.device.terminal\\\
1623\\\
1624for key, func in pairs(oldTerm) do\\\
1625 if type(func) == 'function' then\\\
1626 recTerm[key] = function(...)\\\
1627 local result = { func(...) }\\\
1628\\\
1629 if callCount == 0 then\\\
1630 os.queueEvent('capture_frame')\\\
1631 end\\\
1632 callCount = callCount + 1\\\
1633 curCalls[callCount] = { key, ... }\\\
1634 return table.unpack(result)\\\
1635 end\\\
1636 end\\\
1637end\\\
1638\\\
1639local tabId = multishell.getCurrent()\\\
1640multishell.hideTab(tabId)\\\
1641\\\
1642if not options.noResize then\\\
1643 os.queueEvent('term_resize')\\\
1644end\\\
1645\\\
1646_G.device.keyboard.addHotkey('control-p', function()\\\
1647 os.queueEvent('recorder_stop')\\\
1648end)\\\
1649\\\
1650local curTime = os.clock() - 1\\\
1651\\\
1652while true do\\\
1653 local event = { os.pullEventRaw() }\\\
1654\\\
1655 if event[1] == 'recorder_stop' or event[1] == 'terminate' then\\\
1656 break\\\
1657 end\\\
1658\\\
1659 if event[1] == 'capture_frame' then\\\
1660 local newTime = os.clock()\\\
1661\\\
1662 if callListCount > 0 then\\\
1663 calls[callListCount].delay = (newTime - curTime)\\\
1664 end\\\
1665\\\
1666 curTime = newTime\\\
1667 callListCount = callListCount + 1\\\
1668 calls[callListCount] = curCalls\\\
1669\\\
1670 curCalls, callCount = { delay = 0 }, 0\\\
1671 end\\\
1672end\\\
1673\\\
1674_G.device.keyboard.removeHotkey('control-p')\\\
1675\\\
1676for k,fn in pairs(oldTerm) do\\\
1677 _G.device.terminal[k] = fn\\\
1678end\\\
1679\\\
1680multishell.unhideTab(tabId)\\\
1681multishell.setFocus(tabId)\\\
1682\\\
1683if #calls[#calls] == 0 then calls[#calls] = nil end\\\
1684if skipLast and #calls > 1 then calls[#calls] = nil end\\\
1685\\\
1686calls[#calls].delay = lastDelay\\\
1687\\\
1688if options.rawOutput then\\\
1689 Util.writeTable('tmp/raw.txt', calls)\\\
1690 return\\\
1691end\\\
1692\\\
1693print(string.format(\\\"Encoding %d frames...\\\", #calls))\\\
1694\\\
1695-- Perform a quick re-parse of the recorded data (adding frames for when the cursor blinks):\\\
1696\\\
1697do\\\
1698 local callListCount, tempCalls, blink, oldBlink, curBlink, blinkDelay = 1, {}, false, false, true, 0\\\
1699\\\
1700 for i = 1, #calls - 1 do\\\
1701 curCalls = calls[i]\\\
1702 tempCalls[callListCount] = curCalls\\\
1703 for j = 1, #curCalls do if curCalls[j][1] == \\\"setCursorBlink\\\" then blink = curCalls[j][2] end end\\\
1704\\\
1705 if blink then\\\
1706 if blinkDelay == 0 then\\\
1707 curCalls[#curCalls + 1] = {\\\"toggleCur\\\", curBlink}\\\
1708 blinkDelay, curBlink = 0.4, not curBlink\\\
1709 end\\\
1710\\\
1711 while tempCalls[callListCount].delay > blinkDelay do\\\
1712 local remainder = tempCalls[callListCount].delay - blinkDelay\\\
1713 tempCalls[callListCount].delay = blinkDelay\\\
1714 callListCount = callListCount + 1\\\
1715 tempCalls[callListCount] = {{\\\"toggleCur\\\", curBlink}, [\\\"delay\\\"] = remainder}\\\
1716 blinkDelay, curBlink = 0.4, not curBlink\\\
1717 end\\\
1718\\\
1719 blinkDelay = blinkDelay - tempCalls[callListCount].delay\\\
1720 else\\\
1721 if oldBlink then curCalls[#curCalls + 1] = {\\\"toggleCur\\\", false} end\\\
1722 blinkDelay = (curCalls.delay - blinkDelay) % 0.4\\\
1723 end\\\
1724\\\
1725 callListCount, oldBlink = callListCount + 1, blink\\\
1726 end\\\
1727\\\
1728 tempCalls[callListCount] = calls[#calls]\\\
1729 tempCalls[callListCount][#tempCalls[callListCount] + 1] = {\\\"toggleCur\\\", false}\\\
1730\\\
1731 calls, curCalls = tempCalls, nil\\\
1732end\\\
1733\\\
1734snooze()\\\
1735\\\
1736-- Load font data:\\\
1737do\\\
1738 local ascii, counter = GIF.toPaintutils(GIF.flattenGIF(GIF.loadGIF(\\\"ascii.gif\\\"))), 0\\\
1739 local newFont, ybump, xbump = #ascii ~= #ascii[1], 0, 0\\\
1740 charW, charH, chars = newFont and #ascii[1] / 16 or #ascii[1] * 3 / 64, #ascii / 16, {}\\\
1741\\\
1742 for yy = 0, newFont and 15 or 7 do\\\
1743 for xx = 0, 15 do\\\
1744 local newChar, length = {}, 0\\\
1745\\\
1746 -- Place in 2d grid of bools:\\\
1747 for y = 1, charH do\\\
1748 local newRow = {}\\\
1749\\\
1750 for x = 1, charW do\\\
1751 local set = ascii[y + ybump][x + xbump] == 1\\\
1752 if set and x > length then length = x end\\\
1753 newRow[x] = set\\\
1754 end\\\
1755\\\
1756 newChar[y] = newRow\\\
1757 end\\\
1758\\\
1759 -- Center:\\\
1760 if not newFont then for y = 1, charH do for x = 1, math.floor((charW - length) / 2) do table.insert(newChar[y], 1, false) end end end\\\
1761\\\
1762 chars[counter] = newChar\\\
1763 counter, xbump = counter + 1, xbump + (newFont and charW or charH)\\\
1764 end\\\
1765 xbump, ybump = 0, ybump + charH\\\
1766 end\\\
1767end\\\
1768\\\
1769snooze()\\\
1770\\\
1771-- Terminal data translation:\\\
1772\\\
1773do\\\
1774 local hex, counter = \\\"0123456789abcdef\\\", 1\\\
1775\\\
1776 for i = 1, 16 do\\\
1777 colourNum[counter] = hex:sub(i, i)\\\
1778 counter = counter * 2\\\
1779 end\\\
1780end\\\
1781\\\
1782for y = 1, ySize do\\\
1783 buffer[y] = {}\\\
1784 for x = 1, xSize do buffer[y][x] = {\\\" \\\", colourNum[tCol], colourNum[bCol]} end\\\
1785end\\\
1786\\\
1787if showInput then for x = 1, xSize do buffer[ySize][x][3] = colourNum[colours.lightGrey] end end\\\
1788\\\
1789tTerm.blit = function(text, fgCol, bgCol)\\\
1790 if xPos > xSize or xPos + #text - 1 < 1 or yPos < 1 or yPos > ySize then return end\\\
1791\\\
1792 if not _HOST then text = safeString(text) end\\\
1793\\\
1794 if not term.isColour() then\\\
1795 fgCol = safeCol(fgCol, \\\"0\\\")\\\
1796 bgCol = safeCol(bgCol, \\\"f\\\")\\\
1797 end\\\
1798\\\
1799 if xPos < 1 then\\\
1800 text = text:sub(2 - xPos)\\\
1801 fgCol = fgCol:sub(2 - xPos)\\\
1802 bgCol = bgCol:sub(2 - xPos)\\\
1803 xPos = 1\\\
1804 end\\\
1805\\\
1806 if xPos + #text - 1 > xSize then\\\
1807 text = text:sub(1, xSize - xPos + 1)\\\
1808 fgCol = fgCol:sub(1, xSize - xPos + 1)\\\
1809 bgCol = bgCol:sub(1, xSize - xPos + 1)\\\
1810 end\\\
1811\\\
1812 for x = 1, #text do\\\
1813 buffer[yPos][xPos + x - 1][1] = text:sub(x, x)\\\
1814 buffer[yPos][xPos + x - 1][2] = fgCol:sub(x, x)\\\
1815 buffer[yPos][xPos + x - 1][3] = bgCol:sub(x, x)\\\
1816 end\\\
1817\\\
1818 xPos = xPos + #text\\\
1819end\\\
1820\\\
1821tTerm.write = function(text)\\\
1822 text = tostring(text)\\\
1823 tTerm.blit(text, string.rep(colourNum[tCol], #text), string.rep(colourNum[bCol], #text))\\\
1824end\\\
1825\\\
1826tTerm.clearLine = function()\\\
1827 local oldXPos = xPos\\\
1828\\\
1829 xPos = 1\\\
1830 tTerm.write(string.rep(\\\" \\\", xSize))\\\
1831\\\
1832 xPos = oldXPos\\\
1833end\\\
1834\\\
1835tTerm.clear = function()\\\
1836 local oldXPos, oldYPos = xPos, yPos\\\
1837\\\
1838 for y = 1, ySize do\\\
1839 xPos, yPos = 1, y\\\
1840 tTerm.write(string.rep(\\\" \\\", xSize))\\\
1841 end\\\
1842\\\
1843 xPos, yPos = oldXPos, oldYPos\\\
1844end\\\
1845\\\
1846tTerm.setCursorPos = function(x, y)\\\
1847 xPos, yPos = math.floor(x), math.floor(y)\\\
1848end\\\
1849\\\
1850tTerm.setTextColour = function(col)\\\
1851 tCol = col\\\
1852end\\\
1853\\\
1854tTerm.setTextColor = function(col)\\\
1855 tCol = col\\\
1856end\\\
1857\\\
1858tTerm.setBackgroundColour = function(col)\\\
1859 bCol = col\\\
1860end\\\
1861\\\
1862tTerm.setBackgroundColor = function(col)\\\
1863 bCol = col\\\
1864end\\\
1865\\\
1866tTerm.scroll = function(lines)\\\
1867 if math.abs(lines) < ySize then\\\
1868 local oldXPos, oldYPos = xPos, yPos\\\
1869\\\
1870 for y = 1, ySize do\\\
1871 if y + lines > 0 and y + lines <= ySize then\\\
1872 for x = 1, xSize do\\\
1873 xPos, yPos = x, y\\\
1874 tTerm.blit(buffer[y + lines][x][1], buffer[y + lines][x][2], buffer[y + lines][x][3])\\\
1875 end\\\
1876 else\\\
1877 yPos = y\\\
1878 tTerm.clearLine()\\\
1879 end\\\
1880 end\\\
1881\\\
1882 xPos, yPos = oldXPos, oldYPos\\\
1883 else tTerm.clear() end\\\
1884end\\\
1885\\\
1886tTerm.toggleCur = function(newBlink)\\\
1887 curBlink = newBlink\\\
1888end\\\
1889\\\
1890tTerm.newInput = function(input)\\\
1891 local oldTC, oldBC, oldX, oldY = tCol, bCol, xPos, yPos\\\
1892 tCol, bCol, xPos, yPos, ySize, input = colours.grey, colours.lightGrey, 1, ySize + 1, ySize + 1, input .. \\\" \\\"\\\
1893\\\
1894 while #curInput + #input + 1 > xSize do curInput = curInput:sub(curInput:find(\\\" \\\") + 1) end\\\
1895 curInput = curInput .. input .. \\\" \\\"\\\
1896 tTerm.clearLine()\\\
1897 tTerm.write(curInput)\\\
1898\\\
1899 tCol, bCol, xPos, yPos, ySize = oldTC, oldBC, oldX, oldY, ySize - 1\\\
1900end\\\
1901\\\
1902tTerm.key = function(key)\\\
1903 tTerm.newInput((not keys.getName(key)) and \\\"unknownKey\\\" or keys.getName(key))\\\
1904end\\\
1905\\\
1906tTerm.mouse_click = function(button, x, y)\\\
1907 tTerm.newInput(buttons[button] .. \\\"C@\\\" .. tostring(x) .. \\\"x\\\" .. tostring(y))\\\
1908end\\\
1909\\\
1910local image = {[\\\"width\\\"] = xSize * charW, [\\\"height\\\"] = ySize * charH}\\\
1911\\\
1912for i = 1, #calls do\\\
1913 local xMin, yMin, xMax, yMax, oldBuffer, curCalls, changed = xSize + 1, ySize + 1, 0, 0, {}, calls[i], false\\\
1914 calls[i] = nil\\\
1915\\\
1916 for y = 1, ySize do\\\
1917 oldBuffer[y] = {}\\\
1918 for x = 1, xSize do oldBuffer[y][x] = {buffer[y][x][1], buffer[y][x][2], buffer[y][x][3], buffer[y][x][4]} end\\\
1919 end\\\
1920\\\
1921 snooze()\\\
1922\\\
1923 if showInput then ySize = ySize - 1 end\\\
1924 for j = 1, #curCalls do if tTerm[curCalls[j][1]] then tTerm[curCalls[j][1]](unpack(curCalls[j], 2)) end end\\\
1925 if showInput then ySize = ySize + 1 end\\\
1926\\\
1927 if i > 1 then\\\
1928 for yy = 1, ySize do for xx = 1, xSize do if buffer[yy][xx][1] ~= oldBuffer[yy][xx][1] or (buffer[yy][xx][2] ~= oldBuffer[yy][xx][2] and buffer[yy][xx][1] ~= \\\" \\\") or buffer[yy][xx][3] ~= oldBuffer[yy][xx][3] then\\\
1929 changed = true\\\
1930 if xx < xMin then xMin = xx end\\\
1931 if xx > xMax then xMax = xx end\\\
1932 if yy < yMin then yMin = yy end\\\
1933 if yy > yMax then yMax = yy end\\\
1934 end end end\\\
1935 else xMin, yMin, xMax, yMax, changed = 1, 1, xSize, ySize, true end\\\
1936\\\
1937 if oldBlink and (xPos ~= oldXPos or yPos ~= oldYPos or not curBlink) and oldXPos > 0 and oldYPos > 0 and oldXPos <= xSize and oldYPos <= ySize then\\\
1938 changed = true\\\
1939 if oldXPos < xMin then xMin = oldXPos end\\\
1940 if oldXPos > xMax then xMax = oldXPos end\\\
1941 if oldYPos < yMin then yMin = oldYPos end\\\
1942 if oldYPos > yMax then yMax = oldYPos end\\\
1943 buffer[oldYPos][oldXPos][4] = false\\\
1944 end\\\
1945\\\
1946 if curBlink and (xPos ~= oldXPos or yPos ~= oldYPos or not oldBlink) and xPos > 0 and yPos > 0 and xPos <= xSize and yPos <= ySize then\\\
1947 changed = true\\\
1948 if xPos < xMin then xMin = xPos end\\\
1949 if xPos > xMax then xMax = xPos end\\\
1950 if yPos < yMin then yMin = yPos end\\\
1951 if yPos > yMax then yMax = yPos end\\\
1952 buffer[yPos][xPos][4] = true\\\
1953 end\\\
1954\\\
1955 oldBlink, oldXPos, oldYPos = curBlink, xPos, yPos\\\
1956\\\
1957 local thisFrame = {\\\
1958 [\\\"xstart\\\"] = (xMin - 1) * charW,\\\
1959 [\\\"ystart\\\"] = (yMin - 1) * charH,\\\
1960 [\\\"xend\\\"] = (xMax - xMin + 1) * charW,\\\
1961 [\\\"yend\\\"] = (yMax - yMin + 1) * charH,\\\
1962 [\\\"delay\\\"] = curCalls.delay,\\\
1963 [\\\"disposal\\\"] = 1\\\
1964 }\\\
1965\\\
1966 for y = 1, (yMax - yMin + 1) * charH do\\\
1967 local row = {}\\\
1968 for x = 1, (xMax - xMin + 1) * charW do row[x] = \\\" \\\" end\\\
1969 thisFrame[y] = row\\\
1970 end\\\
1971\\\
1972 snooze()\\\
1973\\\
1974 for yy = yMin, yMax do\\\
1975 local yBump = (yy - yMin) * charH\\\
1976\\\
1977 for xx = xMin, xMax do if buffer[yy][xx][1] ~= oldBuffer[yy][xx][1] or (buffer[yy][xx][2] ~= oldBuffer[yy][xx][2] and buffer[yy][xx][1] ~= \\\" \\\") or buffer[yy][xx][3] ~= oldBuffer[yy][xx][3] or buffer[yy][xx][4] ~= oldBuffer[yy][xx][4] or i == 1 then\\\
1978 local thisChar, thisT, thisB, xBump = chars[buffer[yy][xx][1]:byte()], buffer[yy][xx][2], buffer[yy][xx][3], (xx - xMin) * charW\\\
1979if thisChar then\\\
1980 for y = 1, charH do\\\
1981 for x = 1, charW do\\\
1982 local ch = thisChar[y][x] and thisT or thisB\\\
1983 thisFrame[y + yBump][x + xBump] = ch\\\
1984 end\\\
1985 end\\\
1986end\\\
1987\\\
1988 if buffer[yy][xx][4] then\\\
1989 thisT, thisChar = colourNum[tCol], chars[95]\\\
1990 for y = 1, charH do for x = 1, charW do if thisChar[y][x] then thisFrame[y + yBump][x + xBump] = thisT end end end\\\
1991 end\\\
1992 end end\\\
1993\\\
1994 for y = yBump + 1, yBump + charH do\\\
1995 local skip, chars, row = 0, {}, {}\\\
1996\\\
1997 for x = 1, #thisFrame[y] do\\\
1998 if thisFrame[y][x] == \\\" \\\" then\\\
1999 if #chars > 0 then\\\
2000 row[#row + 1] = table.concat(chars)\\\
2001 chars = {}\\\
2002 end\\\
2003\\\
2004 skip = skip + 1\\\
2005 else\\\
2006 if skip > 0 then\\\
2007 row[#row + 1] = skip\\\
2008 skip = 0\\\
2009 end\\\
2010\\\
2011 chars[#chars + 1] = thisFrame[y][x]\\\
2012 end\\\
2013 end\\\
2014\\\
2015 if #chars > 0 then row[#row + 1] = table.concat(chars) end\\\
2016 thisFrame[y] = row\\\
2017 end\\\
2018\\\
2019 snooze()\\\
2020 end\\\
2021\\\
2022 if changed then \\\
2023 image[#image + 1] = thisFrame\\\
2024 else\\\
2025 image[#image].delay = image[#image].delay + curCalls.delay\\\
2026 end\\\
2027end\\\
2028\\\
2029buffer = nil\\\
2030\\\
2031GIF.saveGIF(image, filename)\\\
2032\\\
2033fs.delete('ascii.gif')\\\
2034\\\
2035print(\\\"Encode complete\\\")\",\
2036 [ \"core/apis/refinedAdapter.lua\" ] = \"local class = require('opus.class')\\\
2037local itemDB = require('core.itemDB')\\\
2038local Peripheral = require('opus.peripheral')\\\
2039local Util = require('opus.util')\\\
2040\\\
2041local RefinedAdapter = class()\\\
2042\\\
2043function RefinedAdapter:init(args)\\\
2044 local defaults = {\\\
2045 name = 'refinedStorage',\\\
2046 }\\\
2047 Util.merge(self, defaults)\\\
2048 Util.merge(self, args)\\\
2049\\\
2050 local controller\\\
2051 if not self.side then\\\
2052 controller = Peripheral.getByMethod('getCraftingTasks')\\\
2053 else\\\
2054 controller = Peripheral.getBySide(self.side)\\\
2055 end\\\
2056\\\
2057 if controller then\\\
2058 Util.merge(self, controller)\\\
2059 end\\\
2060end\\\
2061\\\
2062function RefinedAdapter:isValid()\\\
2063 return not not self.getCraftingTasks\\\
2064end\\\
2065\\\
2066function RefinedAdapter:getItemDetails(item)\\\
2067 local detail = self.findItems(item)\\\
2068 if detail and #detail > 0 then\\\
2069 return detail[1].getMetadata()\\\
2070 end\\\
2071end\\\
2072\\\
2073function RefinedAdapter:getCachedItemDetails(item)\\\
2074 local cached = itemDB:get(item)\\\
2075 if cached then\\\
2076 return cached\\\
2077 end\\\
2078\\\
2079 local detail = self:getItemDetails(item)\\\
2080 if detail then\\\
2081 return itemDB:add(detail)\\\
2082 end\\\
2083end\\\
2084\\\
2085function RefinedAdapter:refresh(throttle)\\\
2086 return self:listItems(throttle)\\\
2087end\\\
2088\\\
2089function RefinedAdapter:listItems(throttle)\\\
2090 local items = { }\\\
2091 throttle = throttle or Util.throttle()\\\
2092\\\
2093 local s, m = pcall(function()\\\
2094 for _,v in pairs(self.listAvailableItems()) do\\\
2095 --if v.count > 0 then\\\
2096 local item = self:getCachedItemDetails(v)\\\
2097 if item then\\\
2098 item = Util.shallowCopy(item)\\\
2099 item.count = v.count\\\
2100 table.insert(items, item)\\\
2101 end\\\
2102 --end\\\
2103 throttle()\\\
2104 end\\\
2105 end)\\\
2106\\\
2107 if not s and m then\\\
2108 _G._syslog(m)\\\
2109 end\\\
2110\\\
2111 itemDB:flush()\\\
2112 if not Util.empty(items) then\\\
2113 return items\\\
2114 end\\\
2115end\\\
2116\\\
2117function RefinedAdapter:getItemInfo(item)\\\
2118 return self:getItemDetails(item)\\\
2119end\\\
2120\\\
2121function RefinedAdapter:isCPUAvailable()\\\
2122 return true\\\
2123end\\\
2124\\\
2125function RefinedAdapter:craft(item, qty)\\\
2126 local detail = self.findItem(item)\\\
2127 if detail then\\\
2128 return detail.craft(qty)\\\
2129 end\\\
2130end\\\
2131\\\
2132function RefinedAdapter:isCrafting(item)\\\
2133 for _,task in pairs(self.getCraftingTasks()) do\\\
2134 local output = task.getPattern().outputs[1]\\\
2135 if output.name == item.name and\\\
2136 output.damage == item.damage and\\\
2137 output.nbtHash == item.nbtHash then\\\
2138 return true\\\
2139 end\\\
2140 end\\\
2141 return false\\\
2142end\\\
2143\\\
2144function RefinedAdapter:provide(item, qty, slot, direction)\\\
2145 return pcall(function()\\\
2146 for _,stack in pairs(self.listAvailableItems()) do\\\
2147 if stack.name == item.name and\\\
2148 (not item.damage or stack.damage == item.damage) and\\\
2149 (not item.nbtHash or stack.nbtHash == item.nbtHash) then\\\
2150 local amount = math.min(qty, stack.count)\\\
2151 if amount > 0 then\\\
2152 local detail = self.findItem(item)\\\
2153 if detail then\\\
2154 return detail.export(direction or self.direction, amount, slot)\\\
2155 end\\\
2156 end\\\
2157 qty = qty - amount\\\
2158 if qty <= 0 then\\\
2159 break\\\
2160 end\\\
2161 end\\\
2162 end\\\
2163 end)\\\
2164end\\\
2165\\\
2166function RefinedAdapter:extract(slot, qty, toSlot)\\\
2167 self.pushItems(self.direction, slot, qty, toSlot)\\\
2168end\\\
2169\\\
2170function RefinedAdapter:insert(slot, qty, toSlot)\\\
2171 self.pullItems(self.direction, slot, qty, toSlot)\\\
2172end\\\
2173\\\
2174return RefinedAdapter\",\
2175 [ \"monitor/mwm.lua\" ] = \"local Alt = require('opus.alternate')\\\
2176local Terminal = require('opus.terminal')\\\
2177local trace = require('opus.trace')\\\
2178local Util = require('opus.util')\\\
2179\\\
2180local colors = _G.colors\\\
2181local os = _G.os\\\
2182local peripheral = _G.peripheral\\\
2183local printError = _G.printError\\\
2184local shell = _ENV.shell\\\
2185local term = _G.term\\\
2186local window = _G.window\\\
2187\\\
2188local function syntax()\\\
2189 error('Syntax:\\\\nmwm [--config=filename] [monitor]')\\\
2190end\\\
2191\\\
2192local args = Util.parse(...)\\\
2193local UID = 0\\\
2194local multishell = { }\\\
2195local processes = { }\\\
2196local parentTerm = term.current()\\\
2197local sessionFile = args.config or 'usr/config/mwm'\\\
2198local monName = args[1]\\\
2199local running\\\
2200local parentMon\\\
2201\\\
2202_ENV.multishell = multishell\\\
2203if monName then\\\
2204 parentMon = peripheral.wrap(monName) or syntax()\\\
2205else\\\
2206 parentMon = peripheral.find('monitor') or syntax()\\\
2207end\\\
2208\\\
2209parentMon.setTextScale(.5)\\\
2210\\\
2211local monDim, termDim = { }, { }\\\
2212monDim.width, monDim.height = parentMon.getSize()\\\
2213termDim.width, termDim.height = parentTerm.getSize()\\\
2214\\\
2215-- even though the monitor window is set to visible\\\
2216-- the canvas is not (possibly change default in terminal.lua)\\\
2217\\\
2218-- canvas is not visible so that redraws\\\
2219-- are done once in the event loop\\\
2220local monitor = Terminal.window(parentMon, 1, 1, monDim.width, monDim.height, true)\\\
2221monitor.setBackgroundColor(colors.gray)\\\
2222monitor.clear()\\\
2223\\\
2224local function nextUID()\\\
2225 UID = UID + 1\\\
2226 return UID\\\
2227end\\\
2228\\\
2229local function xprun(env, path, ...)\\\
2230 setmetatable(env, { __index = _G })\\\
2231 local fn, m = loadfile(path, env)\\\
2232 if fn then\\\
2233 return trace(fn, ...)\\\
2234 end\\\
2235 return fn, m\\\
2236end\\\
2237\\\
2238local function write(win, x, y, text)\\\
2239 win.setCursorPos(x, y)\\\
2240 win.write(text)\\\
2241end\\\
2242\\\
2243local function redraw()\\\
2244 --monitor.clear()\\\
2245 monitor.canvas:dirty()\\\
2246 --monitor.setBackgroundColor(colors.gray)\\\
2247 monitor.canvas:clear(colors.gray)\\\
2248 for k, process in ipairs(processes) do\\\
2249 process.container.canvas:dirty()\\\
2250 process:focus(k == #processes)\\\
2251 end\\\
2252end\\\
2253\\\
2254local function getProcessAt(x, y)\\\
2255 for k = #processes, 1, -1 do\\\
2256 local process = processes[k]\\\
2257 if x >= process.x and\\\
2258 y >= process.y and\\\
2259 x <= process.x + process.width - 1 and\\\
2260 y <= process.y + process.height - 1 then\\\
2261 return k, process\\\
2262 end\\\
2263 end\\\
2264end\\\
2265\\\
2266--[[ A runnable process ]]--\\\
2267local Process = { }\\\
2268\\\
2269function Process:new(env, args)\\\
2270 args.env = shell.makeEnv(env)\\\
2271 args.width = args.width or termDim.width\\\
2272 args.height = args.height or termDim.height\\\
2273\\\
2274 -- TODO: randomize start position\\\
2275 local self = setmetatable({\\\
2276 uid = nextUID(),\\\
2277 x = args.x or 1,\\\
2278 y = args.y or 1,\\\
2279 width = args.width,\\\
2280 height = args.height + 1,\\\
2281 path = args.path,\\\
2282 args = args.args or { },\\\
2283 title = args.title or 'shell',\\\
2284 }, { __index = Process })\\\
2285\\\
2286 self:adjustDimensions()\\\
2287 if not args.x then\\\
2288 self.x = math.random(1, monDim.width - self.width + 1)\\\
2289 self.y = math.random(1, monDim.height - self.height + 1)\\\
2290 end\\\
2291\\\
2292 self.container = Terminal.window(monitor, self.x, self.y, self.width, self.height, true)\\\
2293 self.window = window.create(self.container, 1, 2, args.width, args.height, true)\\\
2294 self.terminal = self.window\\\
2295\\\
2296 self.container.setBackgroundColor(colors.black)\\\
2297 self.container.clear()\\\
2298\\\
2299 self.container.canvas.parent = monitor.canvas\\\
2300 if not monitor.canvas.children then\\\
2301 monitor.canvas.children = { }\\\
2302 end\\\
2303 table.insert(monitor.canvas.children, 1, self.container.canvas)\\\
2304 self.container.canvas:setVisible(true)\\\
2305\\\
2306 --self.container.getSize = self.window.getSize\\\
2307\\\
2308 self.co = coroutine.create(function()\\\
2309 local result, err\\\
2310\\\
2311 if args.fn then\\\
2312 result, err = Util.runFunction(args.env, args.fn, table.unpack(self.args))\\\
2313 elseif args.path then\\\
2314 result, err = xprun(args.env, args.path, table.unpack(self.args))\\\
2315 end\\\
2316\\\
2317 if not result and err and err ~= 'Terminated' then\\\
2318 printError('\\\\n' .. tostring(err))\\\
2319 os.pullEventRaw('terminate')\\\
2320 end\\\
2321 multishell.removeProcess(self)\\\
2322 end)\\\
2323\\\
2324 self:focus(false)\\\
2325\\\
2326 return self\\\
2327end\\\
2328\\\
2329function Process:drawTitle(focused)\\\
2330 if self.showSizers and focused then\\\
2331 local sizers = '\\\\25 \\\\26 \\\\24 \\\\27'\\\
2332\\\
2333 self.container.setBackgroundColor(colors.yellow)\\\
2334 self.container.setTextColor(colors.black)\\\
2335\\\
2336 write(self.container, 1, 1, string.rep(' ', self.width))\\\
2337 write(self.container, 2, 1, sizers)\\\
2338\\\
2339 local str = string.format('%d x %d', self.width, self.height - 1)\\\
2340 write(self.container, 10, 1, str)\\\
2341 else\\\
2342 if focused then\\\
2343 self.container.setBackgroundColor(colors.yellow)\\\
2344 else\\\
2345 self.container.setBackgroundColor(colors.lightGray)\\\
2346 end\\\
2347 self.container.setTextColor(colors.black)\\\
2348 write(self.container, 1, 1, string.rep(' ', self.width))\\\
2349 write(self.container, 2, 1, self.title)\\\
2350 end\\\
2351 write(self.container, self.width - 1, 1, '*')\\\
2352end\\\
2353\\\
2354function Process:focus(focused)\\\
2355 self:drawTitle(focused)\\\
2356 if focused then\\\
2357 self.window.restoreCursor()\\\
2358 end\\\
2359end\\\
2360\\\
2361function Process:adjustDimensions()\\\
2362 self.width = math.min(self.width, monDim.width)\\\
2363 self.height = math.min(self.height, monDim.height)\\\
2364\\\
2365 self.x = math.max(1, self.x)\\\
2366 self.y = math.max(1, self.y)\\\
2367 self.x = math.min(self.x, monDim.width - self.width + 1)\\\
2368 self.y = math.min(self.y, monDim.height - self.height + 1)\\\
2369end\\\
2370\\\
2371function Process:reposition()\\\
2372 self:adjustDimensions()\\\
2373 self.container.reposition(self.x, self.y, self.width, self.height)\\\
2374 self.container.setBackgroundColor(colors.black)\\\
2375 self.container.clear()\\\
2376 self.window.reposition(1, 2, self.width, self.height - 1)\\\
2377 if self.window ~= self.terminal then\\\
2378 self.terminal.reposition(1, 1, self.width, self.height - 1)\\\
2379 end\\\
2380 redraw()\\\
2381end\\\
2382\\\
2383function Process:click(x, y)\\\
2384 if y == 1 then -- title bar\\\
2385 if x == self.width - 1 then\\\
2386 self:resume('terminate')\\\
2387 elseif not self.showSizers then\\\
2388 self.showSizers = not self.showSizers\\\
2389 self:drawTitle(true)\\\
2390 else\\\
2391 self:resizeClick(x, y)\\\
2392 end\\\
2393 elseif x > 1 and x < self.width then\\\
2394 if self.showSizers then\\\
2395 self.showSizers = false\\\
2396 self:drawTitle(true)\\\
2397 end\\\
2398 self:resume('mouse_click', 1, x, y - 1)\\\
2399 self:resume('mouse_up', 1, x, y - 1)\\\
2400 end\\\
2401end\\\
2402\\\
2403function Process:resizeClick(x)\\\
2404 if x == 2 then\\\
2405 self.height = self.height + 1\\\
2406 elseif x == 6 then\\\
2407 self.height = self.height - 1\\\
2408 elseif x == 4 then\\\
2409 self.width = self.width + 1\\\
2410 elseif x == 8 then\\\
2411 self.width = self.width - 1\\\
2412 else\\\
2413 return\\\
2414 end\\\
2415 self:reposition()\\\
2416 self:resume('term_resize')\\\
2417 self:drawTitle(true)\\\
2418 multishell.saveSession(sessionFile)\\\
2419end\\\
2420\\\
2421function Process:resume(event, ...)\\\
2422 if coroutine.status(self.co) == 'dead' then\\\
2423 return\\\
2424 end\\\
2425\\\
2426 if not self.filter or self.filter == event or event == \\\"terminate\\\" then\\\
2427 --term.redirect(self.terminal)\\\
2428 local previousTerm = term.redirect(self.terminal)\\\
2429\\\
2430 local previous = running\\\
2431 running = self -- stupid shell set title\\\
2432 local ok, result = coroutine.resume(self.co, event, ...)\\\
2433 running = previous\\\
2434\\\
2435 self.terminal = term.current()\\\
2436 term.redirect(previousTerm)\\\
2437\\\
2438 if ok then\\\
2439 self.filter = result\\\
2440 else\\\
2441 printError(result)\\\
2442 end\\\
2443 return ok, result\\\
2444 end\\\
2445end\\\
2446\\\
2447--[[ Install a multishell manager for the monitor ]]--\\\
2448function multishell.getFocus()\\\
2449 return processes[#processes].uid\\\
2450end\\\
2451\\\
2452function multishell.setFocus(uid)\\\
2453 local process = Util.find(processes, 'uid', uid)\\\
2454\\\
2455 if process then\\\
2456 local lastFocused = processes[#processes]\\\
2457 if lastFocused ~= process then\\\
2458\\\
2459 if lastFocused then\\\
2460 lastFocused:focus(false)\\\
2461 end\\\
2462\\\
2463 Util.removeByValue(processes, process)\\\
2464 table.insert(processes, process)\\\
2465\\\
2466 process.container.canvas:raise()\\\
2467 process:focus(true)\\\
2468 process.container.canvas:dirty()\\\
2469 end\\\
2470 return true\\\
2471 end\\\
2472 return false\\\
2473end\\\
2474\\\
2475function multishell.getTitle(uid)\\\
2476 local process = Util.find(processes, 'uid', uid)\\\
2477 if process then\\\
2478 return process.title\\\
2479 end\\\
2480end\\\
2481\\\
2482function multishell.setTitle(uid, title)\\\
2483 local process = Util.find(processes, 'uid', uid)\\\
2484 if process then\\\
2485 process.title = title or ''\\\
2486 process:focus(process == processes[#processes])\\\
2487 end\\\
2488end\\\
2489\\\
2490function multishell.getCurrent()\\\
2491 if running then\\\
2492 return running.uid\\\
2493 end\\\
2494end\\\
2495\\\
2496function multishell.getCount()\\\
2497 return #processes\\\
2498end\\\
2499\\\
2500function multishell.getTabs()\\\
2501 return processes\\\
2502end\\\
2503\\\
2504function multishell.launch(env, file, ...)\\\
2505 return multishell.openTab(env, {\\\
2506 path = file,\\\
2507 env = env,\\\
2508 title = 'shell',\\\
2509 args = { ... },\\\
2510 })\\\
2511end\\\
2512\\\
2513function multishell.openTab(env, tabInfo)\\\
2514 local process = Process:new(env, tabInfo)\\\
2515\\\
2516 table.insert(processes, 1, process)\\\
2517\\\
2518 --local previousTerm = term.current()\\\
2519 process:resume()\\\
2520 --term.redirect(previousTerm)\\\
2521\\\
2522 multishell.saveSession(sessionFile)\\\
2523\\\
2524 return process.uid\\\
2525end\\\
2526\\\
2527function multishell.removeProcess(process)\\\
2528 Util.removeByValue(processes, process)\\\
2529 process.container.canvas:removeLayer()\\\
2530\\\
2531 multishell.saveSession(sessionFile)\\\
2532 redraw()\\\
2533end\\\
2534\\\
2535function multishell.saveSession(filename)\\\
2536 local t = { }\\\
2537 for _,process in ipairs(processes) do\\\
2538 if process.path and not process.isShell then\\\
2539 table.insert(t, {\\\
2540 x = process.x,\\\
2541 y = process.y,\\\
2542 width = process.width,\\\
2543 height = process.height - 1,\\\
2544 path = process.path,\\\
2545 args = process.args,\\\
2546 })\\\
2547 end\\\
2548 end\\\
2549 Util.writeTable(filename, t)\\\
2550end\\\
2551\\\
2552function multishell.loadSession(filename)\\\
2553 local config = Util.readTable(filename)\\\
2554 if config then\\\
2555 for k = #config, 1, -1 do\\\
2556 multishell.openTab(_ENV, config[k])\\\
2557 end\\\
2558 end\\\
2559end\\\
2560\\\
2561function multishell.stop()\\\
2562 multishell._stop = true\\\
2563end\\\
2564\\\
2565function multishell.start()\\\
2566 while not multishell._stop do\\\
2567\\\
2568 local event = { os.pullEventRaw() }\\\
2569\\\
2570 if event[1] == 'terminate' then\\\
2571 local focused = processes[#processes]\\\
2572 if focused.isShell then\\\
2573 focused:resume('terminate')\\\
2574 else\\\
2575 break\\\
2576 end\\\
2577\\\
2578 elseif event[1] == 'monitor_touch' then\\\
2579 local x, y = event[3], event[4]\\\
2580\\\
2581 local key, process = getProcessAt(x, y)\\\
2582 if process then\\\
2583 if key ~= #processes then\\\
2584 multishell.setFocus(process.uid)\\\
2585 multishell.saveSession(sessionFile)\\\
2586 end\\\
2587 process:click(x - process.x + 1, y - process.y + 1)\\\
2588\\\
2589 else\\\
2590 process = processes[#processes]\\\
2591 if process and process.showSizers then\\\
2592 process.x = math.floor(x - (process.width) / 2)\\\
2593 process.y = y\\\
2594 process:reposition()\\\
2595 process:drawTitle(true)\\\
2596 multishell.saveSession(sessionFile)\\\
2597 end\\\
2598 end\\\
2599\\\
2600 elseif event[1] == 'mouse_click' or\\\
2601 event[1] == 'mouse_up' then\\\
2602\\\
2603 local focused = processes[#processes]\\\
2604 if not focused.isShell then\\\
2605 multishell.setFocus(1) -- shell is always 1\\\
2606 else\\\
2607 focused:resume(table.unpack(event))\\\
2608 end\\\
2609\\\
2610 elseif event[1] == 'char' or\\\
2611 event[1] == 'key' or\\\
2612 event[1] == 'key_up' or\\\
2613 event[1] == 'paste' then\\\
2614\\\
2615 local focused = processes[#processes]\\\
2616 if focused then\\\
2617 focused:resume(table.unpack(event))\\\
2618 end\\\
2619\\\
2620 else\\\
2621 for _,process in pairs(Util.shallowCopy(processes)) do\\\
2622 process:resume(table.unpack(event))\\\
2623 end\\\
2624 end\\\
2625\\\
2626 monitor.canvas:render(parentMon)\\\
2627\\\
2628 local focused = processes[#processes]\\\
2629 if focused then\\\
2630 focused.window.restoreCursor()\\\
2631 end\\\
2632 end\\\
2633end\\\
2634\\\
2635--[[ Special shell process for launching programs ]]--\\\
2636local function addShell()\\\
2637\\\
2638 local process = setmetatable({\\\
2639 x = monDim.width,\\\
2640 y = monDim.height,\\\
2641 width = 1,\\\
2642 height = 1,\\\
2643 isShell = true,\\\
2644 uid = nextUID(),\\\
2645 title = 'Terminal',\\\
2646 }, { __index = Process })\\\
2647\\\
2648 function process:focus(focused)\\\
2649 self.window.setVisible(focused)\\\
2650 if focused then\\\
2651 self.window.restoreCursor()\\\
2652 else\\\
2653 parentTerm.clear()\\\
2654 parentTerm.setCursorBlink(false)\\\
2655 local str = 'Click screen for shell'\\\
2656 write(parentTerm,\\\
2657 math.floor((termDim.width - #str) / 2),\\\
2658 math.floor(termDim.height / 2),\\\
2659 str)\\\
2660 end\\\
2661 end\\\
2662\\\
2663 function process:click()\\\
2664 end\\\
2665\\\
2666 process.container = Terminal.window(monitor, process.x, process.y+1, process.width, process.height, true)\\\
2667 process.window = window.create(parentTerm, 1, 1, termDim.width, termDim.height, true)\\\
2668 process.terminal = process.window\\\
2669\\\
2670 process.co = coroutine.create(function()\\\
2671 print('To run a program on the monitor, type \\\"fg <program>\\\"')\\\
2672 print('To quit, type \\\"exit\\\"')\\\
2673 os.run(shell.makeEnv(_ENV), Alt.get('shell'))\\\
2674 multishell.stop()\\\
2675 end)\\\
2676\\\
2677 table.insert(processes, process)\\\
2678 process:focus(true)\\\
2679\\\
2680 local previousTerm = term.current()\\\
2681 process:resume()\\\
2682 term.redirect(previousTerm)\\\
2683end\\\
2684\\\
2685addShell()\\\
2686\\\
2687multishell.loadSession(sessionFile)\\\
2688multishell.start()\\\
2689\\\
2690term.redirect(parentTerm)\\\
2691parentTerm.clear()\\\
2692parentTerm.setCursorPos(1, 1)\",\
2693 [ \"monitor/mirrorHost.lua\" ] = \"local Event = require('opus.event')\\\
2694local Socket = require('opus.socket')\\\
2695\\\
2696local colors = _G.colors\\\
2697local term = _G.term\\\
2698\\\
2699local mon = term.current()\\\
2700local args = { ... }\\\
2701if args[1] then\\\
2702 mon = _G.device[args[1]]\\\
2703end\\\
2704\\\
2705if not mon then\\\
2706 error('Invalid monitor')\\\
2707end\\\
2708\\\
2709mon.setBackgroundColor(colors.black)\\\
2710mon.clear()\\\
2711\\\
2712while true do\\\
2713 local socket = Socket.server(5902)\\\
2714\\\
2715 print('mirror: connection from ' .. socket.dhost)\\\
2716\\\
2717 Event.addRoutine(function()\\\
2718 while true do\\\
2719 local data = socket:read()\\\
2720 if not data then\\\
2721 break\\\
2722 end\\\
2723 for _,v in ipairs(data) do\\\
2724 mon[v.f](unpack(v.args))\\\
2725 end\\\
2726 end\\\
2727 end)\\\
2728\\\
2729 while true do\\\
2730 Event.pullEvent()\\\
2731 if not socket.connected then\\\
2732 break\\\
2733 end\\\
2734 end\\\
2735\\\
2736 print('connection lost')\\\
2737\\\
2738 socket:close()\\\
2739end\",\
2740 [ \"monitor/mirrorClient.lua\" ] = \"local Event = require('opus.event')\\\
2741local Socket = require('opus.socket')\\\
2742local Util = require('opus.util')\\\
2743\\\
2744local os = _G.os\\\
2745\\\
2746local remoteId\\\
2747local args = { ... }\\\
2748if #args == 1 then\\\
2749 remoteId = tonumber(args[1])\\\
2750else\\\
2751 print('Enter host ID')\\\
2752 remoteId = tonumber(_G.read())\\\
2753end\\\
2754\\\
2755if not remoteId then\\\
2756 error('Syntax: mirrorClient <host ID>')\\\
2757end\\\
2758\\\
2759local function wrapTerm(socket)\\\
2760 local methods = { 'blit', 'clear', 'clearLine', 'setCursorPos', 'write',\\\
2761 'setTextColor', 'setTextColour', 'setBackgroundColor',\\\
2762 'setBackgroundColour', 'scroll', 'setCursorBlink', }\\\
2763\\\
2764 socket.term = _G.device.terminal\\\
2765 socket.oldTerm = Util.shallowCopy(socket.term)\\\
2766\\\
2767 for _,k in pairs(methods) do\\\
2768 socket.term[k] = function(...)\\\
2769 if not socket.queue then\\\
2770 socket.queue = { }\\\
2771 Event.onTimeout(0, function()\\\
2772 if socket.queue then\\\
2773 socket:write(socket.queue)\\\
2774 socket.queue = nil\\\
2775 end\\\
2776 end)\\\
2777 end\\\
2778 table.insert(socket.queue, {\\\
2779 f = k,\\\
2780 args = { ... },\\\
2781 })\\\
2782 socket.oldTerm[k](...)\\\
2783 end\\\
2784 end\\\
2785end\\\
2786\\\
2787while true do\\\
2788 print('connecting...')\\\
2789 local socket\\\
2790\\\
2791 while true do\\\
2792 socket = Socket.connect(remoteId, 5902)\\\
2793 if socket then\\\
2794 break\\\
2795 end\\\
2796 os.sleep(3)\\\
2797 end\\\
2798\\\
2799 print('connected')\\\
2800\\\
2801 wrapTerm(socket)\\\
2802\\\
2803 os.queueEvent('term_resize')\\\
2804\\\
2805 while true do\\\
2806 local e = Event.pullEvent()\\\
2807 if e[1] == 'terminate' then\\\
2808 break\\\
2809 end\\\
2810 if not socket.connected then\\\
2811 break\\\
2812 end\\\
2813 end\\\
2814\\\
2815 for k,v in pairs(socket.oldTerm) do\\\
2816 socket.term[k] = v\\\
2817 end\\\
2818\\\
2819 socket:close()\\\
2820\\\
2821end\",\
2822 [ \"core/apis/meAdapter18.lua\" ] = \"local class = require('opus.class')\\\
2823local RSAdapter = require('core.refinedAdapter')\\\
2824local Peripheral = require('opus.peripheral')\\\
2825local Util = require('opus.util')\\\
2826\\\
2827local MEAdapter = class(RSAdapter)\\\
2828\\\
2829local DEVICE_TYPE = 'appliedenergistics2:interface'\\\
2830\\\
2831function MEAdapter:init(args)\\\
2832 local defaults = {\\\
2833 name = 'appliedEnergistics',\\\
2834 jobList = { },\\\
2835 }\\\
2836 Util.merge(self, defaults)\\\
2837 Util.merge(self, args)\\\
2838\\\
2839 local controller\\\
2840 if not self.side then\\\
2841 controller = Peripheral.getByType(DEVICE_TYPE)\\\
2842 else\\\
2843 controller = Peripheral.getBySide(self.side)\\\
2844 end\\\
2845\\\
2846 if controller then\\\
2847 Util.merge(self, controller)\\\
2848 end\\\
2849end\\\
2850\\\
2851function MEAdapter:isValid()\\\
2852 return self.type == DEVICE_TYPE and not not self.findItems\\\
2853end\\\
2854\\\
2855function MEAdapter:clearFinished()\\\
2856 for _,key in pairs(Util.keys(self.jobList)) do\\\
2857 local job = self.jobList[key]\\\
2858 if job.info.status() == 'finished' then\\\
2859 self.jobList[key] = nil\\\
2860 end\\\
2861 end\\\
2862end\\\
2863\\\
2864function MEAdapter:isCPUAvailable()\\\
2865 local cpus = self.getCraftingCPUs() or { }\\\
2866 local busy = 0\\\
2867\\\
2868 for _,cpu in pairs(cpus) do\\\
2869 if cpu.busy then\\\
2870 busy = busy + 1\\\
2871 end\\\
2872 end\\\
2873 self:clearFinished()\\\
2874 return busy == Util.size(self.jobList) and busy < #cpus\\\
2875end\\\
2876\\\
2877function MEAdapter:craft(item, count)\\\
2878 if not self:isCPUAvailable() then\\\
2879 return false\\\
2880 end\\\
2881\\\
2882 local detail = self.findItem(item)\\\
2883 if detail and detail.craft then\\\
2884 local info = detail.craft(count or 1)\\\
2885 if info.status() == 'unknown' then\\\
2886 self.jobList[info.getId()] = {\\\
2887 name = item.name,\\\
2888 damage = item.damage,\\\
2889 nbtHash = item.nbtHash,\\\
2890 info = info,\\\
2891 }\\\
2892 return true\\\
2893 end\\\
2894 return false\\\
2895 end\\\
2896end\\\
2897\\\
2898function MEAdapter:isCrafting(item)\\\
2899 self:clearFinished()\\\
2900\\\
2901 for _,job in pairs(self.jobList) do\\\
2902 if job.name == item.name and\\\
2903 job.damage == item.damage and\\\
2904 job.nbtHash == item.nbtHash then\\\
2905 return true\\\
2906 end\\\
2907 end\\\
2908 return false\\\
2909end\\\
2910\\\
2911return MEAdapter\",\
2912 [ \"games/.package\" ] = \"{\\\
2913 title = 'Games',\\\
2914 repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/games',\\\
2915 description = [[Games by various people]],\\\
2916 license = 'MIT',\\\
2917}\",\
2918 [ \"games/etc/fstab\" ] = \"packages/games/Othello.lua urlfs http://pastebin.com/raw/rMeh1Kag\\\
2919packages/games/Pipes.lua urlfs https://pastebin.com/raw/skcs9x1s\\\
2920packages/games/Strafe.lua urlfs https://pastebin.com/raw/jyDH7mLH\\\
2921packages/games/Breakout.lua urlfs https://gist.github.com/LDDestroier/c7528d95bc0103545c2a/raw\\\
2922packages/games/Minesweeper.lua urlfs https://pastebin.com/raw/nsKrHTbN\\\
2923packages/games/Tron.lua urlfs https://raw.githubusercontent.com/LDDestroier/CC/master/tron.lua\",\
2924 [ \"common/Follow.lua\" ] = \"local Event = require('opus.event')\\\
2925local GPS = require('opus.gps')\\\
2926local Point = require('opus.point')\\\
2927local Socket = require('opus.socket')\\\
2928local Swarm = require('core.swarm')\\\
2929local UI = require('opus.ui')\\\
2930local Util = require('opus.util')\\\
2931\\\
2932local colors = _G.colors\\\
2933local network = _G.network\\\
2934local os = _G.os\\\
2935\\\
2936local swarm = Swarm()\\\
2937local gpt = GPS.getPoint() or error('GPS not found')\\\
2938local pts, blocks\\\
2939\\\
2940local page = UI.Page {\\\
2941 menuBar = UI.MenuBar {\\\
2942 buttons = {\\\
2943 { text = 'Range', event = 'range' },\\\
2944 { text = 'Stop', event = 'stop' },\\\
2945 },\\\
2946 mode = UI.Chooser {\\\
2947 x = -16,\\\
2948 choices = {\\\
2949 { name = 'No breaking', value = 'digNone' },\\\
2950 { name = 'Destructive', value = 'turtleSafe' },\\\
2951 },\\\
2952 value = 'digNone',\\\
2953 },\\\
2954 },\\\
2955 grid = UI.ScrollingGrid {\\\
2956 y = 2, ey = -2,\\\
2957 columns = {\\\
2958 { heading = 'Label', key = 'label' },\\\
2959 { heading = 'Dist', key = 'distance' },\\\
2960 { heading = 'Status', key = 'status' },\\\
2961 { heading = 'Fuel', key = 'fuel' },\\\
2962 },\\\
2963 sortColumn = 'distance',\\\
2964 autospace = true,\\\
2965 },\\\
2966 range = UI.SlideOut {\\\
2967 y = -7, height = 7,\\\
2968 titleBar = UI.TitleBar {\\\
2969 event = 'cancel',\\\
2970 title = 'Enter range',\\\
2971 },\\\
2972 notice = UI.TextArea {\\\
2973 x = 2, ex = -2, y = 3, ey = 4,\\\
2974 value =\\\
2975[[Select all turtles within a specified range]],\\\
2976 },\\\
2977 entry = UI.TextEntry {\\\
2978 y = 6, x = 2, ex = 10,\\\
2979 limit = 4,\\\
2980 shadowText = 'range',\\\
2981 accelerators = {\\\
2982 enter = 'select_range',\\\
2983 },\\\
2984 },\\\
2985 button = UI.Button {\\\
2986 x = 12, y = 6,\\\
2987 text = 'Apply',\\\
2988 event = 'select_range',\\\
2989 }\\\
2990 },\\\
2991}\\\
2992\\\
2993function page.grid:getRowTextColor(row, selected)\\\
2994 if swarm.pool[row.id] then\\\
2995 return colors.yellow\\\
2996 end\\\
2997 return UI.ScrollingGrid.getRowTextColor(self, row, selected)\\\
2998end\\\
2999\\\
3000function page.grid:getDisplayValues(row)\\\
3001 row = Util.shallowCopy(row)\\\
3002 if row.fuel then\\\
3003 row.fuel = row.fuel > 0 and Util.toBytes(row.fuel) or ''\\\
3004 end\\\
3005 if row.distance then\\\
3006 row.distance = Util.round(row.distance, 1)\\\
3007 end\\\
3008 return row\\\
3009end\\\
3010\\\
3011function page:enable()\\\
3012 local function update()\\\
3013 local t = { }\\\
3014 for _,v in pairs(network) do\\\
3015 if v.fuel and v.active and v.fuel > 0 and v.distance then\\\
3016 table.insert(t, v)\\\
3017 end\\\
3018 end\\\
3019 self.grid:setValues(t)\\\
3020 end\\\
3021\\\
3022 Event.onInterval(3, function()\\\
3023 update()\\\
3024 self.grid:draw()\\\
3025 self:sync()\\\
3026 end)\\\
3027\\\
3028 update()\\\
3029\\\
3030 UI.Page.enable(self)\\\
3031end\\\
3032\\\
3033local function follow(member)\\\
3034 local turtle = member.turtle\\\
3035 turtle.reset()\\\
3036 turtle.set({\\\
3037 digPolicy = page.menuBar.mode.value,\\\
3038 status = 'Following',\\\
3039 })\\\
3040\\\
3041 if not turtle.enableGPS(nil, true) then\\\
3042 error('turtle: No GPS found')\\\
3043 end\\\
3044\\\
3045 member.snmp = Socket.connect(member.id, 161)\\\
3046 member.snmp.co = coroutine.running()\\\
3047\\\
3048 local pt\\\
3049\\\
3050 while true do\\\
3051 while pt and Point.same(gpt, pt) do\\\
3052 os.sleep(.5)\\\
3053 end\\\
3054 pt = Point.copy(gpt)\\\
3055\\\
3056 local cpt = Point.closest(turtle.getPoint(), pts)\\\
3057\\\
3058 turtle.abort(false)\\\
3059 if turtle.pathfind(cpt, { blocks = blocks }) then\\\
3060 turtle.headTowards(pt)\\\
3061 end\\\
3062 end\\\
3063end\\\
3064\\\
3065function swarm:onRemove(member, status, message)\\\
3066 if member.socket then\\\
3067 pcall(function()\\\
3068 member.turtle.set({ status = 'idle' })\\\
3069 member.turtle.abort(true)\\\
3070 end)\\\
3071 end\\\
3072 if member.snmp then\\\
3073 member.snmp:close()\\\
3074 member.snmp = nil\\\
3075 end\\\
3076 if not status then\\\
3077 _G._syslog(message)\\\
3078 end\\\
3079end\\\
3080\\\
3081function page:eventHandler(event)\\\
3082 if event.type == 'grid_select' then\\\
3083 if not swarm.pool[event.selected.id] then\\\
3084 swarm:add(event.selected.id)\\\
3085 swarm:run(follow)\\\
3086 else\\\
3087 swarm:remove(event.selected.id)\\\
3088 end\\\
3089 self.grid:draw()\\\
3090\\\
3091 elseif event.type == 'choice_change' then\\\
3092 local script = string.format('turtle.set({ digPolicy = \\\"%s\\\"})', event.value)\\\
3093 for _, member in pairs(swarm.pool) do\\\
3094 member.snmp:write({ type = 'scriptEx', args = script })\\\
3095 end\\\
3096\\\
3097 elseif event.type == 'stop' then\\\
3098 for id in pairs(swarm.pool) do\\\
3099 swarm:remove(id)\\\
3100 end\\\
3101\\\
3102 elseif event.type == 'range' then\\\
3103 self.range:show()\\\
3104\\\
3105 elseif event.type == 'cancel' then\\\
3106 self.range:hide()\\\
3107\\\
3108 elseif event.type == 'select_range' then\\\
3109 local range = tonumber(self.range.entry.value)\\\
3110\\\
3111 if range and range > 0 then\\\
3112 for id, v in pairs(network) do\\\
3113 if not swarm.pool[id] then\\\
3114 if v.fuel and v.active and v.fuel > 0 and v.distance and v.distance <= range then\\\
3115 swarm:add(id)\\\
3116 end\\\
3117 end\\\
3118 end\\\
3119 swarm:run(follow)\\\
3120 self.range:hide()\\\
3121 end\\\
3122 else\\\
3123 return UI.Page.eventHandler(self, event)\\\
3124 end\\\
3125 return true\\\
3126end\\\
3127\\\
3128Event.addRoutine(function()\\\
3129 while true do\\\
3130 local pt = GPS.getPoint()\\\
3131 if not pts or (pt and not Point.same(pt, gpt)) then\\\
3132 gpt = pt\\\
3133 pts = {\\\
3134 { x = pt.x + 2, z = pt.z, y = pt.y },\\\
3135 { x = pt.x - 2, z = pt.z, y = pt.y },\\\
3136 { x = pt.x, z = pt.z + 2, y = pt.y },\\\
3137 { x = pt.x, z = pt.z - 2, y = pt.y },\\\
3138 }\\\
3139 blocks = { }\\\
3140\\\
3141 local function addBlocks(tpt)\\\
3142 table.insert(blocks, tpt)\\\
3143 local apts = Point.adjacentPoints(tpt)\\\
3144 for _,apt in pairs(apts) do\\\
3145 table.insert(blocks, apt)\\\
3146 end\\\
3147 end\\\
3148\\\
3149 -- don't run into player\\\
3150 addBlocks(pt)\\\
3151 addBlocks(Point.above(pt))\\\
3152\\\
3153 for _, member in pairs(swarm.pool) do\\\
3154 if member.snmp then\\\
3155 member.snmp:write({ type = 'scriptEx', args = 'turtle.abort(true)' })\\\
3156 end\\\
3157 end\\\
3158 end\\\
3159 os.sleep(1)\\\
3160 end\\\
3161end)\\\
3162\\\
3163UI:setPage(page)\\\
3164UI:start()\\\
3165\\\
3166swarm:stop()\",\
3167 [ \"core/etc/names/minecraft.json\" ] = \"{\\\
3168 \\\"air\\\": {\\\
3169 \\\"id\\\": 0,\\\
3170 \\\"name\\\": \\\"Air\\\",\\\
3171 }, \\\
3172 \\\"stone\\\": {\\\
3173 \\\"id\\\": 1, \\\
3174 \\\"name\\\": [\\\"Stone\\\",\\\
3175 \\\"Granite\\\",\\\
3176 \\\"Polished Granite\\\",\\\
3177 \\\"Diorite\\\",\\\
3178 \\\"Polished Diorite\\\",\\\
3179 \\\"Andesite\\\",\\\
3180 \\\"Polished Andesite\\\"],\\\
3181 }, \\\
3182 \\\"grass\\\": {\\\
3183 \\\"id\\\": 2, \\\
3184 \\\"name\\\": \\\"Grass Block\\\", \\\
3185 }, \\\
3186 \\\"dirt\\\": {\\\
3187 \\\"id\\\": 3, \\\
3188 \\\"name\\\": [\\\"Dirt\\\",\\\
3189 \\\"Coarse Dirt\\\",\\\
3190 \\\"Podzol\\\"], \\\
3191 }, \\\
3192 \\\"cobblestone\\\": {\\\
3193 \\\"id\\\": 4, \\\
3194 \\\"name\\\": \\\"Cobblestone\\\", \\\
3195 }, \\\
3196 \\\"planks\\\": {\\\
3197 \\\"id\\\": 5, \\\
3198 \\\"name\\\": [\\\"Oak Wood Planks\\\",\\\
3199 \\\"Spruce Wood Planks\\\",\\\
3200 \\\"Birch Wood Planks\\\",\\\
3201 \\\"Jungle Wood Planks\\\",\\\
3202 \\\"Acacia Wood Planks\\\", \\\
3203 \\\"Dark Oak Wood Planks\\\"],\\\
3204 }, \\\
3205 \\\"sapling\\\": {\\\
3206 \\\"id\\\": 6, \\\
3207 \\\"name\\\": [\\\"Oak Sapling\\\",\\\
3208 \\\"Spruce Sapling\\\",\\\
3209 \\\"Birch Sapling\\\",\\\
3210 \\\"Jungle Sapling\\\",\\\
3211 \\\"Acacia Sapling\\\", \\\
3212 \\\"Dark Oak Sapling\\\"],\\\
3213 \\\"place\\\": \\\"sapling\\\",\\\
3214 },\\\
3215 \\\"bedrock\\\": {\\\
3216 \\\"id\\\": 7, \\\
3217 \\\"name\\\": \\\"Bedrock\\\", \\\
3218 }, \\\
3219 \\\"flowing_water\\\": {\\\
3220 \\\"id\\\": 8, \\\
3221 \\\"name\\\": \\\"Water\\\",\\\
3222 \\\"place\\\": \\\"flatten\\\",\\\
3223 }, \\\
3224 \\\"water\\\": {\\\
3225 \\\"id\\\": 9, \\\
3226 \\\"name\\\": \\\"Water\\\", \\\
3227 \\\"place\\\": \\\"flatten\\\",\\\
3228 }, \\\
3229 \\\"flowing_lava\\\": {\\\
3230 \\\"id\\\": 10, \\\
3231 \\\"name\\\": \\\"Lava\\\", \\\
3232 }, \\\
3233 \\\"lava\\\": {\\\
3234 \\\"id\\\": 11, \\\
3235 \\\"name\\\": \\\"Lava\\\", \\\
3236 }, \\\
3237 \\\"sand\\\": {\\\
3238 \\\"id\\\": 12, \\\
3239 \\\"name\\\": [\\\"Sand\\\",\\\
3240 \\\"Red Sand\\\"], \\\
3241 }, \\\
3242 \\\"gravel\\\": {\\\
3243 \\\"id\\\": 13, \\\
3244 \\\"name\\\": \\\"Gravel\\\", \\\
3245 }, \\\
3246 \\\"gold_ore\\\": {\\\
3247 \\\"id\\\": 14, \\\
3248 \\\"name\\\": \\\"Gold Ore\\\", \\\
3249 }, \\\
3250 \\\"iron_ore\\\": {\\\
3251 \\\"id\\\": 15, \\\
3252 \\\"name\\\": \\\"Iron Ore\\\", \\\
3253 }, \\\
3254 \\\"coal_ore\\\": {\\\
3255 \\\"id\\\": 16, \\\
3256 \\\"name\\\": \\\"Coal Ore\\\", \\\
3257 }, \\\
3258 \\\"log\\\": {\\\
3259 \\\"id\\\": 17, \\\
3260 \\\"name\\\": [\\\"Oak Wood\\\",\\\
3261 \\\"Spruce Wood\\\",\\\
3262 \\\"Birch Wood\\\",\\\
3263 \\\"Jungle Wood\\\"], \\\
3264 \\\"place\\\": \\\"wood\\\",\\\
3265 }, \\\
3266 \\\"leaves\\\": {\\\
3267 \\\"id\\\": 18, \\\
3268 \\\"name\\\": [\\\"Oak Leaves\\\",\\\
3269 \\\"Spruce Leaves\\\",\\\
3270 \\\"Birch Leaves\\\",\\\
3271 \\\"Jungle Leaves\\\"], \\\
3272 \\\"place\\\": \\\"leaves\\\",\\\
3273 }, \\\
3274 \\\"sponge\\\": {\\\
3275 \\\"id\\\": 19, \\\
3276 \\\"name\\\": [\\\"Sponge\\\",\\\
3277 \\\"Wet Sponge\\\"], \\\
3278 }, \\\
3279 \\\"glass\\\": {\\\
3280 \\\"id\\\": 20, \\\
3281 \\\"name\\\": \\\"Glass\\\", \\\
3282 }, \\\
3283 \\\"lapis_ore\\\": {\\\
3284 \\\"id\\\": 21, \\\
3285 \\\"name\\\": \\\"Lapis Lazuli Ore\\\", \\\
3286 }, \\\
3287 \\\"lapis_block\\\": {\\\
3288 \\\"id\\\": 22, \\\
3289 \\\"name\\\": \\\"Lapis Lazuli Block\\\", \\\
3290 }, \\\
3291 \\\"dispenser\\\": {\\\
3292 \\\"id\\\": 23, \\\
3293 \\\"name\\\": \\\"Dispenser\\\", \\\
3294 \\\"place\\\": \\\"dispenser\\\",\\\
3295 }, \\\
3296 \\\"sandstone\\\": {\\\
3297 \\\"id\\\": 24, \\\
3298 \\\"name\\\": [\\\"Sandstone\\\",\\\
3299 \\\"Chiseled Sandstone\\\",\\\
3300 \\\"Smooth Sandstone\\\"],\\\
3301 }, \\\
3302 \\\"noteblock\\\": {\\\
3303 \\\"id\\\": 25, \\\
3304 \\\"name\\\": \\\"Note Block\\\", \\\
3305 }, \\\
3306 \\\"bed\\\": {\\\
3307 \\\"id\\\": 26, \\\
3308 \\\"name\\\": \\\"Bed\\\", \\\
3309 \\\"place\\\": \\\"bed\\\",\\\
3310 }, \\\
3311 \\\"golden_rail\\\": {\\\
3312 \\\"id\\\": 27, \\\
3313 \\\"name\\\": \\\"Powered Rail\\\", \\\
3314 \\\"place\\\": \\\"adp-rail\\\",\\\
3315 }, \\\
3316 \\\"detector_rail\\\": {\\\
3317 \\\"id\\\": 28, \\\
3318 \\\"name\\\": \\\"Detector Rail\\\", \\\
3319 \\\"place\\\": \\\"adp-rail\\\",\\\
3320 }, \\\
3321 \\\"sticky_piston\\\": {\\\
3322 \\\"id\\\": 29, \\\
3323 \\\"name\\\": \\\"Sticky Piston\\\", \\\
3324 \\\"place\\\": \\\"piston\\\",\\\
3325 }, \\\
3326 \\\"web\\\": {\\\
3327 \\\"id\\\": 30, \\\
3328 \\\"name\\\": \\\"Cobweb\\\", \\\
3329 }, \\\
3330 \\\"tallgrass\\\": {\\\
3331 \\\"id\\\": 31, \\\
3332 \\\"name\\\": [\\\"Shrub\\\",\\\
3333 \\\"Grass\\\",\\\
3334 \\\"Fern\\\"],\\\
3335 }, \\\
3336 \\\"deadbush\\\": {\\\
3337 \\\"id\\\": 32, \\\
3338 \\\"name\\\": \\\"Dead Bush\\\", \\\
3339 }, \\\
3340 \\\"piston\\\": {\\\
3341 \\\"id\\\": 33, \\\
3342 \\\"name\\\": \\\"Piston\\\", \\\
3343 \\\"place\\\": \\\"piston\\\",\\\
3344 }, \\\
3345 \\\"piston_head\\\": {\\\
3346 \\\"id\\\": 34, \\\
3347 \\\"name\\\": \\\"Piston Extension\\\", \\\
3348 \\\"place\\\": \\\"flatten\\\",\\\
3349 }, \\\
3350 \\\"wool\\\": {\\\
3351 \\\"id\\\": 35, \\\
3352 \\\"name\\\": [\\\"White Wool\\\", \\\
3353 \\\"Orange Wool\\\",\\\
3354 \\\"Magenta Wool\\\",\\\
3355 \\\"Light Blue Wool\\\",\\\
3356 \\\"Yellow Wool\\\",\\\
3357 \\\"Lime Wool\\\",\\\
3358 \\\"Pink Wool\\\",\\\
3359 \\\"Gray Wool\\\",\\\
3360 \\\"Light Gray Wool\\\",\\\
3361 \\\"Cyan Wool\\\",\\\
3362 \\\"Purple Wool\\\",\\\
3363 \\\"Blue Wool\\\",\\\
3364 \\\"Brown Wool\\\",\\\
3365 \\\"Green Wool\\\",\\\
3366 \\\"Red Wool\\\",\\\
3367 \\\"Black Wool\\\"],\\\
3368 }, \\\
3369 \\\"piston_extension\\\": {\\\
3370 \\\"id\\\": 36, \\\
3371 \\\"name\\\": \\\"Block moved by Piston\\\", \\\
3372 \\\"place\\\": \\\"flatten\\\",\\\
3373 }, \\\
3374 \\\"yellow_flower\\\": {\\\
3375 \\\"id\\\": 37, \\\
3376 \\\"name\\\": \\\"Dandelion\\\", \\\
3377 }, \\\
3378 \\\"red_flower\\\": {\\\
3379 \\\"id\\\": 38, \\\
3380 \\\"name\\\": [\\\"Poppy\\\",\\\
3381 \\\"Blue Orchid\\\",\\\
3382 \\\"Allium\\\",\\\
3383 \\\"Azure Bluet\\\",\\\
3384 \\\"Red Tulip\\\",\\\
3385 \\\"Orange Tulip\\\",\\\
3386 \\\"White Tulip\\\",\\\
3387 \\\"Pink Tulip\\\",\\\
3388 \\\"Oxeye Daisy\\\"],\\\
3389 }, \\\
3390 \\\"brown_mushroom\\\": {\\\
3391 \\\"id\\\": 39, \\\
3392 \\\"name\\\": \\\"Brown Mushroom\\\", \\\
3393 }, \\\
3394 \\\"red_mushroom\\\": {\\\
3395 \\\"id\\\": 40, \\\
3396 \\\"name\\\": \\\"Red Mushroom\\\", \\\
3397 }, \\\
3398 \\\"gold_block\\\": {\\\
3399 \\\"id\\\": 41, \\\
3400 \\\"name\\\": \\\"Block of Gold\\\", \\\
3401 }, \\\
3402 \\\"iron_block\\\": {\\\
3403 \\\"id\\\": 42, \\\
3404 \\\"name\\\": \\\"Block of Iron\\\", \\\
3405 }, \\\
3406 \\\"double_stone_slab\\\": {\\\
3407 \\\"id\\\": 43, \\\
3408 \\\"name\\\": \\\"Double Stone Slab\\\", \\\
3409 }, \\\
3410 \\\"stone_slab\\\": {\\\
3411 \\\"id\\\": 44, \\\
3412 \\\"name\\\": [\\\"Stone Slab\\\",\\\
3413 \\\"Sandstone Slab\\\",\\\
3414 \\\"Wooden Slab\\\",\\\
3415 \\\"Cobblestone Slab\\\",\\\
3416 \\\"Bricks Slab\\\",\\\
3417 \\\"Stone Bricks Slab\\\",\\\
3418 \\\"Nether Brick Slab\\\",\\\
3419 \\\"Quartz Slab\\\"],\\\
3420 \\\"place\\\": \\\"slab\\\",\\\
3421 }, \\\
3422 \\\"brick_block\\\": {\\\
3423 \\\"id\\\": 45, \\\
3424 \\\"name\\\": \\\"Bricks\\\", \\\
3425 }, \\\
3426 \\\"tnt\\\": {\\\
3427 \\\"id\\\": 46, \\\
3428 \\\"name\\\": \\\"TNT\\\", \\\
3429 }, \\\
3430 \\\"bookshelf\\\": {\\\
3431 \\\"id\\\": 47, \\\
3432 \\\"name\\\": \\\"Bookshelf\\\", \\\
3433 }, \\\
3434 \\\"mossy_cobblestone\\\": {\\\
3435 \\\"id\\\": 48, \\\
3436 \\\"name\\\": \\\"Moss Stone\\\", \\\
3437 }, \\\
3438 \\\"obsidian\\\": {\\\
3439 \\\"id\\\": 49, \\\
3440 \\\"name\\\": \\\"Obsidian\\\", \\\
3441 }, \\\
3442 \\\"torch\\\": {\\\
3443 \\\"id\\\": 50, \\\
3444 \\\"name\\\": \\\"Torch\\\", \\\
3445 \\\"place\\\": \\\"torch\\\",\\\
3446 }, \\\
3447 \\\"fire\\\": {\\\
3448 \\\"id\\\": 51, \\\
3449 \\\"name\\\": \\\"Fire\\\", \\\
3450 \\\"place\\\": \\\"flatten\\\",\\\
3451 }, \\\
3452 \\\"mob_spawner\\\": {\\\
3453 \\\"id\\\": 52, \\\
3454 \\\"name\\\": \\\"Monster Spawner\\\", \\\
3455 }, \\\
3456 \\\"oak_stairs\\\": {\\\
3457 \\\"id\\\": 53, \\\
3458 \\\"name\\\": \\\"Oak Wood Stairs\\\", \\\
3459 \\\"place\\\": \\\"stairs\\\",\\\
3460 }, \\\
3461 \\\"chest\\\": {\\\
3462 \\\"id\\\": 54, \\\
3463 \\\"name\\\": \\\"Chest\\\", \\\
3464 \\\"place\\\": \\\"chest-furnace\\\",\\\
3465 }, \\\
3466 \\\"redstone_wire\\\": {\\\
3467 \\\"id\\\": 55, \\\
3468 \\\"name\\\": \\\"Redstone Wire\\\", \\\
3469 \\\"place\\\": \\\"flatten\\\",\\\
3470 }, \\\
3471 \\\"diamond_ore\\\": {\\\
3472 \\\"id\\\": 56, \\\
3473 \\\"name\\\": \\\"Diamond Ore\\\", \\\
3474 }, \\\
3475 \\\"diamond_block\\\": {\\\
3476 \\\"id\\\": 57, \\\
3477 \\\"name\\\": \\\"Block of Diamond\\\", \\\
3478 }, \\\
3479 \\\"crafting_table\\\": {\\\
3480 \\\"id\\\": 58, \\\
3481 \\\"name\\\": \\\"Crafting Table\\\", \\\
3482 },\\\
3483 \\\"wheat\\\": {\\\
3484 \\\"id\\\": 59, \\\
3485 \\\"name\\\": \\\"Wheat\\\", \\\
3486 \\\"place\\\": \\\"flatten\\\",\\\
3487 }, \\\
3488 \\\"farmland\\\": {\\\
3489 \\\"id\\\": 60, \\\
3490 \\\"name\\\": \\\"Farmland\\\", \\\
3491 \\\"place\\\": \\\"flatten\\\",\\\
3492 }, \\\
3493 \\\"furnace\\\": {\\\
3494 \\\"id\\\": 61, \\\
3495 \\\"name\\\": \\\"Furnace\\\", \\\
3496 \\\"place\\\": \\\"chest-furnace\\\",\\\
3497 }, \\\
3498 \\\"lit_furnace\\\": {\\\
3499 \\\"id\\\": 62, \\\
3500 \\\"name\\\": \\\"Burning Furnace\\\", \\\
3501 \\\"place\\\": \\\"chest-furnace\\\",\\\
3502 }, \\\
3503 \\\"standing_sign\\\": {\\\
3504 \\\"id\\\": 63, \\\
3505 \\\"name\\\": \\\"Sign\\\", \\\
3506 \\\"place\\\": \\\"signpost\\\",\\\
3507 }, \\\
3508 \\\"wooden_door\\\": {\\\
3509 \\\"id\\\": 64, \\\
3510 \\\"name\\\": \\\"Oak Door\\\", \\\
3511 \\\"place\\\": \\\"door\\\",\\\
3512 }, \\\
3513 \\\"ladder\\\": {\\\
3514 \\\"id\\\": 65, \\\
3515 \\\"name\\\": \\\"Ladder\\\", \\\
3516 \\\"place\\\": \\\"wallsign-ladder\\\",\\\
3517 }, \\\
3518 \\\"rail\\\": {\\\
3519 \\\"id\\\": 66, \\\
3520 \\\"name\\\": \\\"Rail\\\", \\\
3521 \\\"place\\\": \\\"rail\\\",\\\
3522 }, \\\
3523 \\\"stone_stairs\\\": {\\\
3524 \\\"id\\\": 67, \\\
3525 \\\"name\\\": \\\"Cobblestone Stairs\\\", \\\
3526 \\\"place\\\": \\\"stairs\\\",\\\
3527 }, \\\
3528 \\\"wall_sign\\\": {\\\
3529 \\\"id\\\": 68, \\\
3530 \\\"name\\\": \\\"Sign\\\", \\\
3531 \\\"place\\\": \\\"wallsign-ladder\\\",\\\
3532 }, \\\
3533 \\\"lever\\\": {\\\
3534 \\\"id\\\": 69, \\\
3535 \\\"name\\\": \\\"Lever\\\", \\\
3536 \\\"place\\\": \\\"lever\\\",\\\
3537 }, \\\
3538 \\\"stone_pressure_plate\\\": {\\\
3539 \\\"id\\\": 70, \\\
3540 \\\"name\\\": \\\"Stone Pressure Plate\\\", \\\
3541 }, \\\
3542 \\\"iron_door\\\": {\\\
3543 \\\"id\\\": 71, \\\
3544 \\\"name\\\": \\\"Iron Door\\\", \\\
3545 \\\"place\\\": \\\"door\\\",\\\
3546 }, \\\
3547 \\\"wooden_pressure_plate\\\": {\\\
3548 \\\"id\\\": 72, \\\
3549 \\\"name\\\": \\\"Wooden Pressure Plate\\\", \\\
3550 }, \\\
3551 \\\"redstone_ore\\\": {\\\
3552 \\\"id\\\": 73, \\\
3553 \\\"name\\\": \\\"Redstone Ore\\\", \\\
3554 }, \\\
3555 \\\"lit_redstone_ore\\\": {\\\
3556 \\\"id\\\": 74, \\\
3557 \\\"name\\\": \\\"Redstone Ore\\\", \\\
3558 }, \\\
3559 \\\"unlit_redstone_torch\\\": {\\\
3560 \\\"id\\\": 75, \\\
3561 \\\"name\\\": \\\"Redstone Torch (inactive)\\\", \\\
3562 \\\"place\\\": \\\"torch\\\",\\\
3563 }, \\\
3564 \\\"redstone_torch\\\": {\\\
3565 \\\"id\\\": 76, \\\
3566 \\\"name\\\": \\\"Redstone Torch (active)\\\", \\\
3567 \\\"place\\\": \\\"torch\\\",\\\
3568 }, \\\
3569 \\\"stone_button\\\": {\\\
3570 \\\"id\\\": 77, \\\
3571 \\\"name\\\": \\\"Stone Button\\\", \\\
3572 \\\"place\\\": \\\"button\\\",\\\
3573 }, \\\
3574 \\\"snow_layer\\\": {\\\
3575 \\\"id\\\": 78, \\\
3576 \\\"name\\\": \\\"Snow\\\", \\\
3577 \\\"place\\\": \\\"flatten\\\",\\\
3578 }, \\\
3579 \\\"ice\\\": {\\\
3580 \\\"id\\\": 79, \\\
3581 \\\"name\\\": \\\"Ice\\\", \\\
3582 }, \\\
3583 \\\"snow\\\": {\\\
3584 \\\"id\\\": 80, \\\
3585 \\\"name\\\": \\\"Snow\\\", \\\
3586 }, \\\
3587 \\\"cactus\\\": {\\\
3588 \\\"id\\\": 81, \\\
3589 \\\"name\\\": \\\"Cactus\\\", \\\
3590 \\\"place\\\": \\\"flatten\\\",\\\
3591 }, \\\
3592 \\\"clay\\\": {\\\
3593 \\\"id\\\": 82, \\\
3594 \\\"name\\\": \\\"Clay\\\", \\\
3595 }, \\\
3596 \\\"reeds\\\": {\\\
3597 \\\"id\\\": 83, \\\
3598 \\\"name\\\": \\\"Sugar Cane\\\", \\\
3599 \\\"place\\\": \\\"flatten\\\",\\\
3600 }, \\\
3601 \\\"jukebox\\\": {\\\
3602 \\\"id\\\": 84, \\\
3603 \\\"name\\\": \\\"Jukebox\\\", \\\
3604 \\\"place\\\": \\\"flatten\\\",\\\
3605 }, \\\
3606 \\\"fence\\\": {\\\
3607 \\\"id\\\": 85, \\\
3608 \\\"name\\\": \\\"Fence\\\", \\\
3609 }, \\\
3610 \\\"pumpkin\\\": {\\\
3611 \\\"id\\\": 86, \\\
3612 \\\"name\\\": \\\"Pumpkin\\\", \\\
3613 \\\"place\\\": \\\"pumpkin\\\",\\\
3614 }, \\\
3615 \\\"netherrack\\\": {\\\
3616 \\\"id\\\": 87, \\\
3617 \\\"name\\\": \\\"Netherrack\\\", \\\
3618 }, \\\
3619 \\\"soul_sand\\\": {\\\
3620 \\\"id\\\": 88, \\\
3621 \\\"name\\\": \\\"Soul Sand\\\", \\\
3622 }, \\\
3623 \\\"glowstone\\\": {\\\
3624 \\\"id\\\": 89, \\\
3625 \\\"name\\\": \\\"Glowstone\\\", \\\
3626 }, \\\
3627 \\\"portal\\\": {\\\
3628 \\\"id\\\": 90, \\\
3629 \\\"name\\\": \\\"Portal\\\", \\\
3630 \\\"place\\\": \\\"flatten\\\",\\\
3631 }, \\\
3632 \\\"lit_pumpkin\\\": {\\\
3633 \\\"id\\\": 91, \\\
3634 \\\"name\\\": \\\"Jack o'Lantern\\\", \\\
3635 \\\"place\\\": \\\"pumpkin\\\",\\\
3636 }, \\\
3637 \\\"cake\\\": {\\\
3638 \\\"id\\\": 92, \\\
3639 \\\"name\\\": \\\"Cake\\\", \\\
3640 \\\"place\\\": \\\"flatten\\\",\\\
3641 }, \\\
3642 \\\"unpowered_repeater\\\": {\\\
3643 \\\"id\\\": 93, \\\
3644 \\\"name\\\": \\\"Redstone Repeater (inactive)\\\", \\\
3645 \\\"place\\\": \\\"repeater\\\",\\\
3646 }, \\\
3647 \\\"powered_repeater\\\": {\\\
3648 \\\"id\\\": 94, \\\
3649 \\\"name\\\": \\\"Redstone Repeater (active)\\\", \\\
3650 \\\"place\\\": \\\"repeater\\\",\\\
3651 }, \\\
3652 \\\"stained_glass\\\": {\\\
3653 \\\"id\\\": 95, \\\
3654 \\\"name\\\": [\\\"White Stained Glass\\\", \\\
3655 \\\"Orange Stained Glass\\\",\\\
3656 \\\"Magenta Stained Glass\\\",\\\
3657 \\\"Light Blue Stained Glass\\\",\\\
3658 \\\"Yellow Stained Glass\\\",\\\
3659 \\\"Lime Stained Glass\\\",\\\
3660 \\\"Pink Stained Glass\\\",\\\
3661 \\\"Gray Stained Glass\\\",\\\
3662 \\\"Light Gray Stained Glass\\\",\\\
3663 \\\"Cyan Stained Glass\\\",\\\
3664 \\\"Purple Stained Glass\\\",\\\
3665 \\\"Blue Stained Glass\\\",\\\
3666 \\\"Brown Stained Glass\\\",\\\
3667 \\\"Green Stained Glass\\\",\\\
3668 \\\"Red Stained Glass\\\",\\\
3669 \\\"Black Stained Glass\\\"],\\\
3670 }, \\\
3671 \\\"trapdoor\\\": {\\\
3672 \\\"id\\\": 96, \\\
3673 \\\"name\\\": \\\"Trapdoor\\\", \\\
3674 \\\"place\\\": \\\"trapdoor\\\",\\\
3675 }, \\\
3676 \\\"monster_egg\\\": {\\\
3677 \\\"id\\\": 97, \\\
3678 \\\"name\\\": [\\\"Stone Monster Egg\\\", \\\
3679 \\\"Cobblestone Monster Egg\\\", \\\
3680 \\\"Stone Brick Monster Egg\\\", \\\
3681 \\\"Mossy Stone Brick Monster Egg\\\", \\\
3682 \\\"Cracked Stone Brick Monster Egg\\\", \\\
3683 \\\"Chiseled Stone Brick Monster Egg\\\"], \\\
3684 }, \\\
3685 \\\"stonebrick\\\": {\\\
3686 \\\"id\\\": 98, \\\
3687 \\\"name\\\": [\\\"Stone Bricks\\\", \\\
3688 \\\"Mossy Stone Bricks\\\", \\\
3689 \\\"Cracked Stone Bricks\\\", \\\
3690 \\\"Chiseled Stone Bricks\\\"], \\\
3691 }, \\\
3692 \\\"brown_mushroom_block\\\": {\\\
3693 \\\"id\\\": 99, \\\
3694 \\\"name\\\": \\\"Brown Mushroom (block)\\\", \\\
3695 \\\"place\\\": \\\"flatten\\\",\\\
3696 }, \\\
3697 \\\"red_mushroom_block\\\": {\\\
3698 \\\"id\\\": 100, \\\
3699 \\\"name\\\": \\\"Red Mushroom (block)\\\", \\\
3700 \\\"place\\\": \\\"flatten\\\",\\\
3701 }, \\\
3702 \\\"iron_bars\\\": {\\\
3703 \\\"id\\\": 101, \\\
3704 \\\"name\\\": \\\"Iron Bars\\\", \\\
3705 }, \\\
3706 \\\"glass_pane\\\": {\\\
3707 \\\"id\\\": 102, \\\
3708 \\\"name\\\": \\\"Glass Pane\\\", \\\
3709 }, \\\
3710 \\\"melon_block\\\": {\\\
3711 \\\"id\\\": 103, \\\
3712 \\\"name\\\": \\\"Melon\\\", \\\
3713 }, \\\
3714 \\\"pumpkin_stem\\\": {\\\
3715 \\\"id\\\": 104, \\\
3716 \\\"name\\\": \\\"Pumpkin Stem\\\", \\\
3717 }, \\\
3718 \\\"melon_stem\\\": {\\\
3719 \\\"id\\\": 105, \\\
3720 \\\"name\\\": \\\"Melon Stem\\\", \\\
3721 }, \\\
3722 \\\"vine\\\": {\\\
3723 \\\"id\\\": 106, \\\
3724 \\\"name\\\": \\\"Vines\\\", \\\
3725 \\\"place\\\": \\\"vine\\\",\\\
3726 }, \\\
3727 \\\"fence_gate\\\": {\\\
3728 \\\"id\\\": 107, \\\
3729 \\\"name\\\": \\\"Fence Gate\\\", \\\
3730 \\\"place\\\": \\\"gate\\\",\\\
3731 }, \\\
3732 \\\"brick_stairs\\\": {\\\
3733 \\\"id\\\": 108, \\\
3734 \\\"name\\\": \\\"Brick Stairs\\\", \\\
3735 \\\"place\\\": \\\"stairs\\\",\\\
3736 }, \\\
3737 \\\"stone_brick_stairs\\\": {\\\
3738 \\\"id\\\": 109, \\\
3739 \\\"name\\\": \\\"Stone Brick Stairs\\\", \\\
3740 \\\"place\\\": \\\"stairs\\\",\\\
3741 }, \\\
3742 \\\"mycelium\\\": {\\\
3743 \\\"id\\\": 110, \\\
3744 \\\"name\\\": \\\"Mycelium\\\", \\\
3745 }, \\\
3746 \\\"waterlily\\\": {\\\
3747 \\\"id\\\": 111, \\\
3748 \\\"name\\\": \\\"Lily Pad\\\", \\\
3749 }, \\\
3750 \\\"nether_brick\\\": {\\\
3751 \\\"id\\\": 112, \\\
3752 \\\"name\\\": \\\"Nether Brick\\\", \\\
3753 }, \\\
3754 \\\"nether_brick_fence\\\": {\\\
3755 \\\"id\\\": 113, \\\
3756 \\\"name\\\": \\\"Nether Brick Fence\\\", \\\
3757 }, \\\
3758 \\\"nether_brick_stairs\\\": {\\\
3759 \\\"id\\\": 114, \\\
3760 \\\"name\\\": \\\"Nether Brick Stairs\\\", \\\
3761 \\\"place\\\": \\\"stairs\\\",\\\
3762 }, \\\
3763 \\\"nether_wart\\\": {\\\
3764 \\\"id\\\": 115, \\\
3765 \\\"name\\\": \\\"Nether Wart\\\", \\\
3766 \\\"place\\\": \\\"flatten\\\",\\\
3767 }, \\\
3768 \\\"enchanting_table\\\": {\\\
3769 \\\"id\\\": 116, \\\
3770 \\\"name\\\": \\\"Enchantment Table\\\", \\\
3771 }, \\\
3772 \\\"brewing_stand\\\": {\\\
3773 \\\"id\\\": 117, \\\
3774 \\\"name\\\": \\\"Brewing Stand\\\", \\\
3775 \\\"place\\\": \\\"flatten\\\",\\\
3776 }, \\\
3777 \\\"cauldron\\\": {\\\
3778 \\\"id\\\": 118, \\\
3779 \\\"name\\\": \\\"Cauldron\\\", \\\
3780 \\\"place\\\": \\\"cauldron\\\",\\\
3781 }, \\\
3782 \\\"end_portal\\\": {\\\
3783 \\\"id\\\": 119, \\\
3784 \\\"name\\\": \\\"End Portal\\\", \\\
3785 }, \\\
3786 \\\"end_portal_frame\\\": {\\\
3787 \\\"id\\\": 120, \\\
3788 \\\"name\\\": \\\"End Portal Block\\\", \\\
3789 \\\"place\\\": \\\"flatten\\\",\\\
3790 }, \\\
3791 \\\"end_stone\\\": {\\\
3792 \\\"id\\\": 121, \\\
3793 \\\"name\\\": \\\"End Stone\\\", \\\
3794 }, \\\
3795 \\\"dragon_egg\\\": {\\\
3796 \\\"id\\\": 122, \\\
3797 \\\"name\\\": \\\"Dragon Egg\\\", \\\
3798 }, \\\
3799 \\\"redstone_lamp\\\": {\\\
3800 \\\"id\\\": 123, \\\
3801 \\\"name\\\": \\\"Redstone Lamp (inactive)\\\", \\\
3802 }, \\\
3803 \\\"lit_redstone_lamp\\\": {\\\
3804 \\\"id\\\": 124, \\\
3805 \\\"name\\\": \\\"Redstone Lamp (active)\\\", \\\
3806 }, \\\
3807 \\\"double_wooden_slab\\\": {\\\
3808 \\\"id\\\": 125, \\\
3809 \\\"name\\\": \\\"Double Wooden Slab\\\", \\\
3810 }, \\\
3811 \\\"wooden_slab\\\": {\\\
3812 \\\"id\\\": 126, \\\
3813 \\\"name\\\": [\\\"Oak Wood Slab\\\", \\\
3814 \\\"Spruce Wood Slab\\\", \\\
3815 \\\"Birch Wood Slab\\\", \\\
3816 \\\"Jungle Wood Slab\\\", \\\
3817 \\\"Acacia Wood Slab\\\", \\\
3818 \\\"Dark Oak Wood Slab\\\"], \\\
3819 \\\"place\\\": \\\"slab\\\",\\\
3820 }, \\\
3821 \\\"cocoa\\\": {\\\
3822 \\\"id\\\": 127, \\\
3823 \\\"name\\\": \\\"Cocoa\\\", \\\
3824 \\\"place\\\": \\\"cocoa\\\",\\\
3825 }, \\\
3826 \\\"sandstone_stairs\\\": {\\\
3827 \\\"id\\\": 128, \\\
3828 \\\"name\\\": \\\"Sandstone Stairs\\\", \\\
3829 \\\"place\\\": \\\"stairs\\\",\\\
3830 }, \\\
3831 \\\"emerald_ore\\\": {\\\
3832 \\\"id\\\": 129, \\\
3833 \\\"name\\\": \\\"Emerald Ore\\\", \\\
3834 }, \\\
3835 \\\"ender_chest\\\": {\\\
3836 \\\"id\\\": 130, \\\
3837 \\\"name\\\": \\\"Ender Chest\\\", \\\
3838 \\\"place\\\": \\\"chest-furnace\\\",\\\
3839 }, \\\
3840 \\\"tripwire_hook\\\": {\\\
3841 \\\"id\\\": 131, \\\
3842 \\\"name\\\": \\\"Tripwire Hook\\\", \\\
3843 \\\"place\\\": \\\"tripwire\\\",\\\
3844 }, \\\
3845 \\\"tripwire\\\": {\\\
3846 \\\"id\\\": 132, \\\
3847 \\\"name\\\": \\\"Tripwire\\\", \\\
3848 \\\"place\\\": \\\"flatten\\\",\\\
3849 }, \\\
3850 \\\"emerald_block\\\": {\\\
3851 \\\"id\\\": 133, \\\
3852 \\\"name\\\": \\\"Block of Emerald\\\", \\\
3853 }, \\\
3854 \\\"spruce_stairs\\\": {\\\
3855 \\\"id\\\": 134, \\\
3856 \\\"name\\\": \\\"Spruce Wood Stairs\\\", \\\
3857 \\\"place\\\": \\\"stairs\\\",\\\
3858 }, \\\
3859 \\\"birch_stairs\\\": {\\\
3860 \\\"id\\\": 135, \\\
3861 \\\"name\\\": \\\"Birch Wood Stairs\\\", \\\
3862 \\\"place\\\": \\\"stairs\\\",\\\
3863 }, \\\
3864 \\\"jungle_stairs\\\": {\\\
3865 \\\"id\\\": 136, \\\
3866 \\\"name\\\": \\\"Jungle Wood Stairs\\\", \\\
3867 \\\"place\\\": \\\"stairs\\\",\\\
3868 }, \\\
3869 \\\"command_block\\\": {\\\
3870 \\\"id\\\": 137, \\\
3871 \\\"name\\\": \\\"Command Block\\\", \\\
3872 }, \\\
3873 \\\"beacon\\\": {\\\
3874 \\\"id\\\": 138, \\\
3875 \\\"name\\\": \\\"Beacon\\\", \\\
3876 }, \\\
3877 \\\"cobblestone_wall\\\": {\\\
3878 \\\"id\\\": 139, \\\
3879 \\\"name\\\": [\\\"Cobblestone Wall\\\", \\\
3880 \\\"Mossy Cobblestone Wall\\\"], \\\
3881 }, \\\
3882 \\\"flower_pot\\\": {\\\
3883 \\\"id\\\": 140, \\\
3884 \\\"name\\\": \\\"Flower Pot\\\", \\\
3885 \\\"place\\\": \\\"flatten\\\",\\\
3886 }, \\\
3887 \\\"carrots\\\": {\\\
3888 \\\"id\\\": 141, \\\
3889 \\\"name\\\": \\\"Carrot\\\", \\\
3890 \\\"place\\\": \\\"flatten\\\",\\\
3891 }, \\\
3892 \\\"potatoes\\\": {\\\
3893 \\\"id\\\": 142, \\\
3894 \\\"name\\\": \\\"Potato\\\", \\\
3895 \\\"place\\\": \\\"flatten\\\",\\\
3896 }, \\\
3897 \\\"wooden_button\\\": {\\\
3898 \\\"id\\\": 143, \\\
3899 \\\"name\\\": \\\"Wooden Button\\\", \\\
3900 \\\"place\\\": \\\"button\\\",\\\
3901 }, \\\
3902 \\\"skull\\\": {\\\
3903 \\\"id\\\": 144, \\\
3904 \\\"name\\\": [\\\"Skeleton Skull\\\",\\\
3905 \\\"Wither Skeleton Skull\\\",\\\
3906 \\\"Zombie Head\\\",\\\
3907 \\\"Human Head\\\",\\\
3908 \\\"Creeper Head\\\",\\\
3909 \\\"Dragon Head\\\"]\\\
3910 \\\"place\\\": \\\"mobhead\\\",\\\
3911 },\\\
3912 \\\"anvil\\\": {\\\
3913 \\\"id\\\": 145, \\\
3914 \\\"name\\\": [\\\"Anvil\\\",\\\
3915 \\\"Slightly Damaged Anvil\\\",\\\
3916 \\\"Very Damaged Anvil\\\"], \\\
3917 \\\"place\\\": \\\"anvil\\\",\\\
3918 }, \\\
3919 \\\"trapped_chest\\\": {\\\
3920 \\\"id\\\": 146, \\\
3921 \\\"name\\\": \\\"Trapped Chest\\\", \\\
3922 \\\"place\\\": \\\"chest-furnace\\\",\\\
3923 }, \\\
3924 \\\"light_weighted_pressure_plate\\\": {\\\
3925 \\\"id\\\": 147, \\\
3926 \\\"name\\\": \\\"Weighted Pressure Plate\\\", \\\
3927 }, \\\
3928 \\\"heavy_weighted_pressure_plate\\\": {\\\
3929 \\\"id\\\": 148, \\\
3930 \\\"name\\\": \\\"Weighted Pressure Plate\\\", \\\
3931 }, \\\
3932 \\\"unpowered_comparator\\\": {\\\
3933 \\\"id\\\": 149, \\\
3934 \\\"name\\\": \\\"Redstone Comparator\\\", \\\
3935 \\\"place\\\": \\\"comparator\\\",\\\
3936 }, \\\
3937 \\\"powered_comparator\\\": {\\\
3938 \\\"id\\\": 150, \\\
3939 \\\"name\\\": \\\"Redstone Comparator\\\", \\\
3940 }, \\\
3941 \\\"daylight_detector\\\": {\\\
3942 \\\"id\\\": 151, \\\
3943 \\\"name\\\": \\\"Daylight Sensor\\\", \\\
3944 \\\"place\\\": \\\"flatten\\\",\\\
3945 }, \\\
3946 \\\"redstone_block\\\": {\\\
3947 \\\"id\\\": 152, \\\
3948 \\\"name\\\": \\\"Block of Redstone\\\", \\\
3949 }, \\\
3950 \\\"quartz_ore\\\": {\\\
3951 \\\"id\\\": 153, \\\
3952 \\\"name\\\": \\\"Nether Quartz Ore\\\", \\\
3953 }, \\\
3954 \\\"hopper\\\": {\\\
3955 \\\"id\\\": 154, \\\
3956 \\\"name\\\": \\\"Hopper\\\", \\\
3957 \\\"place\\\": \\\"hopper\\\",\\\
3958 }, \\\
3959 \\\"quartz_block\\\": {\\\
3960 \\\"id\\\": 155, \\\
3961 \\\"name\\\": [\\\"Block of Quartz\\\", \\\
3962 \\\"Chiseled Quartz Block\\\", \\\
3963 \\\"Pillar Quartz Block\\\"], \\\
3964 }, \\\
3965 \\\"quartz_stairs\\\": {\\\
3966 \\\"id\\\": 156, \\\
3967 \\\"name\\\": \\\"Quartz Stairs\\\", \\\
3968 \\\"place\\\": \\\"stairs\\\",\\\
3969 }, \\\
3970 \\\"activator_rail\\\": {\\\
3971 \\\"id\\\": 157, \\\
3972 \\\"name\\\": \\\"Activator Rail\\\", \\\
3973 \\\"place\\\": \\\"adp-rail\\\",\\\
3974 }, \\\
3975 \\\"dropper\\\": {\\\
3976 \\\"id\\\": 158, \\\
3977 \\\"name\\\": \\\"Dropper\\\", \\\
3978 \\\"place\\\": \\\"dispenser\\\",\\\
3979 }, \\\
3980 \\\"stained_hardened_clay\\\": {\\\
3981 \\\"id\\\": 159, \\\
3982 \\\"name\\\": [\\\"White Stained Clay\\\", \\\
3983 \\\"Orange Stained Clay\\\", \\\
3984 \\\"Magenta Stained Clay\\\", \\\
3985 \\\"Light Blue Stained Clay\\\", \\\
3986 \\\"Yellow Stained Clay\\\", \\\
3987 \\\"Lime Stained Clay\\\", \\\
3988 \\\"Pink Stained Clay\\\", \\\
3989 \\\"Gray Stained Clay\\\", \\\
3990 \\\"Light Gray Stained Clay\\\", \\\
3991 \\\"Cyan Stained Clay\\\", \\\
3992 \\\"Purple Stained Clay\\\", \\\
3993 \\\"Blue Stained Clay\\\", \\\
3994 \\\"Brown Stained Clay\\\", \\\
3995 \\\"Green Stained Clay\\\", \\\
3996 \\\"Red Stained Clay\\\", \\\
3997 \\\"Black Stained Clay\\\"], \\\
3998 }, \\\
3999 \\\"stained_glass_pane\\\": {\\\
4000 \\\"id\\\": 160, \\\
4001 \\\"name\\\": [\\\"White Stained Glass Pane\\\", \\\
4002 \\\"Orange Stained Glass Pane\\\",\\\
4003 \\\"Magenta Stained Glass Pane\\\",\\\
4004 \\\"Light Blue Stained Glass Pane\\\",\\\
4005 \\\"Yellow Stained Glass Pane\\\",\\\
4006 \\\"Lime Stained Glass Pane\\\",\\\
4007 \\\"Pink Stained Glass Pane\\\",\\\
4008 \\\"Gray Stained Glass Pane\\\",\\\
4009 \\\"Light Gray Stained Glass Pane\\\",\\\
4010 \\\"Cyan Stained Glass Pane\\\",\\\
4011 \\\"Purple Stained Glass Pane\\\",\\\
4012 \\\"Blue Stained Glass Pane\\\",\\\
4013 \\\"Brown Stained Glass Pane\\\",\\\
4014 \\\"Green Stained Glass Pane\\\",\\\
4015 \\\"Red Stained Glass Pane\\\",\\\
4016 \\\"Black Stained Glass Pane\\\"],\\\
4017 }, \\\
4018 \\\"leaves2\\\": {\\\
4019 \\\"id\\\": 161, \\\
4020 \\\"name\\\": [\\\"Acacia Leaves\\\", \\\
4021 \\\"Dark Oak Leaves\\\"],\\\
4022 \\\"place\\\": \\\"leaves\\\",\\\
4023 }, \\\
4024 \\\"log2\\\": {\\\
4025 \\\"id\\\": 162, \\\
4026 \\\"name\\\": [\\\"Acacia Wood\\\", \\\
4027 \\\"Dark Oak Wood\\\"],\\\
4028 \\\"place\\\": \\\"wood\\\",\\\
4029 }, \\\
4030 \\\"acacia_stairs\\\": {\\\
4031 \\\"id\\\": 163, \\\
4032 \\\"name\\\": \\\"Acacia Wood Stairs\\\", \\\
4033 \\\"place\\\": \\\"stairs\\\",\\\
4034 }, \\\
4035 \\\"dark_oak_stairs\\\": {\\\
4036 \\\"id\\\": 164, \\\
4037 \\\"name\\\": \\\"Dark Oak Wood Stairs\\\", \\\
4038 \\\"place\\\": \\\"stairs\\\",\\\
4039 }, \\\
4040 \\\"slime\\\": {\\\
4041 \\\"id\\\": 165, \\\
4042 \\\"name\\\": \\\"Slime Block\\\", \\\
4043 }, \\\
4044 \\\"barrier\\\": {\\\
4045 \\\"id\\\": 166, \\\
4046 \\\"name\\\": \\\"Barrier\\\", \\\
4047 }, \\\
4048 \\\"iron_trapdoor\\\": {\\\
4049 \\\"id\\\": 167, \\\
4050 \\\"name\\\": \\\"Iron Trapdoor\\\", \\\
4051 \\\"place\\\": \\\"trapdoor\\\",\\\
4052 }, \\\
4053 \\\"prismarine\\\": {\\\
4054 \\\"id\\\": 168, \\\
4055 \\\"name\\\": [\\\"Prismarine\\\", \\\
4056 \\\"Prismarine Bricks\\\",\\\
4057 \\\"Dark Prismarine\\\"],\\\
4058 }, \\\
4059 \\\"sea_lantern\\\": {\\\
4060 \\\"id\\\": 169, \\\
4061 \\\"name\\\": \\\"Sea Lantern\\\", \\\
4062 }, \\\
4063 \\\"hay_block\\\": {\\\
4064 \\\"id\\\": 170, \\\
4065 \\\"name\\\": \\\"Hay Block\\\", \\\
4066 \\\"place\\\": \\\"hay-bale\\\",\\\
4067 }, \\\
4068 \\\"carpet\\\": {\\\
4069 \\\"id\\\": 171, \\\
4070 \\\"name\\\": [\\\"Carpet\\\", \\\
4071 \\\"Orange Carpet\\\",\\\
4072 \\\"Magenta Carpet\\\",\\\
4073 \\\"Light Blue Carpet\\\",\\\
4074 \\\"Yellow Carpet\\\",\\\
4075 \\\"Lime Carpet\\\",\\\
4076 \\\"Pink Carpet\\\",\\\
4077 \\\"Gray Carpet\\\",\\\
4078 \\\"Light Gray Carpet\\\",\\\
4079 \\\"Cyan Carpet\\\",\\\
4080 \\\"Purple Carpet\\\",\\\
4081 \\\"Blue Carpet\\\",\\\
4082 \\\"Brown Carpet\\\",\\\
4083 \\\"Green Carpet\\\",\\\
4084 \\\"Red Carpet\\\",\\\
4085 \\\"Black Carpet\\\"],\\\
4086 }, \\\
4087 \\\"hardened_clay\\\": {\\\
4088 \\\"id\\\": 172, \\\
4089 \\\"name\\\": \\\"Hardened Clay\\\", \\\
4090 }, \\\
4091 \\\"coal_block\\\": {\\\
4092 \\\"id\\\": 173, \\\
4093 \\\"name\\\": \\\"Block of Coal\\\", \\\
4094 }, \\\
4095 \\\"packed_ice\\\": {\\\
4096 \\\"id\\\": 174, \\\
4097 \\\"name\\\": \\\"Packed Ice\\\", \\\
4098 }, \\\
4099 \\\"double_plant\\\": {\\\
4100 \\\"id\\\": 175, \\\
4101 \\\"name\\\": [\\\"Sunflower\\\",\\\
4102 \\\"Lilac\\\",\\\
4103 \\\"Double Tallgrass\\\",\\\
4104 \\\"Large Fern\\\",\\\
4105 \\\"Rose Bush\\\",\\\
4106 \\\"Peony\\\"],\\\
4107 \\\"place\\\": \\\"largeplant\\\",\\\
4108 }, \\\
4109 \\\"standing_banner\\\": {\\\
4110 \\\"id\\\": 176, \\\
4111 \\\"name\\\": \\\"Banner\\\", \\\
4112 \\\"place\\\": \\\"signpost\\\",\\\
4113 }, \\\
4114 \\\"wall_banner\\\": {\\\
4115 \\\"id\\\": 177, \\\
4116 \\\"name\\\": \\\"Banner\\\", \\\
4117 \\\"place\\\": \\\"wallsign-ladder\\\",\\\
4118 }, \\\
4119 \\\"daylight_detector_inverted\\\": {\\\
4120 \\\"id\\\": 178, \\\
4121 \\\"name\\\": \\\"Inverted Daylight Sensor\\\", \\\
4122 \\\"place\\\": \\\"flatten\\\",\\\
4123 }, \\\
4124 \\\"red_sandstone\\\": {\\\
4125 \\\"id\\\": 179, \\\
4126 \\\"name\\\": [\\\"Red Sandstone\\\", \\\
4127 \\\"Chiseled Red Sandstone\\\",\\\
4128 \\\"Smooth Red Sandstone\\\"],\\\
4129 }, \\\
4130 \\\"red_sandstone_stairs\\\": {\\\
4131 \\\"id\\\": 180, \\\
4132 \\\"name\\\": \\\"Red Sandstone Stairs\\\", \\\
4133 \\\"place\\\": \\\"stairs\\\",\\\
4134 }, \\\
4135 \\\"double_stone_slab2\\\": {\\\
4136 \\\"id\\\": 181, \\\
4137 \\\"name\\\": \\\"Double Red Sandstone Slab\\\", \\\
4138 }, \\\
4139 \\\"stone_slab2\\\": {\\\
4140 \\\"id\\\": 182, \\\
4141 \\\"name\\\": \\\"Red Sandstone Slab\\\", \\\
4142 \\\"place\\\": \\\"slab\\\",\\\
4143 }, \\\
4144 \\\"spruce_fence_gate\\\": {\\\
4145 \\\"id\\\": 183, \\\
4146 \\\"name\\\": \\\"Spruce Fence Gate\\\", \\\
4147 \\\"place\\\": \\\"gate\\\",\\\
4148 }, \\\
4149 \\\"birch_fence_gate\\\": {\\\
4150 \\\"id\\\": 184, \\\
4151 \\\"name\\\": \\\"Birch Fence Gate\\\", \\\
4152 \\\"place\\\": \\\"gate\\\",\\\
4153 }, \\\
4154 \\\"jungle_fence_gate\\\": {\\\
4155 \\\"id\\\": 185, \\\
4156 \\\"name\\\": \\\"Jungle Fence Gate\\\", \\\
4157 \\\"place\\\": \\\"gate\\\",\\\
4158 }, \\\
4159 \\\"dark_oak_fence_gate\\\": {\\\
4160 \\\"id\\\": 186, \\\
4161 \\\"name\\\": \\\"Dark Oak Fence Gate\\\", \\\
4162 \\\"place\\\": \\\"gate\\\",\\\
4163 }, \\\
4164 \\\"acacia_fence_gate\\\": {\\\
4165 \\\"id\\\": 187, \\\
4166 \\\"name\\\": \\\"Acacia Fence Gate\\\", \\\
4167 \\\"place\\\": \\\"gate\\\",\\\
4168 }, \\\
4169 \\\"spruce_fence\\\": {\\\
4170 \\\"id\\\": 188, \\\
4171 \\\"name\\\": \\\"Spruce Fence\\\", \\\
4172 }, \\\
4173 \\\"birch_fence\\\": {\\\
4174 \\\"id\\\": 189, \\\
4175 \\\"name\\\": \\\"Birch Fence\\\", \\\
4176 }, \\\
4177 \\\"jungle_fence\\\": {\\\
4178 \\\"id\\\": 190, \\\
4179 \\\"name\\\": \\\"Jungle Fence\\\", \\\
4180 }, \\\
4181 \\\"dark_oak_fence\\\": {\\\
4182 \\\"id\\\": 191, \\\
4183 \\\"name\\\": \\\"Dark Oak Fence\\\", \\\
4184 }, \\\
4185 \\\"acacia_fence\\\": {\\\
4186 \\\"id\\\": 192, \\\
4187 \\\"name\\\": \\\"Acacia Fence\\\", \\\
4188 }, \\\
4189 \\\"spruce_door\\\": {\\\
4190 \\\"id\\\": 193, \\\
4191 \\\"name\\\": \\\"Spruce Door\\\", \\\
4192 \\\"place\\\": \\\"door\\\",\\\
4193 }, \\\
4194 \\\"birch_door\\\": {\\\
4195 \\\"id\\\": 194, \\\
4196 \\\"name\\\": \\\"Birch Door\\\", \\\
4197 \\\"place\\\": \\\"door\\\",\\\
4198 }, \\\
4199 \\\"jungle_door\\\": {\\\
4200 \\\"id\\\": 195, \\\
4201 \\\"name\\\": \\\"Jungle Door\\\", \\\
4202 \\\"place\\\": \\\"door\\\",\\\
4203 }, \\\
4204 \\\"acacia_door\\\": {\\\
4205 \\\"id\\\": 196, \\\
4206 \\\"name\\\": \\\"Acacia Door\\\", \\\
4207 \\\"place\\\": \\\"door\\\",\\\
4208 }, \\\
4209 \\\"dark_oak_door\\\": {\\\
4210 \\\"id\\\": 197, \\\
4211 \\\"name\\\": \\\"Dark Oak Door\\\", \\\
4212 \\\"place\\\": \\\"door\\\",\\\
4213 },\\\
4214 \\\"end_rod\\\": {\\\
4215 \\\"id\\\": 198\\\
4216 \\\"name\\\": \\\"End Rod\\\",\\\
4217 \\\"place\\\": \\\"end_rod\\\",\\\
4218 },\\\
4219 \\\"chorus_plant\\\": {\\\
4220 \\\"id\\\": 199\\\
4221 \\\"name\\\": \\\"Chorus Plant\\\",\\\
4222 },\\\
4223 \\\"chorus_flower\\\": {\\\
4224 \\\"id\\\": 200\\\
4225 \\\"name\\\": \\\"Chorus Flower\\\",\\\
4226 },\\\
4227 \\\"purpur_block\\\": {\\\
4228 \\\"id\\\": 201\\\
4229 \\\"name\\\": \\\"Purpur Block\\\",\\\
4230 },\\\
4231 \\\"purpur_pillar\\\": {\\\
4232 \\\"id\\\": 202\\\
4233 \\\"name\\\": \\\"Purpur Pillar\\\",\\\
4234 },\\\
4235 \\\"purpur_stairs\\\": {\\\
4236 \\\"id\\\": 203\\\
4237 \\\"name\\\": \\\"Purpur Stairs\\\",\\\
4238 },\\\
4239 \\\"purpur_double_slab\\\": {\\\
4240 \\\"id\\\": 204\\\
4241 \\\"name\\\": \\\"Double Purpur Slabs\\\",\\\
4242 },\\\
4243 \\\"purpur_slab\\\": {\\\
4244 \\\"name\\\": \\\"Purpur Slab\\\",\\\
4245 \\\"id\\\": 205,\\\
4246 \\\"place\\\": \\\"slab\\\",\\\
4247 },\\\
4248 \\\"end_bricks\\\": {\\\
4249 \\\"name\\\": \\\"End Stone Bricks\\\",\\\
4250 \\\"id\\\": 206\\\
4251 },\\\
4252 \\\"beetroots\\\": {\\\
4253 \\\"name\\\": \\\"Beetroot\\\",\\\
4254 \\\"id\\\": 207\\\
4255 },\\\
4256 \\\"grass_path\\\": {\\\
4257 \\\"name\\\": \\\"Path\\\",\\\
4258 \\\"id\\\": 208\\\
4259 },\\\
4260 \\\"end_gateway\\\": {\\\
4261 \\\"id\\\": 209,\\\
4262 \\\"name\\\": \\\"End Gateway\\\"\\\
4263 },\\\
4264 \\\"repeating_command_block\\\": {\\\
4265 \\\"name\\\": \\\"Repeating Command Block\\\",\\\
4266 \\\"id\\\": 210,\\\
4267 \\\"place\\\": \\\"flatten\\\",\\\
4268 },\\\
4269 \\\"chain_command_block\\\": {\\\
4270 \\\"name\\\": \\\"Chain Command Block\\\",\\\
4271 \\\"id\\\": 211\\\
4272 },\\\
4273 \\\"frosted_ice\\\": {\\\
4274 \\\"name\\\": \\\"Frosted Ice\\\",\\\
4275 \\\"id\\\": 212\\\
4276 },\\\
4277 \\\"magma\\\": {\\\
4278 \\\"id\\\": 213,\\\
4279 \\\"name\\\": \\\"Magma Block\\\",\\\
4280 },\\\
4281 \\\"nether_wart_block\\\": {\\\
4282 \\\"id\\\": 214,\\\
4283 \\\"name\\\": \\\"Nether Wart Block\\\",\\\
4284 },\\\
4285 \\\"red_nether_brick\\\": {\\\
4286 \\\"id\\\": 215,\\\
4287 \\\"name\\\": \\\"Red Nether Brick\\\",\\\
4288 },\\\
4289 \\\"bone_block\\\": {\\\
4290 \\\"id\\\": 216,\\\
4291 \\\"name\\\": \\\"Bone Block\\\",\\\
4292 },\\\
4293 \\\"structure_void\\\": {\\\
4294 \\\"id\\\": 217,\\\
4295 \\\"name\\\": \\\"Structure Void\\\",\\\
4296 },\\\
4297 \\\"observer\\\": {\\\
4298 \\\"name\\\": \\\"Observer\\\",\\\
4299 \\\"id\\\": 218\\\
4300 },\\\
4301 \\\"white_shulker_box\\\": {\\\
4302 \\\"name\\\": \\\"White Shulker Box\\\",\\\
4303 \\\"id\\\": 219\\\
4304 },\\\
4305 \\\"orange_shulker_box\\\": {\\\
4306 \\\"name\\\": \\\"Orange Shulker Box\\\",\\\
4307 \\\"id\\\": 220\\\
4308 },\\\
4309 \\\"magenta_shulker_box\\\": {\\\
4310 \\\"name\\\": \\\"Magenta Shulker Box\\\",\\\
4311 \\\"id\\\": 221\\\
4312 },\\\
4313 \\\"light_blue_shulker_box\\\": {\\\
4314 \\\"name\\\": \\\"Light Blue Shulker Box\\\",\\\
4315 \\\"id\\\": 222\\\
4316 },\\\
4317 \\\"yellow_shulker_box\\\": {\\\
4318 \\\"name\\\": \\\"Yellow Shulker Box\\\",\\\
4319 \\\"id\\\": 223\\\
4320 },\\\
4321 \\\"lime_shulker_box\\\": {\\\
4322 \\\"name\\\": \\\"Lime Shulker Box\\\",\\\
4323 \\\"id\\\": 224\\\
4324 },\\\
4325 \\\"pink_shulker_box\\\": {\\\
4326 \\\"name\\\": \\\"Pink Shulker Box\\\",\\\
4327 \\\"id\\\": 225\\\
4328 },\\\
4329 \\\"gray_shulker_box\\\": {\\\
4330 \\\"name\\\": \\\"Gray Shulker Box\\\",\\\
4331 \\\"id\\\": 226\\\
4332 },\\\
4333 \\\"silver_shulker_box\\\": {\\\
4334 \\\"name\\\": \\\"Light Gray Shulker Box\\\",\\\
4335 \\\"id\\\": 227\\\
4336 },\\\
4337 \\\"cyan_shulker_box\\\": {\\\
4338 \\\"name\\\": \\\"Cyan Shulker Box\\\",\\\
4339 \\\"id\\\": 228\\\
4340 },\\\
4341 \\\"purple_shulker_box\\\": {\\\
4342 \\\"name\\\": \\\"Purple Shulker Box\\\",\\\
4343 \\\"id\\\": 229\\\
4344 },\\\
4345 \\\"blue_shulker_box\\\": {\\\
4346 \\\"name\\\": \\\"Blue Shulker Box\\\",\\\
4347 \\\"id\\\": 230\\\
4348 },\\\
4349 \\\"brown_shulker_box\\\": {\\\
4350 \\\"name\\\": \\\"Brown Shulker Box\\\",\\\
4351 \\\"id\\\": 231\\\
4352 },\\\
4353 \\\"green_shulker_box\\\": {\\\
4354 \\\"name\\\": \\\"Green Shulker Box\\\",\\\
4355 \\\"id\\\": 232\\\
4356 },\\\
4357 \\\"red_shulker_box\\\": {\\\
4358 \\\"name\\\": \\\"Red Shulker Box\\\",\\\
4359 \\\"id\\\": 233\\\
4360 },\\\
4361 \\\"black_shulker_box\\\": {\\\
4362 \\\"name\\\": \\\"Black Shulker Box\\\",\\\
4363 \\\"id\\\": 234\\\
4364 },\\\
4365 \\\"white_glazed_terracotta\\\": {\\\
4366 \\\"id\\\": 235,\\\
4367 \\\"name\\\": \\\"White glazed terracotta\\\",\\\
4368 },\\\
4369 \\\"orange_glazed_terracotta\\\": {\\\
4370 \\\"id\\\": 236,\\\
4371 \\\"name\\\": \\\"Orange glazed terracotta\\\",\\\
4372 },\\\
4373 \\\"magenta_glazed_terracotta\\\": {\\\
4374 \\\"id\\\": 237,\\\
4375 \\\"name\\\": \\\"Magenta glazed terracotta\\\",\\\
4376 },\\\
4377 \\\"light_blue_glazed_terracotta\\\": {\\\
4378 \\\"id\\\": 238,\\\
4379 \\\"name\\\": \\\"Light blue glazed terracotta\\\",\\\
4380 },\\\
4381 \\\"yellow_glazed_terracotta\\\": {\\\
4382 \\\"id\\\": 239,\\\
4383 \\\"name\\\": \\\"Yellow glazed terracotta\\\",\\\
4384 },\\\
4385 \\\"lime_glazed_terracotta\\\": {\\\
4386 \\\"id\\\": 240,\\\
4387 \\\"name\\\": \\\"Lime glazed terracotta\\\",\\\
4388 },\\\
4389 \\\"pink_glazed_terracotta\\\": {\\\
4390 \\\"id\\\": 241,\\\
4391 \\\"name\\\": \\\"Pink glazed terracotta\\\",\\\
4392 },\\\
4393 \\\"gray_glazed_terracotta\\\": {\\\
4394 \\\"id\\\": 242,\\\
4395 \\\"name\\\": \\\"Gray glazed terracotta\\\",\\\
4396 },\\\
4397 \\\"light_gray_glazed_terracotta\\\": {\\\
4398 \\\"id\\\": 243,\\\
4399 \\\"name\\\": \\\"Light gray glazed terracotta\\\",\\\
4400 },\\\
4401 \\\"cyan_glazed_terracotta\\\": {\\\
4402 \\\"id\\\": 244,\\\
4403 \\\"name\\\": \\\"Cyan glazed terracotta\\\",\\\
4404 },\\\
4405 \\\"purple_glazed_terracotta\\\": {\\\
4406 \\\"id\\\": 245,\\\
4407 \\\"name\\\": \\\"Purple glazed terracotta\\\",\\\
4408 },\\\
4409 \\\"blue_glazed_terracotta\\\": {\\\
4410 \\\"id\\\": 246,\\\
4411 \\\"name\\\": \\\"Blue glazed terracotta\\\",\\\
4412 },\\\
4413 \\\"brown_glazed_terracotta\\\": {\\\
4414 \\\"id\\\": 247,\\\
4415 \\\"name\\\": \\\"Brown glazed terracotta\\\",\\\
4416 },\\\
4417 \\\"green_glazed_terracotta\\\": {\\\
4418 \\\"id\\\": 248,\\\
4419 \\\"name\\\": \\\"Green glazed terracotta\\\",\\\
4420 },\\\
4421 \\\"red_glazed_terracotta\\\": {\\\
4422 \\\"id\\\": 249,\\\
4423 \\\"name\\\": \\\"Red glazed terracotta\\\",\\\
4424 },\\\
4425 \\\"black_glazed_terracotta\\\": {\\\
4426 \\\"id\\\": 250,\\\
4427 \\\"name\\\": \\\"Black glazed terracotta\\\",\\\
4428 },\\\
4429 \\\"concrete\\\": {\\\
4430 \\\"id\\\": 251,\\\
4431 \\\"name\\\": [\\\"White concrete\\\",\\\
4432 \\\"Orange concrete\\\",\\\
4433 \\\"Magenta concrete\\\",\\\
4434 \\\"Light blue concrete\\\",\\\
4435 \\\"Yellow concrete\\\",\\\
4436 \\\"Lime concrete\\\",\\\
4437 \\\"Pink concrete\\\",\\\
4438 \\\"Gray concrete\\\",\\\
4439 \\\"Silver concrete\\\",\\\
4440 \\\"Cyan concrete\\\",\\\
4441 \\\"Purple concrete\\\",\\\
4442 \\\"Blue concrete\\\",\\\
4443 \\\"Brown concrete\\\",\\\
4444 \\\"Green concrete\\\",\\\
4445 \\\"Red concrete\\\",\\\
4446 \\\"Black concrete\\\"],\\\
4447 },\\\
4448 \\\"concrete_powder\\\": {\\\
4449 \\\"id\\\": 252,\\\
4450 \\\"name\\\": [\\\"White concrete powder\\\",\\\
4451 \\\"Orange concrete powder\\\",\\\
4452 \\\"Magenta concrete powder\\\",\\\
4453 \\\"Light blue concrete powder\\\",\\\
4454 \\\"Yellow concrete powder\\\",\\\
4455 \\\"Lime concrete powder\\\",\\\
4456 \\\"Pink concrete powder\\\",\\\
4457 \\\"Gray concrete powder\\\",\\\
4458 \\\"Silver concrete powder\\\",\\\
4459 \\\"Cyan concrete powder\\\",\\\
4460 \\\"Purple concrete powder\\\",\\\
4461 \\\"Blue concrete powder\\\",\\\
4462 \\\"Brown concrete powder\\\",\\\
4463 \\\"Green concrete powder\\\",\\\
4464 \\\"Red concrete powder\\\",\\\
4465 \\\"Black concrete powder\\\"],\\\
4466 },\\\
4467 \\\"structure_block\\\": {\\\
4468 \\\"name\\\": [\\\"Structure Block (Save)\\\", \\\
4469 \\\"Structure Block (Load)\\\",\\\
4470 \\\"Structure Block (Corner)\\\",\\\
4471 \\\"Structure Block (Data)\\\"],\\\
4472 \\\"id\\\": 255\\\
4473 },\\\
4474 \\\"iron_shovel\\\": {\\\
4475 \\\"id\\\": 256,\\\
4476 \\\"name\\\": \\\"Iron Shovel\\\"\\\
4477 },\\\
4478 \\\"iron_pickaxe\\\": {\\\
4479 \\\"id\\\": 257,\\\
4480 \\\"name\\\": \\\"Iron Pickaxe\\\"\\\
4481 },\\\
4482 \\\"iron_axe\\\": {\\\
4483 \\\"id\\\": 258,\\\
4484 \\\"name\\\": \\\"Iron Axe\\\"\\\
4485 },\\\
4486 \\\"flint_and_steel\\\": {\\\
4487 \\\"id\\\": 259,\\\
4488 \\\"name\\\": \\\"Flint and Steel\\\"\\\
4489 },\\\
4490 \\\"apple\\\": {\\\
4491 \\\"id\\\": 260,\\\
4492 \\\"name\\\": \\\"Apple\\\"\\\
4493 },\\\
4494 \\\"bow\\\": {\\\
4495 \\\"id\\\": 261,\\\
4496 \\\"name\\\": \\\"Bow\\\"\\\
4497 },\\\
4498 \\\"arrow\\\": {\\\
4499 \\\"id\\\": 262,\\\
4500 \\\"name\\\": \\\"Arrow\\\"\\\
4501 },\\\
4502 \\\"coal\\\": {\\\
4503 \\\"name\\\": [\\\"Coal\\\", \\\
4504 \\\"Charcoal\\\"],\\\
4505 \\\"id\\\": 263\\\
4506 },\\\
4507 \\\"diamond\\\": {\\\
4508 \\\"id\\\": 264,\\\
4509 \\\"name\\\": \\\"Diamond\\\"\\\
4510 },\\\
4511 \\\"iron_ingot\\\": {\\\
4512 \\\"id\\\": 265,\\\
4513 \\\"name\\\": \\\"Iron Ingot\\\"\\\
4514 },\\\
4515 \\\"gold_ingot\\\": {\\\
4516 \\\"id\\\": 266,\\\
4517 \\\"name\\\": \\\"Gold Ingot\\\"\\\
4518 },\\\
4519 \\\"iron_sword\\\": {\\\
4520 \\\"id\\\": 267,\\\
4521 \\\"name\\\": \\\"Iron Sword\\\"\\\
4522 },\\\
4523 \\\"wooden_sword\\\": {\\\
4524 \\\"id\\\": 268,\\\
4525 \\\"name\\\": \\\"Wooden Sword\\\"\\\
4526 },\\\
4527 \\\"wooden_shovel\\\": {\\\
4528 \\\"id\\\": 269,\\\
4529 \\\"name\\\": \\\"Wooden Shovel\\\"\\\
4530 },\\\
4531 \\\"wooden_pickaxe\\\": {\\\
4532 \\\"id\\\": 270,\\\
4533 \\\"name\\\": \\\"Wooden Pickaxe\\\"\\\
4534 },\\\
4535 \\\"wooden_axe\\\": {\\\
4536 \\\"id\\\": 271,\\\
4537 \\\"name\\\": \\\"Wooden Axe\\\"\\\
4538 },\\\
4539 \\\"stone_sword\\\": {\\\
4540 \\\"id\\\": 272,\\\
4541 \\\"name\\\": \\\"Stone Sword\\\"\\\
4542 },\\\
4543 \\\"stone_shovel\\\": {\\\
4544 \\\"id\\\": 273,\\\
4545 \\\"name\\\": \\\"Stone Shovel\\\"\\\
4546 },\\\
4547 \\\"stone_pickaxe\\\": {\\\
4548 \\\"id\\\": 274,\\\
4549 \\\"name\\\": \\\"Stone Pickaxe\\\"\\\
4550 },\\\
4551 \\\"stone_axe\\\": {\\\
4552 \\\"id\\\": 275,\\\
4553 \\\"name\\\": \\\"Stone Axe\\\"\\\
4554 },\\\
4555 \\\"diamond_sword\\\": {\\\
4556 \\\"id\\\": 276,\\\
4557 \\\"name\\\": \\\"Diamond Sword\\\"\\\
4558 },\\\
4559 \\\"diamond_shovel\\\": {\\\
4560 \\\"id\\\": 277,\\\
4561 \\\"name\\\": \\\"Diamond Shovel\\\"\\\
4562 },\\\
4563 \\\"diamond_pickaxe\\\": {\\\
4564 \\\"id\\\": 278,\\\
4565 \\\"name\\\": \\\"Diamond Pickaxe\\\"\\\
4566 },\\\
4567 \\\"diamond_axe\\\": {\\\
4568 \\\"id\\\": 279,\\\
4569 \\\"name\\\": \\\"Diamond Axe\\\"\\\
4570 },\\\
4571 \\\"stick\\\": {\\\
4572 \\\"id\\\": 280,\\\
4573 \\\"name\\\": \\\"Stick\\\"\\\
4574 },\\\
4575 \\\"bowl\\\": {\\\
4576 \\\"id\\\": 281,\\\
4577 \\\"name\\\": \\\"Bowl\\\"\\\
4578 },\\\
4579 \\\"mushroom_stew\\\": {\\\
4580 \\\"id\\\": 282,\\\
4581 \\\"name\\\": \\\"Mushroom Stew\\\"\\\
4582 },\\\
4583 \\\"golden_sword\\\": {\\\
4584 \\\"id\\\": 283,\\\
4585 \\\"name\\\": \\\"Golden Sword\\\"\\\
4586 },\\\
4587 \\\"golden_shovel\\\": {\\\
4588 \\\"id\\\": 284,\\\
4589 \\\"name\\\": \\\"Golden Shovel\\\"\\\
4590 },\\\
4591 \\\"golden_pickaxe\\\": {\\\
4592 \\\"id\\\": 285,\\\
4593 \\\"name\\\": \\\"Golden Pickaxe\\\"\\\
4594 },\\\
4595 \\\"golden_axe\\\": {\\\
4596 \\\"id\\\": 286,\\\
4597 \\\"name\\\": \\\"Golden Axe\\\"\\\
4598 },\\\
4599 \\\"string\\\": {\\\
4600 \\\"id\\\": 287,\\\
4601 \\\"name\\\": \\\"String\\\"\\\
4602 },\\\
4603 \\\"feather\\\": {\\\
4604 \\\"id\\\": 288,\\\
4605 \\\"name\\\": \\\"Feather\\\"\\\
4606 },\\\
4607 \\\"gunpowder\\\": {\\\
4608 \\\"id\\\": 289,\\\
4609 \\\"name\\\": \\\"Gunpowder\\\"\\\
4610 },\\\
4611 \\\"wooden_hoe\\\": {\\\
4612 \\\"id\\\": 290,\\\
4613 \\\"name\\\": \\\"Wooden Hoe\\\"\\\
4614 },\\\
4615 \\\"stone_hoe\\\": {\\\
4616 \\\"id\\\": 291,\\\
4617 \\\"name\\\": \\\"Stone Hoe\\\"\\\
4618 },\\\
4619 \\\"iron_hoe\\\": {\\\
4620 \\\"id\\\": 292,\\\
4621 \\\"name\\\": \\\"Iron Hoe\\\"\\\
4622 },\\\
4623 \\\"diamond_hoe\\\": {\\\
4624 \\\"id\\\": 293,\\\
4625 \\\"name\\\": \\\"Diamond Hoe\\\"\\\
4626 },\\\
4627 \\\"golden_hoe\\\": {\\\
4628 \\\"id\\\": 294,\\\
4629 \\\"name\\\": \\\"Golden Hoe\\\"\\\
4630 },\\\
4631 \\\"wheat_seeds\\\": {\\\
4632 \\\"id\\\": 295,\\\
4633 \\\"name\\\": \\\"Wheat Seeds\\\",\\\
4634 },\\\
4635 \\\"bread\\\": {\\\
4636 \\\"id\\\": 297,\\\
4637 \\\"name\\\": \\\"Bread\\\"\\\
4638 },\\\
4639 \\\"leather_helmet\\\": {\\\
4640 \\\"id\\\": 298,\\\
4641 \\\"name\\\": \\\"Leather Helmet\\\"\\\
4642 },\\\
4643 \\\"leather_chestplate\\\": {\\\
4644 \\\"id\\\": 299,\\\
4645 \\\"name\\\": \\\"Leather Tunic\\\"\\\
4646 },\\\
4647 \\\"leather_leggings\\\": {\\\
4648 \\\"id\\\": 300,\\\
4649 \\\"name\\\": \\\"Leather Pants\\\"\\\
4650 },\\\
4651 \\\"leather_boots\\\": {\\\
4652 \\\"id\\\": 301,\\\
4653 \\\"name\\\": \\\"Leather Boots\\\"\\\
4654 },\\\
4655 \\\"chainmail_helmet\\\": {\\\
4656 \\\"id\\\": 302,\\\
4657 \\\"name\\\": \\\"Chainmail Helmet\\\"\\\
4658 },\\\
4659 \\\"chainmail_chestplate\\\": {\\\
4660 \\\"id\\\": 303,\\\
4661 \\\"name\\\": \\\"Chainmail Chestplate\\\"\\\
4662 },\\\
4663 \\\"chainmail_leggings\\\": {\\\
4664 \\\"id\\\": 304,\\\
4665 \\\"name\\\": \\\"Chainmail Leggings\\\"\\\
4666 },\\\
4667 \\\"chainmail_boots\\\": {\\\
4668 \\\"id\\\": 305,\\\
4669 \\\"name\\\": \\\"Chainmail Boots\\\"\\\
4670 },\\\
4671 \\\"iron_helmet\\\": {\\\
4672 \\\"id\\\": 306,\\\
4673 \\\"name\\\": \\\"Iron Helmet\\\"\\\
4674 },\\\
4675 \\\"iron_chestplate\\\": {\\\
4676 \\\"id\\\": 307,\\\
4677 \\\"name\\\": \\\"Iron Chestplate\\\"\\\
4678 },\\\
4679 \\\"iron_leggings\\\": {\\\
4680 \\\"id\\\": 308,\\\
4681 \\\"name\\\": \\\"Iron Leggings\\\"\\\
4682 },\\\
4683 \\\"iron_boots\\\": {\\\
4684 \\\"id\\\": 309,\\\
4685 \\\"name\\\": \\\"Iron Boots\\\"\\\
4686 },\\\
4687 \\\"diamond_helmet\\\": {\\\
4688 \\\"id\\\": 310,\\\
4689 \\\"name\\\": \\\"Diamond Helmet\\\"\\\
4690 },\\\
4691 \\\"diamond_chestplate\\\": {\\\
4692 \\\"id\\\": 311,\\\
4693 \\\"name\\\": \\\"Diamond Chestplate\\\"\\\
4694 },\\\
4695 \\\"diamond_leggings\\\": {\\\
4696 \\\"id\\\": 312,\\\
4697 \\\"name\\\": \\\"Diamond Leggings\\\"\\\
4698 },\\\
4699 \\\"diamond_boots\\\": {\\\
4700 \\\"id\\\": 313,\\\
4701 \\\"name\\\": \\\"Diamond Boots\\\"\\\
4702 },\\\
4703 \\\"golden_helmet\\\": {\\\
4704 \\\"id\\\": 314,\\\
4705 \\\"name\\\": \\\"Golden Helmet\\\"\\\
4706 },\\\
4707 \\\"golden_chestplate\\\": {\\\
4708 \\\"id\\\": 315,\\\
4709 \\\"name\\\": \\\"Golden Chestplate\\\"\\\
4710 },\\\
4711 \\\"golden_leggings\\\": {\\\
4712 \\\"id\\\": 316,\\\
4713 \\\"name\\\": \\\"Golden Leggings\\\"\\\
4714 },\\\
4715 \\\"golden_boots\\\": {\\\
4716 \\\"id\\\": 317,\\\
4717 \\\"name\\\": \\\"Golden Boots\\\"\\\
4718 },\\\
4719 \\\"flint\\\": {\\\
4720 \\\"id\\\": 318,\\\
4721 \\\"name\\\": \\\"Flint\\\"\\\
4722 },\\\
4723 \\\"porkchop\\\": {\\\
4724 \\\"id\\\": 319,\\\
4725 \\\"name\\\": \\\"Raw Porkchop\\\"\\\
4726 },\\\
4727 \\\"cooked_porkchop\\\": {\\\
4728 \\\"id\\\": 320,\\\
4729 \\\"name\\\": \\\"Cooked Porkchop\\\"\\\
4730 },\\\
4731 \\\"painting\\\": {\\\
4732 \\\"id\\\": 321,\\\
4733 \\\"name\\\": \\\"Painting\\\"\\\
4734 },\\\
4735 \\\"golden_apple\\\": {\\\
4736 \\\"id\\\": 322,\\\
4737 \\\"name\\\": [\\\"Enchanted Golden Apple\\\",\\\
4738 \\\"Enchanted Golden Apple\\\" ]\\\
4739 },\\\
4740 \\\"sign\\\": {\\\
4741 \\\"id\\\": 323,\\\
4742 \\\"name\\\": \\\"Sign\\\",\\\
4743 },\\\
4744 \\\"bucket\\\": {\\\
4745 \\\"id\\\": 325,\\\
4746 \\\"name\\\": \\\"Bucket\\\"\\\
4747 },\\\
4748 \\\"water_bucket\\\": {\\\
4749 \\\"id\\\": 326,\\\
4750 \\\"name\\\": \\\"Water Bucket\\\"\\\
4751 },\\\
4752 \\\"lava_bucket\\\": {\\\
4753 \\\"id\\\": 327,\\\
4754 \\\"name\\\": \\\"Lava Bucket\\\"\\\
4755 },\\\
4756 \\\"minecart\\\": {\\\
4757 \\\"id\\\": 328,\\\
4758 \\\"name\\\": \\\"Minecart\\\"\\\
4759 },\\\
4760 \\\"saddle\\\": {\\\
4761 \\\"id\\\": 329,\\\
4762 \\\"name\\\": \\\"Saddle\\\"\\\
4763 },\\\
4764 \\\"redstone\\\": {\\\
4765 \\\"id\\\": 331,\\\
4766 \\\"name\\\": \\\"Redstone Dust\\\",\\\
4767 },\\\
4768 \\\"snowball\\\": {\\\
4769 \\\"id\\\": 332,\\\
4770 \\\"name\\\": \\\"Snowball\\\"\\\
4771 },\\\
4772 \\\"boat\\\": {\\\
4773 \\\"id\\\": 333,\\\
4774 \\\"name\\\": \\\"Oak Boat\\\"\\\
4775 },\\\
4776 \\\"leather\\\": {\\\
4777 \\\"id\\\": 334,\\\
4778 \\\"name\\\": \\\"Leather\\\"\\\
4779 },\\\
4780 \\\"milk_bucket\\\": {\\\
4781 \\\"id\\\": 335,\\\
4782 \\\"name\\\": \\\"Milk Bucket\\\"\\\
4783 },\\\
4784 \\\"brick\\\": {\\\
4785 \\\"id\\\": 336,\\\
4786 \\\"name\\\": \\\"Brick\\\"\\\
4787 },\\\
4788 \\\"clay_ball\\\": {\\\
4789 \\\"id\\\": 337,\\\
4790 \\\"name\\\": \\\"Clay\\\"\\\
4791 },\\\
4792 \\\"paper\\\": {\\\
4793 \\\"id\\\": 339,\\\
4794 \\\"name\\\": \\\"Paper\\\"\\\
4795 },\\\
4796 \\\"book\\\": {\\\
4797 \\\"id\\\": 340,\\\
4798 \\\"name\\\": \\\"Book\\\"\\\
4799 },\\\
4800 \\\"slime_ball\\\": {\\\
4801 \\\"id\\\": 341,\\\
4802 \\\"name\\\": \\\"Slimeball\\\"\\\
4803 },\\\
4804 \\\"chest_minecart\\\": {\\\
4805 \\\"id\\\": 342,\\\
4806 \\\"name\\\": \\\"Minecart with Chest\\\"\\\
4807 },\\\
4808 \\\"furnace_minecart\\\": {\\\
4809 \\\"id\\\": 343,\\\
4810 \\\"name\\\": \\\"Minecart with Furnace\\\"\\\
4811 },\\\
4812 \\\"egg\\\": {\\\
4813 \\\"id\\\": 344,\\\
4814 \\\"name\\\": \\\"Egg\\\"\\\
4815 },\\\
4816 \\\"compass\\\": {\\\
4817 \\\"id\\\": 345,\\\
4818 \\\"name\\\": \\\"Compass\\\"\\\
4819 },\\\
4820 \\\"fishing_rod\\\": {\\\
4821 \\\"id\\\": 346,\\\
4822 \\\"name\\\": \\\"Fishing Rod\\\"\\\
4823 },\\\
4824 \\\"clock\\\": {\\\
4825 \\\"id\\\": 347,\\\
4826 \\\"name\\\": \\\"Clock\\\"\\\
4827 },\\\
4828 \\\"glowstone_dust\\\": {\\\
4829 \\\"id\\\": 348,\\\
4830 \\\"name\\\": \\\"Glowstone Dust\\\"\\\
4831 },\\\
4832 \\\"fish\\\": {\\\
4833 \\\"id\\\": 349,\\\
4834 \\\"name\\\": [\\\"Raw Fish\\\",\\\
4835 \\\"Raw Salmon\\\",\\\
4836 \\\"Clownfish\\\",\\\
4837 \\\"Pufferfish\\\"]\\\
4838 },\\\
4839 \\\"cooked_fish\\\": {\\\
4840 \\\"id\\\": 350,\\\
4841 \\\"name\\\": [\\\"Cooked Fish\\\",\\\
4842 \\\"Cooked Salmon\\\"]\\\
4843 },\\\
4844 \\\"dye\\\": {\\\
4845 \\\"id\\\": 351,\\\
4846 \\\"name\\\": [\\\"Ink Sack\\\",\\\
4847 \\\"Rose Red\\\",\\\
4848 \\\"Cactus Green\\\",\\\
4849 \\\"Cocoa Bean\\\",\\\
4850 \\\"Lapis Lazuli\\\",\\\
4851 \\\"Purple Dye\\\",\\\
4852 \\\"Cyan Dye\\\",\\\
4853 \\\"Light Gray Dye\\\",\\\
4854 \\\"Gray Dye\\\",\\\
4855 \\\"Pink Dye\\\",\\\
4856 \\\"Lime Dye\\\",\\\
4857 \\\"Dandelion Yellow\\\",\\\
4858 \\\"Light Blue Dye\\\",\\\
4859 \\\"Magenta Dye\\\",\\\
4860 \\\"Orange Dye\\\",\\\
4861 \\\"Bone Meal\\\"]\\\
4862 },\\\
4863 \\\"bone\\\": {\\\
4864 \\\"id\\\": 352,\\\
4865 \\\"name\\\": \\\"Bone\\\"\\\
4866 },\\\
4867 \\\"sugar\\\": {\\\
4868 \\\"id\\\": 353,\\\
4869 \\\"name\\\": \\\"Sugar\\\"\\\
4870 },\\\
4871 \\\"bed-block\\\": {\\\
4872 \\\"id\\\": 355,\\\
4873 \\\"name\\\": \\\"Bed\\\",\\\
4874 \\\"place\\\": \\\"bed\\\",\\\
4875 },\\\
4876 \\\"repeater\\\": {\\\
4877 \\\"id\\\": 356,\\\
4878 \\\"name\\\": \\\"Redstone Repeater\\\",\\\
4879 \\\"place\\\": \\\"repeater\\\",\\\
4880 },\\\
4881 \\\"cookie\\\": {\\\
4882 \\\"id\\\": 357,\\\
4883 \\\"name\\\": \\\"Cookie\\\"\\\
4884 },\\\
4885 \\\"filled_map\\\": {\\\
4886 \\\"id\\\": 358,\\\
4887 \\\"name\\\": \\\"Map\\\"\\\
4888 },\\\
4889 \\\"shears\\\": {\\\
4890 \\\"id\\\": 359,\\\
4891 \\\"name\\\": \\\"Shears\\\"\\\
4892 },\\\
4893 \\\"melon\\\": {\\\
4894 \\\"id\\\": 360,\\\
4895 \\\"name\\\": \\\"Melon\\\"\\\
4896 },\\\
4897 \\\"pumpkin_seeds\\\": {\\\
4898 \\\"id\\\": 361,\\\
4899 \\\"name\\\": \\\"Pumpkin Seeds\\\"\\\
4900 },\\\
4901 \\\"melon_seeds\\\": {\\\
4902 \\\"id\\\": 362,\\\
4903 \\\"name\\\": \\\"Melon Seeds\\\"\\\
4904 },\\\
4905 \\\"beef\\\": {\\\
4906 \\\"id\\\": 363,\\\
4907 \\\"name\\\": \\\"Raw Beef\\\"\\\
4908 },\\\
4909 \\\"cooked_beef\\\": {\\\
4910 \\\"id\\\": 364,\\\
4911 \\\"name\\\": \\\"Steak\\\"\\\
4912 },\\\
4913 \\\"chicken\\\": {\\\
4914 \\\"id\\\": 365,\\\
4915 \\\"name\\\": \\\"Raw Chicken\\\"\\\
4916 },\\\
4917 \\\"cooked_chicken\\\": {\\\
4918 \\\"id\\\": 366,\\\
4919 \\\"name\\\": \\\"Cooked Chicken\\\"\\\
4920 },\\\
4921 \\\"rotten_flesh\\\": {\\\
4922 \\\"id\\\": 367,\\\
4923 \\\"name\\\": \\\"Rotten Flesh\\\"\\\
4924 },\\\
4925 \\\"ender_pearl\\\": {\\\
4926 \\\"id\\\": 368,\\\
4927 \\\"name\\\": \\\"Ender Pearl\\\"\\\
4928 },\\\
4929 \\\"blaze_rod\\\": {\\\
4930 \\\"id\\\": 369,\\\
4931 \\\"name\\\": \\\"Blaze Rod\\\"\\\
4932 },\\\
4933 \\\"ghast_tear\\\": {\\\
4934 \\\"id\\\": 370,\\\
4935 \\\"name\\\": \\\"Ghast Tear\\\"\\\
4936 },\\\
4937 \\\"gold_nugget\\\": {\\\
4938 \\\"id\\\": 371,\\\
4939 \\\"name\\\": \\\"Gold Nugget\\\"\\\
4940 },\\\
4941 \\\"potion\\\": {\\\
4942 \\\"id\\\": 373,\\\
4943 \\\"name\\\": \\\"Potion\\\"\\\
4944 },\\\
4945 \\\"glass_bottle\\\": {\\\
4946 \\\"id\\\": 374,\\\
4947 \\\"name\\\": \\\"Glass Bottle\\\"\\\
4948 },\\\
4949 \\\"spider_eye\\\": {\\\
4950 \\\"id\\\": 375,\\\
4951 \\\"name\\\": \\\"Spider Eye\\\"\\\
4952 },\\\
4953 \\\"fermented_spider_eye\\\": {\\\
4954 \\\"id\\\": 376,\\\
4955 \\\"name\\\": \\\"Fermented Spider Eye\\\"\\\
4956 },\\\
4957 \\\"blaze_powder\\\": {\\\
4958 \\\"id\\\": 377,\\\
4959 \\\"name\\\": \\\"Blaze Powder\\\"\\\
4960 },\\\
4961 \\\"magma_cream\\\": {\\\
4962 \\\"id\\\": 378,\\\
4963 \\\"name\\\": \\\"Magma Cream\\\"\\\
4964 },\\\
4965 \\\"ender_eye\\\": {\\\
4966 \\\"id\\\": 381,\\\
4967 \\\"name\\\": \\\"Eye of Ender\\\"\\\
4968 },\\\
4969 \\\"speckled_melon\\\": {\\\
4970 \\\"id\\\": 382,\\\
4971 \\\"name\\\": \\\"Glistering Melon\\\"\\\
4972 },\\\
4973 \\\"spawn_egg\\\": {\\\
4974 \\\"id\\\": 383,\\\
4975 \\\"name\\\": \\\"Spawn Egg\\\"\\\
4976 },\\\
4977 \\\"experience_bottle\\\": {\\\
4978 \\\"id\\\": 384,\\\
4979 \\\"name\\\": \\\"Bottle o' Enchanting\\\"\\\
4980 },\\\
4981 \\\"fire_charge\\\": {\\\
4982 \\\"id\\\": 385,\\\
4983 \\\"name\\\": \\\"Fire Charge\\\"\\\
4984 },\\\
4985 \\\"writable_book\\\": {\\\
4986 \\\"id\\\": 386,\\\
4987 \\\"name\\\": \\\"Book and Quill\\\"\\\
4988 },\\\
4989 \\\"written_book\\\": {\\\
4990 \\\"id\\\": 387,\\\
4991 \\\"name\\\": \\\"Written Book\\\"\\\
4992 },\\\
4993 \\\"emerald\\\": {\\\
4994 \\\"id\\\": 388,\\\
4995 \\\"name\\\": \\\"Emerald\\\"\\\
4996 },\\\
4997 \\\"item_frame\\\": {\\\
4998 \\\"id\\\": 389,\\\
4999 \\\"name\\\": \\\"Item Frame\\\"\\\
5000 },\\\
5001 \\\"carrot\\\": {\\\
5002 \\\"id\\\": 391,\\\
5003 \\\"name\\\": \\\"Carrot\\\",\\\
5004 },\\\
5005 \\\"potato\\\": {\\\
5006 \\\"id\\\": 392,\\\
5007 \\\"name\\\": \\\"Potato\\\",\\\
5008 },\\\
5009 \\\"baked_potato\\\": {\\\
5010 \\\"id\\\": 393,\\\
5011 \\\"name\\\": \\\"Baked Potato\\\"\\\
5012 },\\\
5013 \\\"poisonous_potato\\\": {\\\
5014 \\\"id\\\": 394,\\\
5015 \\\"name\\\": \\\"Poisonous Potato\\\"\\\
5016 },\\\
5017 \\\"map\\\": {\\\
5018 \\\"id\\\": 395,\\\
5019 \\\"name\\\": \\\"Empty Map\\\"\\\
5020 },\\\
5021 \\\"golden_carrot\\\": {\\\
5022 \\\"id\\\": 396,\\\
5023 \\\"name\\\": \\\"Golden Carrot\\\"\\\
5024 },\\\
5025 \\\"carrot_on_a_stick\\\": {\\\
5026 \\\"id\\\": 398,\\\
5027 \\\"name\\\": \\\"Carrot on a Stick\\\"\\\
5028 },\\\
5029 \\\"nether_star\\\": {\\\
5030 \\\"id\\\": 399,\\\
5031 \\\"name\\\": \\\"Nether Star\\\"\\\
5032 },\\\
5033 \\\"pumpkin_pie\\\": {\\\
5034 \\\"id\\\": 400,\\\
5035 \\\"name\\\": \\\"Pumpkin Pie\\\"\\\
5036 },\\\
5037 \\\"fireworks\\\": {\\\
5038 \\\"id\\\": 401,\\\
5039 \\\"name\\\": \\\"Firework Rocket\\\"\\\
5040 },\\\
5041 \\\"firework_charge\\\": {\\\
5042 \\\"id\\\": 402,\\\
5043 \\\"name\\\": \\\"Firework Star\\\"\\\
5044 },\\\
5045 \\\"enchanted_book\\\": {\\\
5046 \\\"id\\\": 403,\\\
5047 \\\"name\\\": \\\"Enchanted Book\\\"\\\
5048 },\\\
5049 \\\"comparator\\\": {\\\
5050 \\\"id\\\": 404,\\\
5051 \\\"name\\\": \\\"Redstone Comparator\\\",\\\
5052 \\\"place\\\": \\\"comparator\\\",\\\
5053 },\\\
5054 \\\"netherbrick\\\": {\\\
5055 \\\"id\\\": 405,\\\
5056 \\\"name\\\": \\\"Nether Brick\\\"\\\
5057 },\\\
5058 \\\"quartz\\\": {\\\
5059 \\\"id\\\": 406,\\\
5060 \\\"name\\\": \\\"Nether Quartz\\\"\\\
5061 },\\\
5062 \\\"tnt_minecart\\\": {\\\
5063 \\\"id\\\": 407,\\\
5064 \\\"name\\\": \\\"Minecart with TNT\\\"\\\
5065 },\\\
5066 \\\"hopper_minecart\\\": {\\\
5067 \\\"id\\\": 408,\\\
5068 \\\"name\\\": \\\"Minecart with Hopper\\\"\\\
5069 },\\\
5070 \\\"prismarine_shard\\\": {\\\
5071 \\\"id\\\": 409,\\\
5072 \\\"name\\\": \\\"Prismarine Shard\\\"\\\
5073 },\\\
5074 \\\"prismarine_crystals\\\": {\\\
5075 \\\"id\\\": 410,\\\
5076 \\\"name\\\": \\\"Prismarine Crystals\\\"\\\
5077 },\\\
5078 \\\"rabbit\\\": {\\\
5079 \\\"id\\\": 411,\\\
5080 \\\"name\\\": \\\"Raw Rabbit\\\"\\\
5081 },\\\
5082 \\\"cooked_rabbit\\\": {\\\
5083 \\\"id\\\": 412,\\\
5084 \\\"name\\\": \\\"Cooked Rabbit\\\"\\\
5085 },\\\
5086 \\\"rabbit_stew\\\": {\\\
5087 \\\"id\\\": 413,\\\
5088 \\\"name\\\": \\\"Rabbit Stew\\\"\\\
5089 },\\\
5090 \\\"rabbit_foot\\\": {\\\
5091 \\\"id\\\": 414,\\\
5092 \\\"name\\\": \\\"Rabbit's Foot\\\"\\\
5093 },\\\
5094 \\\"rabbit_hide\\\": {\\\
5095 \\\"id\\\": 415,\\\
5096 \\\"name\\\": \\\"Rabbit Hide\\\"\\\
5097 },\\\
5098 \\\"armor_stand\\\": {\\\
5099 \\\"id\\\": 416,\\\
5100 \\\"name\\\": \\\"Armor Stand\\\"\\\
5101 },\\\
5102 \\\"iron_horse_armor\\\": {\\\
5103 \\\"id\\\": 417,\\\
5104 \\\"name\\\": \\\"Iron Horse Armor\\\"\\\
5105 },\\\
5106 \\\"golden_horse_armor\\\": {\\\
5107 \\\"id\\\": 418,\\\
5108 \\\"name\\\": \\\"Golden Horse Armor\\\"\\\
5109 },\\\
5110 \\\"diamond_horse_armor\\\": {\\\
5111 \\\"id\\\": 419,\\\
5112 \\\"name\\\": \\\"Diamond Horse Armor\\\"\\\
5113 },\\\
5114 \\\"lead\\\": {\\\
5115 \\\"id\\\": 420,\\\
5116 \\\"name\\\": \\\"Lead\\\"\\\
5117 },\\\
5118 \\\"name_tag\\\": {\\\
5119 \\\"id\\\": 421,\\\
5120 \\\"name\\\": \\\"Name Tag\\\"\\\
5121 },\\\
5122 \\\"command_block_minecart\\\": {\\\
5123 \\\"id\\\": 422,\\\
5124 \\\"name\\\": \\\"Minecart with Command Block\\\"\\\
5125 },\\\
5126 \\\"mutton\\\": {\\\
5127 \\\"id\\\": 423,\\\
5128 \\\"name\\\": \\\"Raw Mutton\\\"\\\
5129 },\\\
5130 \\\"cooked_mutton\\\": {\\\
5131 \\\"id\\\": 424,\\\
5132 \\\"name\\\": \\\"Cooked Mutton\\\"\\\
5133 },\\\
5134 \\\"banner\\\": {\\\
5135 \\\"id\\\": 425,\\\
5136 \\\"name\\\": \\\"Banner\\\",\\\
5137 },\\\
5138 \\\"chorus_fruit\\\": {\\\
5139 \\\"id\\\": 432,\\\
5140 \\\"name\\\": \\\"Chorus Fruit\\\"\\\
5141 },\\\
5142 \\\"popped_chorus_fruit\\\": {\\\
5143 \\\"id\\\": 433,\\\
5144 \\\"name\\\": \\\"Popped Chorus Fruit\\\"\\\
5145 },\\\
5146 \\\"beetroot\\\": {\\\
5147 \\\"id\\\": 434,\\\
5148 \\\"name\\\": \\\"Beetroot\\\"\\\
5149 },\\\
5150 \\\"beetroot_seeds\\\": {\\\
5151 \\\"id\\\": 435,\\\
5152 \\\"name\\\": \\\"Beetroot Seeds\\\"\\\
5153 },\\\
5154 \\\"beetroot_soup\\\": {\\\
5155 \\\"id\\\": 436,\\\
5156 \\\"name\\\": \\\"Beetroot Soup\\\"\\\
5157 },\\\
5158 \\\"dragon_breath\\\": {\\\
5159 \\\"id\\\": 437,\\\
5160 \\\"name\\\": \\\"Dragon's Breath\\\"\\\
5161 },\\\
5162 \\\"splash_potion\\\": {\\\
5163 \\\"id\\\": 438,\\\
5164 \\\"name\\\": \\\"Splash Potion\\\"\\\
5165 },\\\
5166 \\\"spectral_arrow\\\": {\\\
5167 \\\"id\\\": 439,\\\
5168 \\\"name\\\": \\\"Spectral Arrow\\\"\\\
5169 },\\\
5170 \\\"tipped_arrow\\\": {\\\
5171 \\\"id\\\": 440,\\\
5172 \\\"name\\\": \\\"Tipped Arrow\\\"\\\
5173 },\\\
5174 \\\"lingering_potion\\\": {\\\
5175 \\\"id\\\": 441,\\\
5176 \\\"name\\\": \\\"Lingering Potion\\\"\\\
5177 },\\\
5178 \\\"shield\\\": {\\\
5179 \\\"id\\\": 442,\\\
5180 \\\"name\\\": \\\"Shield\\\"\\\
5181 },\\\
5182 \\\"elytra\\\": {\\\
5183 \\\"id\\\": 443,\\\
5184 \\\"name\\\": \\\"Elytra\\\"\\\
5185 },\\\
5186 \\\"spruce_boat\\\": {\\\
5187 \\\"id\\\": 444,\\\
5188 \\\"name\\\": \\\"Spruce Boat\\\"\\\
5189 },\\\
5190 \\\"birch_boat\\\": {\\\
5191 \\\"id\\\": 445,\\\
5192 \\\"name\\\": \\\"Birch Boat\\\"\\\
5193 },\\\
5194 \\\"jungle_boat\\\": {\\\
5195 \\\"id\\\": 446,\\\
5196 \\\"name\\\": \\\"Jungle Boat\\\"\\\
5197 },\\\
5198 \\\"acacia_boat\\\": {\\\
5199 \\\"id\\\": 447,\\\
5200 \\\"name\\\": \\\"Acacia Boat\\\"\\\
5201 },\\\
5202 \\\"dark_oak_boat\\\": {\\\
5203 \\\"id\\\": 448,\\\
5204 \\\"name\\\": \\\"Dark Oak Boat\\\"\\\
5205 },\\\
5206 \\\"totem_of_undying\\\": {\\\
5207 \\\"id\\\": 449,\\\
5208 \\\"name\\\": \\\"Totem of Undying\\\"\\\
5209 },\\\
5210 \\\"shulker_shell\\\": {\\\
5211 \\\"id\\\": 450,\\\
5212 \\\"name\\\": \\\"Shulker Shell\\\"\\\
5213 },\\\
5214 \\\"iron_nugget\\\": {\\\
5215 \\\"id\\\": 452,\\\
5216 \\\"name\\\": \\\"Iron Nugget\\\"\\\
5217 },\\\
5218 \\\"record_13\\\": {\\\
5219 \\\"id\\\": 2256,\\\
5220 \\\"name\\\": \\\"13 Disc\\\"\\\
5221 },\\\
5222 \\\"record_cat\\\": {\\\
5223 \\\"id\\\": 2257,\\\
5224 \\\"name\\\": \\\"Cat Disc\\\"\\\
5225 }\\\
5226 \\\"record_blocks\\\": {\\\
5227 \\\"id\\\": 2258,\\\
5228 \\\"name\\\": \\\"Blocks Disc\\\"\\\
5229 },\\\
5230 \\\"record_chirp\\\": {\\\
5231 \\\"id\\\": 2259,\\\
5232 \\\"name\\\": \\\"Chirp Disc\\\"\\\
5233 },\\\
5234 \\\"record_far\\\": {\\\
5235 \\\"id\\\": 2260,\\\
5236 \\\"name\\\": \\\"Far Disc\\\"\\\
5237 },\\\
5238 \\\"record_mall\\\": {\\\
5239 \\\"id\\\": 2261,\\\
5240 \\\"name\\\": \\\"Mall Disc\\\"\\\
5241 },\\\
5242 \\\"record_mellohi\\\": {\\\
5243 \\\"id\\\": 2262,\\\
5244 \\\"name\\\": \\\"Mellohi Disc\\\"\\\
5245 },\\\
5246 \\\"record_stal\\\": {\\\
5247 \\\"id\\\": 2263,\\\
5248 \\\"name\\\": \\\"Stal Disc\\\"\\\
5249 },\\\
5250 \\\"record_strad\\\": {\\\
5251 \\\"id\\\": 2264,\\\
5252 \\\"name\\\": \\\"Strad Disc\\\"\\\
5253 },\\\
5254 \\\"record_ward\\\": {\\\
5255 \\\"id\\\": 2265,\\\
5256 \\\"name\\\": \\\"Ward Disc\\\"\\\
5257 },\\\
5258 \\\"record_11\\\": {\\\
5259 \\\"id\\\": 2266,\\\
5260 \\\"name\\\": \\\"11 Disc\\\"\\\
5261 },\\\
5262 \\\"record_wait\\\": {\\\
5263 \\\"id\\\": 2267,\\\
5264 \\\"name\\\": \\\"Wait Disc\\\"\\\
5265 },\\\
5266}\",\
5267 [ \"core/apis/chestAdapter.lua\" ] = \"local class = require('opus.class')\\\
5268local itemDB = require('core.itemDB')\\\
5269local Peripheral = require('opus.peripheral')\\\
5270local Util = require('opus.util')\\\
5271\\\
5272local os = _G.os\\\
5273\\\
5274local ChestAdapter = class()\\\
5275\\\
5276local convertNames = {\\\
5277 name = 'id',\\\
5278 damage = 'dmg',\\\
5279 maxCount = 'max_size',\\\
5280 count = 'qty',\\\
5281 displayName = 'display_name',\\\
5282 maxDamage = 'max_dmg',\\\
5283 nbtHash = 'nbt_hash',\\\
5284}\\\
5285\\\
5286-- Strip off color prefix\\\
5287local function safeString(text)\\\
5288\\\
5289 local val = text:byte(1)\\\
5290\\\
5291 if val < 32 or val > 128 then\\\
5292\\\
5293 local newText = {}\\\
5294 for i = 4, #text do\\\
5295 val = text:byte(i)\\\
5296 newText[i - 3] = (val > 31 and val < 127) and val or 63\\\
5297 end\\\
5298 return string.char(unpack(newText))\\\
5299 end\\\
5300\\\
5301 return text\\\
5302end\\\
5303\\\
5304local function convertItem(item)\\\
5305 for k,v in pairs(convertNames) do\\\
5306 item[k] = item[v]\\\
5307 item[v] = nil\\\
5308 end\\\
5309 item.displayName = safeString(item.displayName)\\\
5310end\\\
5311\\\
5312function ChestAdapter:init(args)\\\
5313 local defaults = {\\\
5314 name = 'chest',\\\
5315 }\\\
5316 Util.merge(self, defaults)\\\
5317 Util.merge(self, args)\\\
5318\\\
5319 local chest\\\
5320 if not self.side then\\\
5321 chest = Peripheral.getByMethod('getAllStacks')\\\
5322 else\\\
5323 chest = Peripheral.getBySide(self.side)\\\
5324 if chest and not chest.getAllStacks then\\\
5325 chest = nil\\\
5326 end\\\
5327 end\\\
5328\\\
5329 if chest then\\\
5330 Util.merge(self, chest)\\\
5331\\\
5332 if chest.listAvailableItems then\\\
5333 self.list = chest.listAvailableItems\\\
5334 end\\\
5335 end\\\
5336end\\\
5337\\\
5338function ChestAdapter:isValid()\\\
5339 return not not self.getAllStacks\\\
5340end\\\
5341\\\
5342function ChestAdapter:refresh(throttle)\\\
5343 return self:listItems(throttle)\\\
5344end\\\
5345\\\
5346-- provide a consolidated list of items\\\
5347function ChestAdapter:listItems(throttle)\\\
5348 local cache = { }\\\
5349 local items = { }\\\
5350 throttle = throttle or Util.throttle()\\\
5351\\\
5352 -- getAllStacks sometimes fails\\\
5353 local s, m = pcall(function()\\\
5354 for _,v in pairs(self.getAllStacks(false)) do\\\
5355 if v.qty > 0 then\\\
5356 convertItem(v)\\\
5357 local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')\\\
5358\\\
5359 local entry = cache[key]\\\
5360 if not entry then\\\
5361 entry = itemDB:get(v) or itemDB:add(v)\\\
5362 entry = Util.shallowCopy(entry)\\\
5363 entry.count = 0\\\
5364 cache[key] = entry\\\
5365 table.insert(items, entry)\\\
5366 end\\\
5367 entry.count = entry.count + v.count\\\
5368 throttle()\\\
5369 end\\\
5370 itemDB:flush()\\\
5371 end\\\
5372 end)\\\
5373 if s then\\\
5374 if not Util.empty(items) then\\\
5375 self.cache = cache\\\
5376 return items\\\
5377 end\\\
5378 else\\\
5379 _G._syslog(m)\\\
5380 end\\\
5381end\\\
5382\\\
5383function ChestAdapter:getItemInfo(item)\\\
5384 if not self.cache then\\\
5385 self:listItems()\\\
5386 end\\\
5387 local key = table.concat({ item.name, item.damage, item.nbtHash }, ':')\\\
5388 return self.cache[key]\\\
5389end\\\
5390\\\
5391function ChestAdapter:provide(item, qty, slot, direction)\\\
5392 pcall(function()\\\
5393 for key,stack in Util.rpairs(self.getAllStacks(false)) do\\\
5394 if stack.id == item.name and\\\
5395 (not item.damage or stack.dmg == item.damage) and\\\
5396 (not item.nbtHash or stack.nbt_hash == item.nbtHash) then\\\
5397 local amount = math.min(qty, stack.qty)\\\
5398 if amount > 0 then\\\
5399 self.pushItemIntoSlot(direction or self.direction, key, amount, slot)\\\
5400 end\\\
5401 qty = qty - amount\\\
5402 if qty <= 0 then\\\
5403 break\\\
5404 end\\\
5405 end\\\
5406 end\\\
5407 end)\\\
5408end\\\
5409\\\
5410function ChestAdapter:extract(slot, qty, toSlot)\\\
5411 if toSlot then\\\
5412 self.pushItemIntoSlot(self.direction, slot, qty, toSlot)\\\
5413 else\\\
5414 self.pushItem(self.direction, slot, qty)\\\
5415 end\\\
5416end\\\
5417\\\
5418function ChestAdapter:insert(slot, qty, toSlot)\\\
5419 -- toSlot not tested ...\\\
5420 local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot)\\\
5421 if not s and m then\\\
5422 os.sleep(1)\\\
5423 pcall(self.pullItem, self.direction, slot, qty, toSlot)\\\
5424 end\\\
5425end\\\
5426\\\
5427return ChestAdapter\",\
5428 [ \"monitor/.package\" ] = \"{\\\
5429 title = 'Various monitor related programs',\\\
5430 repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/monitor',\\\
5431 description = [[Mirror terminal to monitor, Monitor Window Manager (mwm), and more]],\\\
5432 license = 'MIT',\\\
5433}\",\
5434 [ \"common/etc/fstab\" ] = \"packages/common/ascii.lua urlfs http://pastebin.com/raw/u3kcnyjd\\\
5435packages/common/hexedit.lua urlfs https://pastebin.com/raw/Ds9ajsp4\\\
5436packages/common/colors.lua urlfs https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/ignore/colors.lua\\\
5437packages/common/cowsay.lua urlfs https://pastebin.com/raw/n00VQJsw\\\
5438packages/common/calc.lua urlfs https://pastebin.com/raw/nAinUn1h\\\
5439packages/common/write.lua urlfs https://pastebin.com/raw/RSyhCjqv\\\
5440packages/common/apis/debugger.lua urlfs https://raw.githubusercontent.com/slembcke/debugger.lua/master/debugger.lua\",\
5441 [ \"monitor/etc/apps.db\" ] = \"{\\\
5442 [ \\\"58ec8d6e36e346d9f42eb43935652e3e58e2c829\\\" ] = {\\\
5443 title = \\\"Mwm\\\",\\\
5444 category = \\\"Apps\\\",\\\
5445 icon = \\\"\\\\030f\\\\031f \\\\0304 \\\\010\\\\030f\\\\031dshell]\\\\0304\\\\0314 \\\\010\\\\0304\\\\031f \\\",\\\
5446 iconExt = \\\"\\\\030 \\\\031f\\\\0305\\\\031f\\\\155\\\\030f\\\\128\\\\031d\\\\152\\\\140\\\\030d\\\\031f\\\\151\\\\030f\\\\128\\\\128\\\\0304\\\\0314\\\\128\\\\010\\\\030 \\\\031f\\\\030f\\\\0315\\\\152\\\\129\\\\030d\\\\031f\\\\141\\\\030f\\\\031d\\\\153\\\\030d\\\\031f\\\\149\\\\030f\\\\031d\\\\131\\\\148\\\\0304\\\\0314\\\\128\\\\010\\\\030 \\\\031f\\\\0304\\\\031f\\\\131\\\\131\\\\131\\\\131\\\\131\\\\131\\\\131\\\\030e\\\\0314\\\\131\\\",\\\
5447 run = \\\"mwm.lua usr/config/mwm\\\",\\\
5448 },\\\
5449}\",\
5450 [ \"monitor/help/mirror.txt\" ] = \"Mirror the terminal or a program on a monitor. Monitor touch events are translated to mouse click events.\\\
5451\\\
5452Optional arguments:\\\
5453 -s : set monitor to .5 scaling\\\
5454 -r : resize the terminal to the size of the monitor\\\
5455 -e : run a program on the monitor\\\
5456\\\
5457If the -e argument is not used, the current terminal will be mirrored.\\\
5458\\\
5459Example:\\\
5460mirror -s -r -e edit /startup\",\
5461 [ \"minify/minifyDir.lua\" ] = \"local fs = _G.fs\\\
5462local shell = _ENV.shell\\\
5463\\\
5464local function recurse(path)\\\
5465 if fs.isDir(path) then\\\
5466 for _, v in pairs(fs.listEx(path)) do\\\
5467 if not v.isReadOnly then\\\
5468 recurse(fs.combine(path, v.name))\\\
5469 end\\\
5470 end\\\
5471 elseif path:match('%.lua$') and not fs.isReadOnly(path) then\\\
5472 local sz = fs.getSize(path)\\\
5473 shell.run('minify.lua minify ' .. path)\\\
5474 print(string.format('%s : %.2f%%', path, (sz - fs.getSize(path)) / sz * 100))\\\
5475 end\\\
5476end\\\
5477\\\
5478local path = ({ ... })[1] or error('Syntax: minifyDir PATH')\\\
5479\\\
5480path = fs.combine(path, '')\\\
5481if not fs.isDir(path) then\\\
5482 error('Invalid path')\\\
5483end\\\
5484\\\
5485recurse(path)\",\
5486 [ \"common/Events.lua\" ] = \"local Event = require('opus.event')\\\
5487local UI = require('opus.ui')\\\
5488local Util = require('opus.util')\\\
5489\\\
5490local multishell = _ENV.multishell\\\
5491local kernel = _G.kernel\\\
5492\\\
5493UI:configure('Events', ...)\\\
5494\\\
5495local page = UI.Page {\\\
5496 menuBar = UI.MenuBar {\\\
5497 buttons = {\\\
5498 { text = 'Filter', event = 'filter' },\\\
5499 { text = 'Reset', event = 'reset' },\\\
5500 { text = 'Pause ', event = 'toggle', name = 'pauseButton' },\\\
5501 },\\\
5502 },\\\
5503 grid = UI.ScrollingGrid {\\\
5504 y = 2,\\\
5505 columns = {\\\
5506 { key = 'event' },\\\
5507 { key = 'p1' },\\\
5508 { key = 'p2' },\\\
5509 { key = 'p3' },\\\
5510 { key = 'p4' },\\\
5511 { key = 'p5' },\\\
5512 },\\\
5513 autospace = true,\\\
5514 disableHeader = true,\\\
5515 getDisplayValues = function(_, row)\\\
5516 row = Util.shallowCopy(row)\\\
5517\\\
5518 local function tovalue(s)\\\
5519 if type(s) == 'table' then\\\
5520 return 'table'\\\
5521 end\\\
5522 return s\\\
5523 end\\\
5524\\\
5525 for k,v in pairs(row) do\\\
5526 row[k] = tovalue(v)\\\
5527 end\\\
5528\\\
5529 return row\\\
5530 end,\\\
5531 },\\\
5532 accelerators = {\\\
5533 f = 'filter',\\\
5534 p = 'toggle',\\\
5535 r = 'reset',\\\
5536 c = 'clear',\\\
5537 [ 'control-q' ] = 'quit',\\\
5538 },\\\
5539 filtered = { },\\\
5540 eventHandler = function(self, event)\\\
5541 if event.type == 'filter' then\\\
5542 local entry = self.grid:getSelected()\\\
5543 self.filtered[entry.event] = true\\\
5544\\\
5545 elseif event.type == 'toggle' then\\\
5546 self.paused = not self.paused\\\
5547 if self.paused then\\\
5548 self.menuBar.pauseButton.text = 'Resume'\\\
5549 else\\\
5550 self.menuBar.pauseButton.text = 'Pause '\\\
5551 end\\\
5552 self.menuBar:draw()\\\
5553\\\
5554 elseif event.type == 'grid_select' then\\\
5555 multishell.openTab(_ENV, {\\\
5556 path = 'sys/apps/Lua.lua',\\\
5557 args = { event.selected },\\\
5558 focused = true,\\\
5559 })\\\
5560\\\
5561 elseif event.type == 'reset' then\\\
5562 self.filtered = { }\\\
5563 self.grid:setValues({ })\\\
5564 self.grid:draw()\\\
5565 if self.paused then\\\
5566 self:emit({ type = 'toggle' })\\\
5567 end\\\
5568\\\
5569 elseif event.type == 'clear' then\\\
5570 self.grid:setValues({ })\\\
5571 self.grid:draw()\\\
5572\\\
5573 elseif event.type == 'quit' then\\\
5574 UI:quit()\\\
5575\\\
5576 else\\\
5577 return UI.Page.eventHandler(self, event)\\\
5578 end\\\
5579 return true\\\
5580 end,\\\
5581}\\\
5582\\\
5583local updated = false\\\
5584local timerId = os.startTimer(1)\\\
5585\\\
5586Event.addRoutine(function()\\\
5587 while true do\\\
5588 local _, id = os.pullEvent('timer')\\\
5589 if id == timerId then\\\
5590 if updated then\\\
5591 while #page.grid.values > 100 do -- page.grid.height do\\\
5592 table.remove(page.grid.values, 100) -- #page.grid.values)\\\
5593 end\\\
5594 updated = false\\\
5595 page.grid:update()\\\
5596 page.grid:draw()\\\
5597 page:sync()\\\
5598 end\\\
5599 timerId = os.startTimer(1)\\\
5600 end\\\
5601 end\\\
5602end)\\\
5603\\\
5604local hookFunction = function(event, e)\\\
5605 if not page.filtered[event] and not page.paused and not (event == 'timer' and e[1] == timerId) then\\\
5606 updated = true\\\
5607 table.insert(page.grid.values, 1, {\\\
5608 event = event,\\\
5609 p1 = e[1],\\\
5610 p2 = e[2],\\\
5611 p3 = e[3],\\\
5612 p4 = e[4],\\\
5613 p5 = e[5],\\\
5614 })\\\
5615 end\\\
5616end\\\
5617\\\
5618kernel.hook('*', hookFunction)\\\
5619\\\
5620UI:setPage(page)\\\
5621UI:start()\\\
5622\\\
5623kernel.unhook('*', hookFunction)\",\
5624 [ \"core/apis/chestAdapter18.lua\" ] = \"local class = require('opus.class')\\\
5625local Util = require('opus.util')\\\
5626local itemDB = require('core.itemDB')\\\
5627local Peripheral = require('opus.peripheral')\\\
5628\\\
5629local ChestAdapter = class()\\\
5630\\\
5631function ChestAdapter:init(args)\\\
5632 local defaults = {\\\
5633 name = 'chest',\\\
5634 adapter = 'ChestAdapter18'\\\
5635 }\\\
5636 Util.merge(self, defaults)\\\
5637 Util.merge(self, args)\\\
5638\\\
5639 local chest\\\
5640 if not self.side then\\\
5641 chest = Peripheral.getByMethod('list') or\\\
5642 Peripheral.getByMethod('listAvailableItems')\\\
5643 else\\\
5644 chest = Peripheral.getBySide(self.side)\\\
5645 if chest and not chest.list and not chest.listAvailableItems then\\\
5646 chest = nil\\\
5647 end\\\
5648 end\\\
5649\\\
5650 if chest then\\\
5651 Util.merge(self, chest)\\\
5652\\\
5653 if chest.listAvailableItems then\\\
5654 self.list = chest.listAvailableItems\\\
5655 end\\\
5656 end\\\
5657end\\\
5658\\\
5659function ChestAdapter:isValid()\\\
5660 return not not self.list\\\
5661end\\\
5662\\\
5663-- handle both AE/RS and generic inventory\\\
5664function ChestAdapter:getItemDetails(index, item)\\\
5665 if self.getItemMeta then\\\
5666 local s, detail = pcall(self.getItemMeta, index)\\\
5667 if not s or not detail or detail.name ~= item.name then\\\
5668 return\\\
5669 end\\\
5670 return detail\\\
5671 else\\\
5672 local detail = self.findItems(item)\\\
5673 if detail and #detail > 0 then\\\
5674 return detail[1].getMetadata()\\\
5675 end\\\
5676 end\\\
5677end\\\
5678\\\
5679function ChestAdapter:getCachedItemDetails(item, k)\\\
5680 local cached = itemDB:get(item)\\\
5681 if cached then\\\
5682 return cached\\\
5683 end\\\
5684\\\
5685 local detail = self:getItemDetails(k, item)\\\
5686 if detail then\\\
5687 return itemDB:add(detail)\\\
5688 end\\\
5689end\\\
5690\\\
5691function ChestAdapter:refresh(throttle)\\\
5692 return self:listItems(throttle)\\\
5693end\\\
5694\\\
5695-- provide a consolidated list of items\\\
5696function ChestAdapter:listItems(throttle)\\\
5697 for _ = 1, 5 do\\\
5698 local list = self:listItemsInternal(throttle)\\\
5699 if list then\\\
5700 return list\\\
5701 end\\\
5702 end\\\
5703 error('Error accessing inventory: ' .. self.direction)\\\
5704end\\\
5705\\\
5706function ChestAdapter:listItemsInternal(throttle)\\\
5707 local cache = { }\\\
5708 local items = { }\\\
5709 throttle = throttle or Util.throttle()\\\
5710\\\
5711 for k,v in pairs(self.list()) do\\\
5712 if v.count > 0 then\\\
5713 local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')\\\
5714\\\
5715 local entry = cache[key]\\\
5716 if not entry then\\\
5717 entry = self:getCachedItemDetails(v, k)\\\
5718 if not entry then\\\
5719 return -- Inventory has changed\\\
5720 end\\\
5721 entry = Util.shallowCopy(entry)\\\
5722 entry.count = 0\\\
5723 cache[key] = entry\\\
5724 table.insert(items, entry)\\\
5725 end\\\
5726\\\
5727 if entry then\\\
5728 entry.count = entry.count + v.count\\\
5729 end\\\
5730 throttle()\\\
5731 end\\\
5732 end\\\
5733 itemDB:flush()\\\
5734\\\
5735 self.cache = cache\\\
5736 return items\\\
5737end\\\
5738\\\
5739function ChestAdapter:getItemInfo(item)\\\
5740 if not self.cache then\\\
5741 self:listItems()\\\
5742 end\\\
5743 local key = table.concat({ item.name, item.damage, item.nbtHash }, ':')\\\
5744 local items = self.cache or { }\\\
5745 return items[key]\\\
5746end\\\
5747\\\
5748function ChestAdapter:getPercentUsed()\\\
5749 if self.cache and self.getDrawerCount then\\\
5750 return math.floor(Util.size(self.cache) / self.getDrawerCount() * 100)\\\
5751 end\\\
5752 return 0\\\
5753end\\\
5754\\\
5755function ChestAdapter:provide(item, qty, slot, direction)\\\
5756 local total = 0\\\
5757\\\
5758 local _, m = pcall(function()\\\
5759 local stacks = self.list()\\\
5760 for key,stack in Util.rpairs(stacks) do\\\
5761 if stack.name == item.name and\\\
5762 stack.damage == item.damage and\\\
5763 stack.nbtHash == item.nbtHash then\\\
5764 local amount = math.min(qty, stack.count)\\\
5765 if amount > 0 then\\\
5766 amount = self.pushItems(direction or self.direction, key, amount, slot)\\\
5767 end\\\
5768 qty = qty - amount\\\
5769 total = total + amount\\\
5770 if qty <= 0 then\\\
5771 break\\\
5772 end\\\
5773 end\\\
5774 end\\\
5775 end)\\\
5776 return total, m\\\
5777end\\\
5778\\\
5779function ChestAdapter:extract(slot, qty, toSlot, direction)\\\
5780 return self.pushItems(direction or self.direction, slot, qty, toSlot)\\\
5781end\\\
5782\\\
5783function ChestAdapter:insert(slot, qty, toSlot, direction)\\\
5784 return self.pullItems(direction or self.direction, slot, qty, toSlot)\\\
5785end\\\
5786\\\
5787return ChestAdapter\",\
5788 [ \"minify/minify.lua\" ] = \"--[[\\\
5789MIT License\\\
5790\\\
5791Copyright (c) 2017 Mark Langen\\\
5792\\\
5793Permission is hereby granted, free of charge, to any person obtaining a copy\\\
5794of this software and associated documentation files (the \\\"Software\\\"), to deal\\\
5795in the Software without restriction, including without limitation the rights\\\
5796to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\\\
5797copies of the Software, and to permit persons to whom the Software is\\\
5798furnished to do so, subject to the following conditions:\\\
5799\\\
5800The above copyright notice and this permission notice shall be included in all\\\
5801copies or substantial portions of the Software.\\\
5802\\\
5803THE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\\
5804IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\\
5805FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\\\
5806AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\\
5807LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\\\
5808OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\\\
5809SOFTWARE.\\\
5810]]\\\
5811\\\
5812local function lookupify(tb)\\\
5813 for _, v in pairs(tb) do\\\
5814 tb[v] = true\\\
5815 end\\\
5816 return tb\\\
5817end\\\
5818\\\
5819local function CountTable(tb)\\\
5820 local c = 0\\\
5821 for _ in pairs(tb) do c = c + 1 end\\\
5822 return c\\\
5823end\\\
5824\\\
5825local function FormatTableInt(tb, atIndent, ignoreFunc)\\\
5826 if tb.Print then\\\
5827 return tb.Print()\\\
5828 end\\\
5829 atIndent = atIndent or 0\\\
5830 local useNewlines = (CountTable(tb) > 1)\\\
5831 local baseIndent = string.rep(' ', atIndent+1)\\\
5832 local out = \\\"{\\\"..(useNewlines and '\\\\n' or '')\\\
5833 for k, v in pairs(tb) do\\\
5834 if type(v) ~= 'function' and not ignoreFunc(k) then\\\
5835 out = out..(useNewlines and baseIndent or '')\\\
5836 if type(k) == 'number' then\\\
5837 --nothing to do\\\
5838 elseif type(k) == 'string' and k:match(\\\"^[A-Za-z_][A-Za-z0-9_]*$\\\") then\\\
5839 out = out..k..\\\" = \\\"\\\
5840 elseif type(k) == 'string' then\\\
5841 out = out..\\\"[\\\\\\\"\\\"..k..\\\"\\\\\\\"] = \\\"\\\
5842 else\\\
5843 out = out..\\\"[\\\"..tostring(k)..\\\"] = \\\"\\\
5844 end\\\
5845 if type(v) == 'string' then\\\
5846 out = out..\\\"\\\\\\\"\\\"..v..\\\"\\\\\\\"\\\"\\\
5847 elseif type(v) == 'number' then\\\
5848 out = out..v\\\
5849 elseif type(v) == 'table' then\\\
5850 out = out..FormatTableInt(v, atIndent+(useNewlines and 1 or 0), ignoreFunc)\\\
5851 else\\\
5852 out = out..tostring(v)\\\
5853 end\\\
5854 if next(tb, k) then\\\
5855 out = out..\\\",\\\"\\\
5856 end\\\
5857 if useNewlines then\\\
5858 out = out..'\\\\n'\\\
5859 end\\\
5860 end\\\
5861 end\\\
5862 out = out..(useNewlines and string.rep(' ', atIndent) or '')..\\\"}\\\"\\\
5863 return out\\\
5864end\\\
5865\\\
5866local function FormatTable(tb, ignoreFunc)\\\
5867 ignoreFunc = ignoreFunc or function()\\\
5868 return false\\\
5869 end\\\
5870 return FormatTableInt(tb, 0, ignoreFunc)\\\
5871end\\\
5872\\\
5873local WhiteChars = lookupify{' ', '\\\\n', '\\\\t', '\\\\r'}\\\
5874\\\
5875local CharacterForEscape = {['r'] = '\\\\r', ['n'] = '\\\\n', ['t'] = '\\\\t', ['\\\"'] = '\\\"', [\\\"'\\\"] = \\\"'\\\", ['\\\\\\\\'] = '\\\\\\\\'}\\\
5876\\\
5877local AllIdentStartChars = lookupify{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',\\\
5878 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',\\\
5879 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',\\\
5880 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',\\\
5881 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',\\\
5882 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_'}\\\
5883\\\
5884local AllIdentChars = lookupify{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',\\\
5885 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',\\\
5886 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',\\\
5887 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',\\\
5888 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',\\\
5889 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_',\\\
5890 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}\\\
5891\\\
5892local Digits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}\\\
5893\\\
5894local HexDigits = lookupify{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\\\
5895 'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f'}\\\
5896\\\
5897local Symbols = lookupify{'+', '-', '*', '/', '^', '%', ',', '{', '}', '[', ']', '(', ')', ';', '#', '.', ':'}\\\
5898\\\
5899local EqualSymbols = lookupify{'~', '=', '>', '<'}\\\
5900\\\
5901local Keywords = lookupify{\\\
5902 'and', 'break', 'do', 'else', 'elseif',\\\
5903 'end', 'false', 'for', 'function', 'goto', 'if',\\\
5904 'in', 'local', 'nil', 'not', 'or', 'repeat',\\\
5905 'return', 'then', 'true', 'until', 'while',\\\
5906};\\\
5907\\\
5908local BlockFollowKeyword = lookupify{'else', 'elseif', 'until', 'end'}\\\
5909\\\
5910local UnopSet = lookupify{'-', 'not', '#'}\\\
5911\\\
5912local BinopSet = lookupify{\\\
5913 '+', '-', '*', '/', '%', '^', '#',\\\
5914 '..', '.', ':',\\\
5915 '>', '<', '<=', '>=', '~=', '==',\\\
5916 'and', 'or'\\\
5917}\\\
5918\\\
5919local BinaryPriority = {\\\
5920 ['+'] = {6, 6};\\\
5921 ['-'] = {6, 6};\\\
5922 ['*'] = {7, 7};\\\
5923 ['/'] = {7, 7};\\\
5924 ['%'] = {7, 7};\\\
5925 ['^'] = {10, 9};\\\
5926 ['..'] = {5, 4};\\\
5927 ['=='] = {3, 3};\\\
5928 ['~='] = {3, 3};\\\
5929 ['>'] = {3, 3};\\\
5930 ['<'] = {3, 3};\\\
5931 ['>='] = {3, 3};\\\
5932 ['<='] = {3, 3};\\\
5933 ['and'] = {2, 2};\\\
5934 ['or'] = {1, 1};\\\
5935};\\\
5936local UnaryPriority = 8\\\
5937\\\
5938-- Eof, Ident, Keyword, Number, String, Symbol\\\
5939\\\
5940local function CreateLuaTokenStream(text)\\\
5941 -- Tracking for the current position in the buffer, and\\\
5942 -- the current line / character we are on.\\\
5943 local p = 1\\\
5944 local length = #text\\\
5945\\\
5946 -- Output buffer for tokens\\\
5947 local tokenBuffer = {}\\\
5948\\\
5949 -- Get a character, or '' if at eof\\\
5950 local function look(n)\\\
5951 n = p + (n or 0)\\\
5952 if n <= length then\\\
5953 return text:sub(n, n)\\\
5954 else\\\
5955 return ''\\\
5956 end\\\
5957 end\\\
5958 local function get()\\\
5959 if p <= length then\\\
5960 local c = text:sub(p, p)\\\
5961 p = p + 1\\\
5962 return c\\\
5963 else\\\
5964 return ''\\\
5965 end\\\
5966 end\\\
5967\\\
5968 -- Error\\\
5969 local olderr = error\\\
5970 local function error(str)\\\
5971 local q = 1\\\
5972 local line = 1\\\
5973 local char = 1\\\
5974 while q <= p do\\\
5975 if text:sub(q, q) == '\\\\n' then\\\
5976 line = line + 1\\\
5977 char = 1\\\
5978 else\\\
5979 char = char + 1\\\
5980 end\\\
5981 q = q + 1\\\
5982 end\\\
5983 for _, token in pairs(tokenBuffer) do\\\
5984 --print(token.Type..\\\"<\\\"..token.Source..\\\">\\\")\\\
5985 end\\\
5986 olderr(\\\"file<\\\"..line..\\\":\\\"..char..\\\">: \\\"..str)\\\
5987 end\\\
5988\\\
5989 -- Consume a long data with equals count of `eqcount'\\\
5990 local function longdata(eqcount)\\\
5991 while true do\\\
5992 local c = get()\\\
5993 if c == '' then\\\
5994 error(\\\"Unfinished long string.\\\")\\\
5995 elseif c == ']' then\\\
5996 local done = true -- Until contested\\\
5997 for _ = 1, eqcount do\\\
5998 if look() == '=' then\\\
5999 p = p + 1\\\
6000 else\\\
6001 done = false\\\
6002 break\\\
6003 end\\\
6004 end\\\
6005 if done and get() == ']' then\\\
6006 return\\\
6007 end\\\
6008 end\\\
6009 end\\\
6010 end\\\
6011\\\
6012 -- Get the opening part for a long data `[` `=`* `[`\\\
6013 -- Precondition: The first `[` has been consumed\\\
6014 -- Return: nil or the equals count\\\
6015 local function getopen()\\\
6016 local startp = p\\\
6017 while look() == '=' do\\\
6018 p = p + 1\\\
6019 end\\\
6020 if look() == '[' then\\\
6021 p = p + 1\\\
6022 return p - startp - 1\\\
6023 else\\\
6024 p = startp\\\
6025 return nil\\\
6026 end\\\
6027 end\\\
6028\\\
6029 -- Add token\\\
6030 local whiteStart = 1\\\
6031 local tokenStart = 1\\\
6032 local function token(type)\\\
6033 local tk = {\\\
6034 Type = type;\\\
6035 LeadingWhite = text:sub(whiteStart, tokenStart-1);\\\
6036 Source = text:sub(tokenStart, p-1);\\\
6037 }\\\
6038 table.insert(tokenBuffer, tk)\\\
6039 whiteStart = p\\\
6040 tokenStart = p\\\
6041 return tk\\\
6042 end\\\
6043\\\
6044 -- Parse tokens loop\\\
6045 while true do\\\
6046 -- Mark the whitespace start\\\
6047 whiteStart = p\\\
6048\\\
6049 -- Get the leading whitespace + comments\\\
6050 while true do\\\
6051 local c = look()\\\
6052 if c == '' then\\\
6053 break\\\
6054 elseif c == '-' then\\\
6055 if look(1) == '-' then\\\
6056 p = p + 2\\\
6057 -- Consume comment body\\\
6058 if look() == '[' then\\\
6059 p = p + 1\\\
6060 local eqcount = getopen()\\\
6061 if eqcount then\\\
6062 -- Long comment body\\\
6063 longdata(eqcount)\\\
6064 else\\\
6065 -- Normal comment body\\\
6066 while true do\\\
6067 local c2 = get()\\\
6068 if c2 == '' or c2 == '\\\\n' then\\\
6069 break\\\
6070 end\\\
6071 end\\\
6072 end\\\
6073 else\\\
6074 -- Normal comment body\\\
6075 while true do\\\
6076 local c2 = get()\\\
6077 if c2 == '' or c2 == '\\\\n' then\\\
6078 break\\\
6079 end\\\
6080 end\\\
6081 end\\\
6082 else\\\
6083 break\\\
6084 end\\\
6085 elseif WhiteChars[c] then\\\
6086 p = p + 1\\\
6087 else\\\
6088 break\\\
6089 end\\\
6090 end\\\
6091 -- local leadingWhite = text:sub(whiteStart, p-1)\\\
6092\\\
6093 -- Mark the token start\\\
6094 tokenStart = p\\\
6095\\\
6096 -- Switch on token type\\\
6097 local c1 = get()\\\
6098 if c1 == '' then\\\
6099 -- End of file\\\
6100 token('Eof')\\\
6101 break\\\
6102 elseif c1 == '\\\\'' or c1 == '\\\\\\\"' then\\\
6103 -- String constant\\\
6104 while true do\\\
6105 local c2 = get()\\\
6106 if c2 == '\\\\\\\\' then\\\
6107 local c3 = get()\\\
6108 local esc = CharacterForEscape[c3]\\\
6109 if not esc then\\\
6110 error(\\\"Invalid Escape Sequence `\\\"..c3..\\\"`.\\\")\\\
6111 end\\\
6112 elseif c2 == c1 then\\\
6113 break\\\
6114 end\\\
6115 end\\\
6116 token('String')\\\
6117 elseif AllIdentStartChars[c1] then\\\
6118 -- Ident or Keyword\\\
6119 while AllIdentChars[look()] do\\\
6120 p = p + 1\\\
6121 end\\\
6122 if Keywords[text:sub(tokenStart, p-1)] then\\\
6123 token('Keyword')\\\
6124 else\\\
6125 token('Ident')\\\
6126 end\\\
6127 elseif Digits[c1] or (c1 == '.' and Digits[look()]) then\\\
6128 -- Number\\\
6129 if c1 == '0' and look() == 'x' then\\\
6130 p = p + 1\\\
6131 -- Hex number\\\
6132 while HexDigits[look()] do\\\
6133 p = p + 1\\\
6134 end\\\
6135 else\\\
6136 -- Normal Number\\\
6137 while Digits[look()] do\\\
6138 p = p + 1\\\
6139 end\\\
6140 if look() == '.' then\\\
6141 -- With decimal point\\\
6142 p = p + 1\\\
6143 while Digits[look()] do\\\
6144 p = p + 1\\\
6145 end\\\
6146 end\\\
6147 if look() == 'e' or look() == 'E' then\\\
6148 -- With exponent\\\
6149 p = p + 1\\\
6150 if look() == '-' then\\\
6151 p = p + 1\\\
6152 end\\\
6153 while Digits[look()] do\\\
6154 p = p + 1\\\
6155 end\\\
6156 end\\\
6157 end\\\
6158 token('Number')\\\
6159 elseif c1 == '[' then\\\
6160 -- '[' Symbol or Long String\\\
6161 local eqCount = getopen()\\\
6162 if eqCount then\\\
6163 -- Long string\\\
6164 longdata(eqCount)\\\
6165 token('String')\\\
6166 else\\\
6167 -- Symbol\\\
6168 token('Symbol')\\\
6169 end\\\
6170 elseif c1 == '.' then\\\
6171 -- Greedily consume up to 3 `.` for . / .. / ... tokens\\\
6172 if look() == '.' then\\\
6173 get()\\\
6174 if look() == '.' then\\\
6175 get()\\\
6176 end\\\
6177 end\\\
6178 token('Symbol')\\\
6179 elseif EqualSymbols[c1] then\\\
6180 if look() == '=' then\\\
6181 p = p + 1\\\
6182 end\\\
6183 token('Symbol')\\\
6184 elseif Symbols[c1] then\\\
6185 token('Symbol')\\\
6186 else\\\
6187 error(\\\"Bad symbol `\\\"..c1..\\\"` in source.\\\")\\\
6188 end\\\
6189 end\\\
6190 return tokenBuffer\\\
6191end\\\
6192\\\
6193local function CreateLuaParser(text)\\\
6194 -- Token stream and pointer into it\\\
6195 local tokens = CreateLuaTokenStream(text)\\\
6196 -- for _, tok in pairs(tokens) do\\\
6197 -- print(tok.Type..\\\": \\\"..tok.Source)\\\
6198 -- end\\\
6199 local p = 1\\\
6200\\\
6201 local function get()\\\
6202 local tok = tokens[p]\\\
6203 if p < #tokens then\\\
6204 p = p + 1\\\
6205 end\\\
6206 return tok\\\
6207 end\\\
6208 local function peek(n)\\\
6209 n = p + (n or 0)\\\
6210 return tokens[n] or tokens[#tokens]\\\
6211 end\\\
6212\\\
6213 local function getTokenStartPosition(token)\\\
6214 local line = 1\\\
6215 local char = 0\\\
6216 local tkNum = 1\\\
6217 while true do\\\
6218 local tk = tokens[tkNum]\\\
6219 if tk == token then\\\
6220 text = tk.LeadingWhite\\\
6221 else\\\
6222 text = tk.LeadingWhite..tk.Source\\\
6223 end\\\
6224 for i = 1, #text do\\\
6225 local c = text:sub(i, i)\\\
6226 if c == '\\\\n' then\\\
6227 line = line + 1\\\
6228 char = 0\\\
6229 else\\\
6230 char = char + 1\\\
6231 end\\\
6232 end\\\
6233 if tk == token then\\\
6234 break\\\
6235 end\\\
6236 tkNum = tkNum + 1\\\
6237 end\\\
6238 return line..\\\":\\\"..(char+1)\\\
6239 end\\\
6240 local function debugMark()\\\
6241 local tk = peek()\\\
6242 return \\\"<\\\"..tk.Type..\\\" `\\\"..tk.Source..\\\"`> at: \\\"..getTokenStartPosition(tk)\\\
6243 end\\\
6244\\\
6245 local function isBlockFollow()\\\
6246 local tok = peek()\\\
6247 return tok.Type == 'Eof' or (tok.Type == 'Keyword' and BlockFollowKeyword[tok.Source])\\\
6248 end\\\
6249 local function isUnop()\\\
6250 return UnopSet[peek().Source] or false\\\
6251 end\\\
6252 local function isBinop()\\\
6253 return BinopSet[peek().Source] or false\\\
6254 end\\\
6255 local function expect(type, source)\\\
6256 local tk = peek()\\\
6257 if tk.Type == type and (source == nil or tk.Source == source) then\\\
6258 return get()\\\
6259 else\\\
6260 for i = -3, 3 do\\\
6261 --print(\\\"Tokens[\\\"..i..\\\"] = `\\\"..peek(i).Source..\\\"`\\\")\\\
6262 end\\\
6263 if source then\\\
6264 error(getTokenStartPosition(tk)..\\\": `\\\"..source..\\\"` expected.\\\")\\\
6265 else\\\
6266 error(getTokenStartPosition(tk)..\\\": \\\"..type..\\\" expected.\\\")\\\
6267 end\\\
6268 end\\\
6269 end\\\
6270\\\
6271 local function MkNode(node)\\\
6272 local getf = node.GetFirstToken\\\
6273 local getl = node.GetLastToken\\\
6274 function node:GetFirstToken()\\\
6275 local t = getf(self)\\\
6276 assert(t)\\\
6277 return t\\\
6278 end\\\
6279 function node:GetLastToken()\\\
6280 local t = getl(self)\\\
6281 assert(t)\\\
6282 return t\\\
6283 end\\\
6284 return node\\\
6285 end\\\
6286\\\
6287 -- Forward decls\\\
6288 local block;\\\
6289 local expr;\\\
6290\\\
6291 -- Expression list\\\
6292 local function exprlist()\\\
6293 local exprList = {}\\\
6294 local commaList = {}\\\
6295 table.insert(exprList, expr())\\\
6296 while peek().Source == ',' do\\\
6297 table.insert(commaList, get())\\\
6298 table.insert(exprList, expr())\\\
6299 end\\\
6300 return exprList, commaList\\\
6301 end\\\
6302\\\
6303 local function prefixexpr()\\\
6304 local tk = peek()\\\
6305 if tk.Source == '(' then\\\
6306 local oparenTk = get()\\\
6307 local inner = expr()\\\
6308 local cparenTk = expect('Symbol', ')')\\\
6309 return MkNode{\\\
6310 Type = 'ParenExpr';\\\
6311 Expression = inner;\\\
6312 Token_OpenParen = oparenTk;\\\
6313 Token_CloseParen = cparenTk;\\\
6314 GetFirstToken = function(self)\\\
6315 return self.Token_OpenParen\\\
6316 end;\\\
6317 GetLastToken = function(self)\\\
6318 return self.Token_CloseParen\\\
6319 end;\\\
6320 }\\\
6321 elseif tk.Type == 'Ident' then\\\
6322 return MkNode{\\\
6323 Type = 'VariableExpr';\\\
6324 Token = get();\\\
6325 GetFirstToken = function(self)\\\
6326 return self.Token\\\
6327 end;\\\
6328 GetLastToken = function(self)\\\
6329 return self.Token\\\
6330 end;\\\
6331 }\\\
6332 else\\\
6333 print(debugMark())\\\
6334 error(getTokenStartPosition(tk)..\\\": Unexpected symbol\\\")\\\
6335 end\\\
6336 end\\\
6337\\\
6338 local function tableexpr()\\\
6339 local obrace = expect('Symbol', '{')\\\
6340 local entries = {}\\\
6341 local separators = {}\\\
6342 while peek().Source ~= '}' do\\\
6343 if peek().Source == '[' then\\\
6344 -- Index\\\
6345 local obrac = get()\\\
6346 local index = expr()\\\
6347 local cbrac = expect('Symbol', ']')\\\
6348 local eq = expect('Symbol', '=')\\\
6349 local value = expr()\\\
6350 table.insert(entries, {\\\
6351 EntryType = 'Index';\\\
6352 Index = index;\\\
6353 Value = value;\\\
6354 Token_OpenBracket = obrac;\\\
6355 Token_CloseBracket = cbrac;\\\
6356 Token_Equals = eq;\\\
6357 })\\\
6358 elseif peek().Type == 'Ident' and peek(1).Source == '=' then\\\
6359 -- Field\\\
6360 local field = get()\\\
6361 local eq = get()\\\
6362 local value = expr()\\\
6363 table.insert(entries, {\\\
6364 EntryType = 'Field';\\\
6365 Field = field;\\\
6366 Value = value;\\\
6367 Token_Equals = eq;\\\
6368 })\\\
6369 else\\\
6370 -- Value\\\
6371 local value = expr()\\\
6372 table.insert(entries, {\\\
6373 EntryType = 'Value';\\\
6374 Value = value;\\\
6375 })\\\
6376 end\\\
6377\\\
6378 -- Comma or Semicolon separator\\\
6379 if peek().Source == ',' or peek().Source == ';' then\\\
6380 table.insert(separators, get())\\\
6381 else\\\
6382 break\\\
6383 end\\\
6384 end\\\
6385 local cbrace = expect('Symbol', '}')\\\
6386 return MkNode{\\\
6387 Type = 'TableLiteral';\\\
6388 EntryList = entries;\\\
6389 Token_SeparatorList = separators;\\\
6390 Token_OpenBrace = obrace;\\\
6391 Token_CloseBrace = cbrace;\\\
6392 GetFirstToken = function(self)\\\
6393 return self.Token_OpenBrace\\\
6394 end;\\\
6395 GetLastToken = function(self)\\\
6396 return self.Token_CloseBrace\\\
6397 end;\\\
6398 }\\\
6399 end\\\
6400\\\
6401 -- List of identifiers\\\
6402 local function varlist()\\\
6403 local varList = {}\\\
6404 local commaList = {}\\\
6405 if peek().Type == 'Ident' then\\\
6406 table.insert(varList, get())\\\
6407 end\\\
6408 while peek().Source == ',' do\\\
6409 table.insert(commaList, get())\\\
6410 local id = expect('Ident')\\\
6411 table.insert(varList, id)\\\
6412 end\\\
6413 return varList, commaList\\\
6414 end\\\
6415\\\
6416 -- Body\\\
6417 local function blockbody(terminator)\\\
6418 local body = block()\\\
6419 local after = peek()\\\
6420 if after.Type == 'Keyword' and after.Source == terminator then\\\
6421 get()\\\
6422 return body, after\\\
6423 else\\\
6424 print(after.Type, after.Source)\\\
6425 error(getTokenStartPosition(after)..\\\": \\\"..terminator..\\\" expected.\\\")\\\
6426 end\\\
6427 end\\\
6428\\\
6429 -- Function declaration\\\
6430 local function funcdecl(isAnonymous)\\\
6431 local functionKw = get()\\\
6432 --\\\
6433 local nameChain;\\\
6434 local nameChainSeparator;\\\
6435 --\\\
6436 if not isAnonymous then\\\
6437 nameChain = {}\\\
6438 nameChainSeparator = {}\\\
6439 --\\\
6440 table.insert(nameChain, expect('Ident'))\\\
6441 --\\\
6442 while peek().Source == '.' do\\\
6443 table.insert(nameChainSeparator, get())\\\
6444 table.insert(nameChain, expect('Ident'))\\\
6445 end\\\
6446 if peek().Source == ':' then\\\
6447 table.insert(nameChainSeparator, get())\\\
6448 table.insert(nameChain, expect('Ident'))\\\
6449 end\\\
6450 end\\\
6451 --\\\
6452 local oparenTk = expect('Symbol', '(')\\\
6453 local argList, argCommaList = varlist()\\\
6454 local cparenTk = expect('Symbol', ')')\\\
6455 local fbody, enTk = blockbody('end')\\\
6456 --\\\
6457 return MkNode{\\\
6458 Type = (isAnonymous and 'FunctionLiteral' or 'FunctionStat');\\\
6459 NameChain = nameChain;\\\
6460 ArgList = argList;\\\
6461 Body = fbody;\\\
6462 --\\\
6463 Token_Function = functionKw;\\\
6464 Token_NameChainSeparator = nameChainSeparator;\\\
6465 Token_OpenParen = oparenTk;\\\
6466 Token_ArgCommaList = argCommaList;\\\
6467 Token_CloseParen = cparenTk;\\\
6468 Token_End = enTk;\\\
6469 GetFirstToken = function(self)\\\
6470 return self.Token_Function\\\
6471 end;\\\
6472 GetLastToken = function(self)\\\
6473 return self.Token_End;\\\
6474 end;\\\
6475 }\\\
6476 end\\\
6477\\\
6478 -- Argument list passed to a funciton\\\
6479 local function functionargs()\\\
6480 local tk = peek()\\\
6481 if tk.Source == '(' then\\\
6482 local oparenTk = get()\\\
6483 local argList = {}\\\
6484 local argCommaList = {}\\\
6485 while peek().Source ~= ')' do\\\
6486 table.insert(argList, expr())\\\
6487 if peek().Source == ',' then\\\
6488 table.insert(argCommaList, get())\\\
6489 else\\\
6490 break\\\
6491 end\\\
6492 end\\\
6493 local cparenTk = expect('Symbol', ')')\\\
6494 return MkNode{\\\
6495 CallType = 'ArgCall';\\\
6496 ArgList = argList;\\\
6497 --\\\
6498 Token_CommaList = argCommaList;\\\
6499 Token_OpenParen = oparenTk;\\\
6500 Token_CloseParen = cparenTk;\\\
6501 GetFirstToken = function(self)\\\
6502 return self.Token_OpenParen\\\
6503 end;\\\
6504 GetLastToken = function(self)\\\
6505 return self.Token_CloseParen\\\
6506 end;\\\
6507 }\\\
6508 elseif tk.Source == '{' then\\\
6509 return MkNode{\\\
6510 CallType = 'TableCall';\\\
6511 TableExpr = expr();\\\
6512 GetFirstToken = function(self)\\\
6513 return self.TableExpr:GetFirstToken()\\\
6514 end;\\\
6515 GetLastToken = function(self)\\\
6516 return self.TableExpr:GetLastToken()\\\
6517 end;\\\
6518 }\\\
6519 elseif tk.Type == 'String' then\\\
6520 return MkNode{\\\
6521 CallType = 'StringCall';\\\
6522 Token = get();\\\
6523 GetFirstToken = function(self)\\\
6524 return self.Token\\\
6525 end;\\\
6526 GetLastToken = function(self)\\\
6527 return self.Token\\\
6528 end;\\\
6529 }\\\
6530 else\\\
6531 error(\\\"Function arguments expected.\\\")\\\
6532 end\\\
6533 end\\\
6534\\\
6535 local function primaryexpr()\\\
6536 local base = prefixexpr()\\\
6537 assert(base, \\\"nil prefixexpr\\\")\\\
6538 while true do\\\
6539 local tk = peek()\\\
6540 if tk.Source == '.' then\\\
6541 local dotTk = get()\\\
6542 local fieldName = expect('Ident')\\\
6543 base = MkNode{\\\
6544 Type = 'FieldExpr';\\\
6545 Base = base;\\\
6546 Field = fieldName;\\\
6547 Token_Dot = dotTk;\\\
6548 GetFirstToken = function(self)\\\
6549 return self.Base:GetFirstToken()\\\
6550 end;\\\
6551 GetLastToken = function(self)\\\
6552 return self.Field\\\
6553 end;\\\
6554 }\\\
6555 elseif tk.Source == ':' then\\\
6556 local colonTk = get()\\\
6557 local methodName = expect('Ident')\\\
6558 local fargs = functionargs()\\\
6559 base = MkNode{\\\
6560 Type = 'MethodExpr';\\\
6561 Base = base;\\\
6562 Method = methodName;\\\
6563 FunctionArguments = fargs;\\\
6564 Token_Colon = colonTk;\\\
6565 GetFirstToken = function(self)\\\
6566 return self.Base:GetFirstToken()\\\
6567 end;\\\
6568 GetLastToken = function(self)\\\
6569 return self.FunctionArguments:GetLastToken()\\\
6570 end;\\\
6571 }\\\
6572 elseif tk.Source == '[' then\\\
6573 local obrac = get()\\\
6574 local index = expr()\\\
6575 local cbrac = expect('Symbol', ']')\\\
6576 base = MkNode{\\\
6577 Type = 'IndexExpr';\\\
6578 Base = base;\\\
6579 Index = index;\\\
6580 Token_OpenBracket = obrac;\\\
6581 Token_CloseBracket = cbrac;\\\
6582 GetFirstToken = function(self)\\\
6583 return self.Base:GetFirstToken()\\\
6584 end;\\\
6585 GetLastToken = function(self)\\\
6586 return self.Token_CloseBracket\\\
6587 end;\\\
6588 }\\\
6589 elseif tk.Source == '{' then\\\
6590 base = MkNode{\\\
6591 Type = 'CallExpr';\\\
6592 Base = base;\\\
6593 FunctionArguments = functionargs();\\\
6594 GetFirstToken = function(self)\\\
6595 return self.Base:GetFirstToken()\\\
6596 end;\\\
6597 GetLastToken = function(self)\\\
6598 return self.FunctionArguments:GetLastToken()\\\
6599 end;\\\
6600 }\\\
6601 elseif tk.Source == '(' then\\\
6602 base = MkNode{\\\
6603 Type = 'CallExpr';\\\
6604 Base = base;\\\
6605 FunctionArguments = functionargs();\\\
6606 GetFirstToken = function(self)\\\
6607 return self.Base:GetFirstToken()\\\
6608 end;\\\
6609 GetLastToken = function(self)\\\
6610 return self.FunctionArguments:GetLastToken()\\\
6611 end;\\\
6612 }\\\
6613 else\\\
6614 return base\\\
6615 end\\\
6616 end\\\
6617 end\\\
6618\\\
6619 local function simpleexpr()\\\
6620 local tk = peek()\\\
6621 if tk.Type == 'Number' then\\\
6622 return MkNode{\\\
6623 Type = 'NumberLiteral';\\\
6624 Token = get();\\\
6625 GetFirstToken = function(self)\\\
6626 return self.Token\\\
6627 end;\\\
6628 GetLastToken = function(self)\\\
6629 return self.Token\\\
6630 end;\\\
6631 }\\\
6632 elseif tk.Type == 'String' then\\\
6633 return MkNode{\\\
6634 Type = 'StringLiteral';\\\
6635 Token = get();\\\
6636 GetFirstToken = function(self)\\\
6637 return self.Token\\\
6638 end;\\\
6639 GetLastToken = function(self)\\\
6640 return self.Token\\\
6641 end;\\\
6642 }\\\
6643 elseif tk.Source == 'nil' then\\\
6644 return MkNode{\\\
6645 Type = 'NilLiteral';\\\
6646 Token = get();\\\
6647 GetFirstToken = function(self)\\\
6648 return self.Token\\\
6649 end;\\\
6650 GetLastToken = function(self)\\\
6651 return self.Token\\\
6652 end;\\\
6653 }\\\
6654 elseif tk.Source == 'true' or tk.Source == 'false' then\\\
6655 return MkNode{\\\
6656 Type = 'BooleanLiteral';\\\
6657 Token = get();\\\
6658 GetFirstToken = function(self)\\\
6659 return self.Token\\\
6660 end;\\\
6661 GetLastToken = function(self)\\\
6662 return self.Token\\\
6663 end;\\\
6664 }\\\
6665 elseif tk.Source == '...' then\\\
6666 return MkNode{\\\
6667 Type = 'VargLiteral';\\\
6668 Token = get();\\\
6669 GetFirstToken = function(self)\\\
6670 return self.Token\\\
6671 end;\\\
6672 GetLastToken = function(self)\\\
6673 return self.Token\\\
6674 end;\\\
6675 }\\\
6676 elseif tk.Source == '{' then\\\
6677 return tableexpr()\\\
6678 elseif tk.Source == 'function' then\\\
6679 return funcdecl(true)\\\
6680 else\\\
6681 return primaryexpr()\\\
6682 end\\\
6683 end\\\
6684\\\
6685 local function subexpr(limit)\\\
6686 local curNode;\\\
6687\\\
6688 -- Initial Base Expression\\\
6689 if isUnop() then\\\
6690 local opTk = get()\\\
6691 local ex = subexpr(UnaryPriority)\\\
6692 curNode = MkNode{\\\
6693 Type = 'UnopExpr';\\\
6694 Token_Op = opTk;\\\
6695 Rhs = ex;\\\
6696 GetFirstToken = function(self)\\\
6697 return self.Token_Op\\\
6698 end;\\\
6699 GetLastToken = function(self)\\\
6700 return self.Rhs:GetLastToken()\\\
6701 end;\\\
6702 }\\\
6703 else\\\
6704 curNode = simpleexpr()\\\
6705 assert(curNode, \\\"nil simpleexpr\\\")\\\
6706 end\\\
6707\\\
6708 -- Apply Precedence Recursion Chain\\\
6709 while isBinop() and BinaryPriority[peek().Source][1] > limit do\\\
6710 local opTk = get()\\\
6711 local rhs = subexpr(BinaryPriority[opTk.Source][2])\\\
6712 assert(rhs, \\\"RhsNeeded\\\")\\\
6713 curNode = MkNode{\\\
6714 Type = 'BinopExpr';\\\
6715 Lhs = curNode;\\\
6716 Rhs = rhs;\\\
6717 Token_Op = opTk;\\\
6718 GetFirstToken = function(self)\\\
6719 return self.Lhs:GetFirstToken()\\\
6720 end;\\\
6721 GetLastToken = function(self)\\\
6722 return self.Rhs:GetLastToken()\\\
6723 end;\\\
6724 }\\\
6725 end\\\
6726\\\
6727 -- Return result\\\
6728 return curNode\\\
6729 end\\\
6730\\\
6731 -- Expression\\\
6732 expr = function()\\\
6733 return subexpr(0)\\\
6734 end\\\
6735\\\
6736 -- Expression statement\\\
6737 local function exprstat()\\\
6738 local ex = primaryexpr()\\\
6739 if ex.Type == 'MethodExpr' or ex.Type == 'CallExpr' then\\\
6740 -- all good, calls can be statements\\\
6741 return MkNode{\\\
6742 Type = 'CallExprStat';\\\
6743 Expression = ex;\\\
6744 GetFirstToken = function(self)\\\
6745 return self.Expression:GetFirstToken()\\\
6746 end;\\\
6747 GetLastToken = function(self)\\\
6748 return self.Expression:GetLastToken()\\\
6749 end;\\\
6750 }\\\
6751 else\\\
6752 -- Assignment expr\\\
6753 local lhs = {ex}\\\
6754 local lhsSeparator = {}\\\
6755 while peek().Source == ',' do\\\
6756 table.insert(lhsSeparator, get())\\\
6757 local lhsPart = primaryexpr()\\\
6758 if lhsPart.Type == 'MethodExpr' or lhsPart.Type == 'CallExpr' then\\\
6759 error(\\\"Bad left hand side of assignment\\\")\\\
6760 end\\\
6761 table.insert(lhs, lhsPart)\\\
6762 end\\\
6763 local eq = expect('Symbol', '=')\\\
6764 local rhs = {expr()}\\\
6765 local rhsSeparator = {}\\\
6766 while peek().Source == ',' do\\\
6767 table.insert(rhsSeparator, get())\\\
6768 table.insert(rhs, expr())\\\
6769 end\\\
6770 return MkNode{\\\
6771 Type = 'AssignmentStat';\\\
6772 Rhs = rhs;\\\
6773 Lhs = lhs;\\\
6774 Token_Equals = eq;\\\
6775 Token_LhsSeparatorList = lhsSeparator;\\\
6776 Token_RhsSeparatorList = rhsSeparator;\\\
6777 GetFirstToken = function(self)\\\
6778 return self.Lhs[1]:GetFirstToken()\\\
6779 end;\\\
6780 GetLastToken = function(self)\\\
6781 return self.Rhs[#self.Rhs]:GetLastToken()\\\
6782 end;\\\
6783 }\\\
6784 end\\\
6785 end\\\
6786\\\
6787 -- If statement\\\
6788 local function ifstat()\\\
6789 local ifKw = get()\\\
6790 local condition = expr()\\\
6791 local thenKw = expect('Keyword', 'then')\\\
6792 local ifBody = block()\\\
6793 local elseClauses = {}\\\
6794 while peek().Source == 'elseif' or peek().Source == 'else' do\\\
6795 local elseifKw = get()\\\
6796 local elseifCondition, elseifThenKw;\\\
6797 if elseifKw.Source == 'elseif' then\\\
6798 elseifCondition = expr()\\\
6799 elseifThenKw = expect('Keyword', 'then')\\\
6800 end\\\
6801 local elseifBody = block()\\\
6802 table.insert(elseClauses, {\\\
6803 Condition = elseifCondition;\\\
6804 Body = elseifBody;\\\
6805 --\\\
6806 ClauseType = elseifKw.Source;\\\
6807 Token = elseifKw;\\\
6808 Token_Then = elseifThenKw;\\\
6809 })\\\
6810 if elseifKw.Source == 'else' then\\\
6811 break\\\
6812 end\\\
6813 end\\\
6814 local enKw = expect('Keyword', 'end')\\\
6815 return MkNode{\\\
6816 Type = 'IfStat';\\\
6817 Condition = condition;\\\
6818 Body = ifBody;\\\
6819 ElseClauseList = elseClauses;\\\
6820 --\\\
6821 Token_If = ifKw;\\\
6822 Token_Then = thenKw;\\\
6823 Token_End = enKw;\\\
6824 GetFirstToken = function(self)\\\
6825 return self.Token_If\\\
6826 end;\\\
6827 GetLastToken = function(self)\\\
6828 return self.Token_End\\\
6829 end;\\\
6830 }\\\
6831 end\\\
6832\\\
6833 -- Do statement\\\
6834 local function dostat()\\\
6835 local doKw = get()\\\
6836 local body, enKw = blockbody('end')\\\
6837 --\\\
6838 return MkNode{\\\
6839 Type = 'DoStat';\\\
6840 Body = body;\\\
6841 --\\\
6842 Token_Do = doKw;\\\
6843 Token_End = enKw;\\\
6844 GetFirstToken = function(self)\\\
6845 return self.Token_Do\\\
6846 end;\\\
6847 GetLastToken = function(self)\\\
6848 return self.Token_End\\\
6849 end;\\\
6850 }\\\
6851 end\\\
6852\\\
6853 -- While statement\\\
6854 local function whilestat()\\\
6855 local whileKw = get()\\\
6856 local condition = expr()\\\
6857 local doKw = expect('Keyword', 'do')\\\
6858 local body, enKw = blockbody('end')\\\
6859 --\\\
6860 return MkNode{\\\
6861 Type = 'WhileStat';\\\
6862 Condition = condition;\\\
6863 Body = body;\\\
6864 --\\\
6865 Token_While = whileKw;\\\
6866 Token_Do = doKw;\\\
6867 Token_End = enKw;\\\
6868 GetFirstToken = function(self)\\\
6869 return self.Token_While\\\
6870 end;\\\
6871 GetLastToken = function(self)\\\
6872 return self.Token_End\\\
6873 end;\\\
6874 }\\\
6875 end\\\
6876\\\
6877 -- For statement\\\
6878 local function forstat()\\\
6879 local forKw = get()\\\
6880 local loopVars, loopVarCommas = varlist()\\\
6881 --local node = {}\\\
6882 if peek().Source == '=' then\\\
6883 local eqTk = get()\\\
6884 local exprList, exprCommaList = exprlist()\\\
6885 if #exprList < 2 or #exprList > 3 then\\\
6886 error(\\\"expected 2 or 3 values for range bounds\\\")\\\
6887 end\\\
6888 local doTk = expect('Keyword', 'do')\\\
6889 local body, enTk = blockbody('end')\\\
6890 return MkNode{\\\
6891 Type = 'NumericForStat';\\\
6892 VarList = loopVars;\\\
6893 RangeList = exprList;\\\
6894 Body = body;\\\
6895 --\\\
6896 Token_For = forKw;\\\
6897 Token_VarCommaList = loopVarCommas;\\\
6898 Token_Equals = eqTk;\\\
6899 Token_RangeCommaList = exprCommaList;\\\
6900 Token_Do = doTk;\\\
6901 Token_End = enTk;\\\
6902 GetFirstToken = function(self)\\\
6903 return self.Token_For\\\
6904 end;\\\
6905 GetLastToken = function(self)\\\
6906 return self.Token_End\\\
6907 end;\\\
6908 }\\\
6909 elseif peek().Source == 'in' then\\\
6910 local inTk = get()\\\
6911 local exprList, exprCommaList = exprlist()\\\
6912 local doTk = expect('Keyword', 'do')\\\
6913 local body, enTk = blockbody('end')\\\
6914 return MkNode{\\\
6915 Type = 'GenericForStat';\\\
6916 VarList = loopVars;\\\
6917 GeneratorList = exprList;\\\
6918 Body = body;\\\
6919 --\\\
6920 Token_For = forKw;\\\
6921 Token_VarCommaList = loopVarCommas;\\\
6922 Token_In = inTk;\\\
6923 Token_GeneratorCommaList = exprCommaList;\\\
6924 Token_Do = doTk;\\\
6925 Token_End = enTk;\\\
6926 GetFirstToken = function(self)\\\
6927 return self.Token_For\\\
6928 end;\\\
6929 GetLastToken = function(self)\\\
6930 return self.Token_End\\\
6931 end;\\\
6932 }\\\
6933 else\\\
6934 error(\\\"`=` or in expected\\\")\\\
6935 end\\\
6936 end\\\
6937\\\
6938 -- Repeat statement\\\
6939 local function repeatstat()\\\
6940 local repeatKw = get()\\\
6941 local body, untilTk = blockbody('until')\\\
6942 local condition = expr()\\\
6943 return MkNode{\\\
6944 Type = 'RepeatStat';\\\
6945 Body = body;\\\
6946 Condition = condition;\\\
6947 --\\\
6948 Token_Repeat = repeatKw;\\\
6949 Token_Until = untilTk;\\\
6950 GetFirstToken = function(self)\\\
6951 return self.Token_Repeat\\\
6952 end;\\\
6953 GetLastToken = function(self)\\\
6954 return self.Condition:GetLastToken()\\\
6955 end;\\\
6956 }\\\
6957 end\\\
6958\\\
6959 -- Local var declaration\\\
6960 local function localdecl()\\\
6961 local localKw = get()\\\
6962 if peek().Source == 'function' then\\\
6963 -- Local function def\\\
6964 local funcStat = funcdecl(false)\\\
6965 if #funcStat.NameChain > 1 then\\\
6966 error(getTokenStartPosition(funcStat.Token_NameChainSeparator[1])..\\\": `(` expected.\\\")\\\
6967 end\\\
6968 return MkNode{\\\
6969 Type = 'LocalFunctionStat';\\\
6970 FunctionStat = funcStat;\\\
6971 Token_Local = localKw;\\\
6972 GetFirstToken = function(self)\\\
6973 return self.Token_Local\\\
6974 end;\\\
6975 GetLastToken = function(self)\\\
6976 return self.FunctionStat:GetLastToken()\\\
6977 end;\\\
6978 }\\\
6979 elseif peek().Type == 'Ident' then\\\
6980 -- Local variable declaration\\\
6981 local varList, varCommaList = varlist()\\\
6982 local exprList, exprCommaList = {}, {}\\\
6983 local eqToken;\\\
6984 if peek().Source == '=' then\\\
6985 eqToken = get()\\\
6986 exprList, exprCommaList = exprlist()\\\
6987 end\\\
6988 return MkNode{\\\
6989 Type = 'LocalVarStat';\\\
6990 VarList = varList;\\\
6991 ExprList = exprList;\\\
6992 Token_Local = localKw;\\\
6993 Token_Equals = eqToken;\\\
6994 Token_VarCommaList = varCommaList;\\\
6995 Token_ExprCommaList = exprCommaList;\\\
6996 GetFirstToken = function(self)\\\
6997 return self.Token_Local\\\
6998 end;\\\
6999 GetLastToken = function(self)\\\
7000 if #self.ExprList > 0 then\\\
7001 return self.ExprList[#self.ExprList]:GetLastToken()\\\
7002 else\\\
7003 return self.VarList[#self.VarList]\\\
7004 end\\\
7005 end;\\\
7006 }\\\
7007 else\\\
7008 error(\\\"`function` or ident expected\\\")\\\
7009 end\\\
7010 end\\\
7011\\\
7012 -- Return statement\\\
7013 local function retstat()\\\
7014 local returnKw = get()\\\
7015 local exprList;\\\
7016 local commaList;\\\
7017 if isBlockFollow() or peek().Source == ';' then\\\
7018 exprList = {}\\\
7019 commaList = {}\\\
7020 else\\\
7021 exprList, commaList = exprlist()\\\
7022 end\\\
7023 return {\\\
7024 Type = 'ReturnStat';\\\
7025 ExprList = exprList;\\\
7026 Token_Return = returnKw;\\\
7027 Token_CommaList = commaList;\\\
7028 GetFirstToken = function(self)\\\
7029 return self.Token_Return\\\
7030 end;\\\
7031 GetLastToken = function(self)\\\
7032 if #self.ExprList > 0 then\\\
7033 return self.ExprList[#self.ExprList]:GetLastToken()\\\
7034 else\\\
7035 return self.Token_Return\\\
7036 end\\\
7037 end;\\\
7038 }\\\
7039 end\\\
7040\\\
7041 -- Break statement\\\
7042 local function breakstat()\\\
7043 local breakKw = get()\\\
7044 return {\\\
7045 Type = 'BreakStat';\\\
7046 Token_Break = breakKw;\\\
7047 GetFirstToken = function(self)\\\
7048 return self.Token_Break\\\
7049 end;\\\
7050 GetLastToken = function(self)\\\
7051 return self.Token_Break\\\
7052 end;\\\
7053 }\\\
7054 end\\\
7055\\\
7056 -- Expression\\\
7057 local function statement()\\\
7058 local tok = peek()\\\
7059 if tok.Source == 'if' then\\\
7060 return false, ifstat()\\\
7061 elseif tok.Source == 'while' then\\\
7062 return false, whilestat()\\\
7063 elseif tok.Source == 'do' then\\\
7064 return false, dostat()\\\
7065 elseif tok.Source == 'for' then\\\
7066 return false, forstat()\\\
7067 elseif tok.Source == 'repeat' then\\\
7068 return false, repeatstat()\\\
7069 elseif tok.Source == 'function' then\\\
7070 return false, funcdecl(false)\\\
7071 elseif tok.Source == 'local' then\\\
7072 return false, localdecl()\\\
7073 elseif tok.Source == 'return' then\\\
7074 return true, retstat()\\\
7075 elseif tok.Source == 'break' then\\\
7076 return true, breakstat()\\\
7077 else\\\
7078 return false, exprstat()\\\
7079 end\\\
7080 end\\\
7081\\\
7082 -- Chunk\\\
7083 block = function()\\\
7084 local statements = {}\\\
7085 local semicolons = {}\\\
7086 local isLast = false\\\
7087 while not isLast and not isBlockFollow() do\\\
7088 -- Parse statement\\\
7089 local stat;\\\
7090 isLast, stat = statement()\\\
7091 table.insert(statements, stat)\\\
7092 local next = peek()\\\
7093 if next.Type == 'Symbol' and next.Source == ';' then\\\
7094 semicolons[#statements] = get()\\\
7095 end\\\
7096 end\\\
7097 return {\\\
7098 Type = 'StatList';\\\
7099 StatementList = statements;\\\
7100 SemicolonList = semicolons;\\\
7101 GetFirstToken = function(self)\\\
7102 if #self.StatementList == 0 then\\\
7103 return nil\\\
7104 else\\\
7105 return self.StatementList[1]:GetFirstToken()\\\
7106 end\\\
7107 end;\\\
7108 GetLastToken = function(self)\\\
7109 if #self.StatementList == 0 then\\\
7110 return nil\\\
7111 elseif self.SemicolonList[#self.StatementList] then\\\
7112 -- Last token may be one of the semicolon separators\\\
7113 return self.SemicolonList[#self.StatementList]\\\
7114 else\\\
7115 return self.StatementList[#self.StatementList]:GetLastToken()\\\
7116 end\\\
7117 end;\\\
7118 }\\\
7119 end\\\
7120\\\
7121 return block()\\\
7122end\\\
7123\\\
7124local function VisitAst(ast, visitors)\\\
7125 local ExprType = lookupify{\\\
7126 'BinopExpr'; 'UnopExpr';\\\
7127 'NumberLiteral'; 'StringLiteral'; 'NilLiteral'; 'BooleanLiteral'; 'VargLiteral';\\\
7128 'FieldExpr'; 'IndexExpr';\\\
7129 'MethodExpr'; 'CallExpr';\\\
7130 'FunctionLiteral';\\\
7131 'VariableExpr';\\\
7132 'ParenExpr';\\\
7133 'TableLiteral';\\\
7134 }\\\
7135\\\
7136 local StatType = lookupify{\\\
7137 'StatList';\\\
7138 'BreakStat';\\\
7139 'ReturnStat';\\\
7140 'LocalVarStat';\\\
7141 'LocalFunctionStat';\\\
7142 'FunctionStat';\\\
7143 'RepeatStat';\\\
7144 'GenericForStat';\\\
7145 'NumericForStat';\\\
7146 'WhileStat';\\\
7147 'DoStat';\\\
7148 'IfStat';\\\
7149 'CallExprStat';\\\
7150 'AssignmentStat';\\\
7151 }\\\
7152\\\
7153 -- Check for typos in visitor construction\\\
7154 for visitorSubject in pairs(visitors) do\\\
7155 if not StatType[visitorSubject] and not ExprType[visitorSubject] then\\\
7156 error(\\\"Invalid visitor target: `\\\"..visitorSubject..\\\"`\\\")\\\
7157 end\\\
7158 end\\\
7159\\\
7160 -- Helpers to call visitors on a node\\\
7161 local function preVisit(exprOrStat)\\\
7162 local visitor = visitors[exprOrStat.Type]\\\
7163 if type(visitor) == 'function' then\\\
7164 return visitor(exprOrStat)\\\
7165 elseif visitor and visitor.Pre then\\\
7166 return visitor.Pre(exprOrStat)\\\
7167 end\\\
7168 end\\\
7169 local function postVisit(exprOrStat)\\\
7170 local visitor = visitors[exprOrStat.Type]\\\
7171 if visitor and type(visitor) == 'table' and visitor.Post then\\\
7172 return visitor.Post(exprOrStat)\\\
7173 end\\\
7174 end\\\
7175\\\
7176 local visitExpr, visitStat;\\\
7177\\\
7178 visitExpr = function(expr)\\\
7179 if preVisit(expr) then\\\
7180 -- Handler did custom child iteration or blocked child iteration\\\
7181 return\\\
7182 end\\\
7183 if expr.Type == 'BinopExpr' then\\\
7184 visitExpr(expr.Lhs)\\\
7185 visitExpr(expr.Rhs)\\\
7186 elseif expr.Type == 'UnopExpr' then\\\
7187 visitExpr(expr.Rhs)\\\
7188 elseif expr.Type == 'NumberLiteral' or expr.Type == 'StringLiteral' or\\\
7189 expr.Type == 'NilLiteral' or expr.Type == 'BooleanLiteral' or\\\
7190 expr.Type == 'VargLiteral'\\\
7191 then\\\
7192 -- No children to visit, single token literals\\\
7193 elseif expr.Type == 'FieldExpr' then\\\
7194 visitExpr(expr.Base)\\\
7195 elseif expr.Type == 'IndexExpr' then\\\
7196 visitExpr(expr.Base)\\\
7197 visitExpr(expr.Index)\\\
7198 elseif expr.Type == 'MethodExpr' or expr.Type == 'CallExpr' then\\\
7199 visitExpr(expr.Base)\\\
7200 if expr.FunctionArguments.CallType == 'ArgCall' then\\\
7201 for _, argExpr in pairs(expr.FunctionArguments.ArgList) do\\\
7202 visitExpr(argExpr)\\\
7203 end\\\
7204 elseif expr.FunctionArguments.CallType == 'TableCall' then\\\
7205 visitExpr(expr.FunctionArguments.TableExpr)\\\
7206 end\\\
7207 elseif expr.Type == 'FunctionLiteral' then\\\
7208 visitStat(expr.Body)\\\
7209 elseif expr.Type == 'VariableExpr' then\\\
7210 -- No children to visit\\\
7211 elseif expr.Type == 'ParenExpr' then\\\
7212 visitExpr(expr.Expression)\\\
7213 elseif expr.Type == 'TableLiteral' then\\\
7214 for _, entry in pairs(expr.EntryList) do\\\
7215 if entry.EntryType == 'Field' then\\\
7216 visitExpr(entry.Value)\\\
7217 elseif entry.EntryType == 'Index' then\\\
7218 visitExpr(entry.Index)\\\
7219 visitExpr(entry.Value)\\\
7220 elseif entry.EntryType == 'Value' then\\\
7221 visitExpr(entry.Value)\\\
7222 else\\\
7223 assert(false, \\\"unreachable\\\")\\\
7224 end\\\
7225 end\\\
7226 else\\\
7227 assert(false, \\\"unreachable, type: \\\"..expr.Type..\\\":\\\"..FormatTable(expr))\\\
7228 end\\\
7229 postVisit(expr)\\\
7230 end\\\
7231\\\
7232 visitStat = function(stat)\\\
7233 if preVisit(stat) then\\\
7234 -- Handler did custom child iteration or blocked child iteration\\\
7235 return\\\
7236 end\\\
7237 if stat.Type == 'StatList' then\\\
7238 for _, ch in pairs(stat.StatementList) do\\\
7239 visitStat(ch)\\\
7240 end\\\
7241 elseif stat.Type == 'BreakStat' then\\\
7242 -- No children to visit\\\
7243 elseif stat.Type == 'ReturnStat' then\\\
7244 for _, expr in pairs(stat.ExprList) do\\\
7245 visitExpr(expr)\\\
7246 end\\\
7247 elseif stat.Type == 'LocalVarStat' then\\\
7248 if stat.Token_Equals then\\\
7249 for _, expr in pairs(stat.ExprList) do\\\
7250 visitExpr(expr)\\\
7251 end\\\
7252 end\\\
7253 elseif stat.Type == 'LocalFunctionStat' then\\\
7254 visitStat(stat.FunctionStat.Body)\\\
7255 elseif stat.Type == 'FunctionStat' then\\\
7256 visitStat(stat.Body)\\\
7257 elseif stat.Type == 'RepeatStat' then\\\
7258 visitStat(stat.Body)\\\
7259 visitExpr(stat.Condition)\\\
7260 elseif stat.Type == 'GenericForStat' then\\\
7261 for _, expr in pairs(stat.GeneratorList) do\\\
7262 visitExpr(expr)\\\
7263 end\\\
7264 visitStat(stat.Body)\\\
7265 elseif stat.Type == 'NumericForStat' then\\\
7266 for _, expr in pairs(stat.RangeList) do\\\
7267 visitExpr(expr)\\\
7268 end\\\
7269 visitStat(stat.Body)\\\
7270 elseif stat.Type == 'WhileStat' then\\\
7271 visitExpr(stat.Condition)\\\
7272 visitStat(stat.Body)\\\
7273 elseif stat.Type == 'DoStat' then\\\
7274 visitStat(stat.Body)\\\
7275 elseif stat.Type == 'IfStat' then\\\
7276 visitExpr(stat.Condition)\\\
7277 visitStat(stat.Body)\\\
7278 for _, clause in pairs(stat.ElseClauseList) do\\\
7279 if clause.Condition then\\\
7280 visitExpr(clause.Condition)\\\
7281 end\\\
7282 visitStat(clause.Body)\\\
7283 end\\\
7284 elseif stat.Type == 'CallExprStat' then\\\
7285 visitExpr(stat.Expression)\\\
7286 elseif stat.Type == 'AssignmentStat' then\\\
7287 for _, ex in pairs(stat.Lhs) do\\\
7288 visitExpr(ex)\\\
7289 end\\\
7290 for _, ex in pairs(stat.Rhs) do\\\
7291 visitExpr(ex)\\\
7292 end\\\
7293 else\\\
7294 assert(false, \\\"unreachable\\\")\\\
7295 end\\\
7296 postVisit(stat)\\\
7297 end\\\
7298\\\
7299 if StatType[ast.Type] then\\\
7300 visitStat(ast)\\\
7301 else\\\
7302 visitExpr(ast)\\\
7303 end\\\
7304end\\\
7305\\\
7306local function AddVariableInfo(ast)\\\
7307 local globalVars = {}\\\
7308 local currentScope = nil\\\
7309\\\
7310 -- Numbering generator for variable lifetimes\\\
7311 local locationGenerator = 0\\\
7312 local function markLocation()\\\
7313 locationGenerator = locationGenerator + 1\\\
7314 return locationGenerator\\\
7315 end\\\
7316\\\
7317 -- Scope management\\\
7318 local function pushScope()\\\
7319 currentScope = {\\\
7320 ParentScope = currentScope;\\\
7321 ChildScopeList = {};\\\
7322 VariableList = {};\\\
7323 BeginLocation = markLocation();\\\
7324 }\\\
7325 if currentScope.ParentScope then\\\
7326 currentScope.Depth = currentScope.ParentScope.Depth + 1\\\
7327 table.insert(currentScope.ParentScope.ChildScopeList, currentScope)\\\
7328 else\\\
7329 currentScope.Depth = 1\\\
7330 end\\\
7331 function currentScope:GetVar(varName)\\\
7332 for _, var in pairs(self.VariableList) do\\\
7333 if var.Name == varName then\\\
7334 return var\\\
7335 end\\\
7336 end\\\
7337 if self.ParentScope then\\\
7338 return self.ParentScope:GetVar(varName)\\\
7339 else\\\
7340 for _, var in pairs(globalVars) do\\\
7341 if var.Name == varName then\\\
7342 return var\\\
7343 end\\\
7344 end\\\
7345 end\\\
7346 end\\\
7347 end\\\
7348 local function popScope()\\\
7349 local scope = currentScope\\\
7350\\\
7351 -- Mark where this scope ends\\\
7352 scope.EndLocation = markLocation()\\\
7353\\\
7354 -- Mark all of the variables in the scope as ending there\\\
7355 for _, var in pairs(scope.VariableList) do\\\
7356 var.ScopeEndLocation = scope.EndLocation\\\
7357 end\\\
7358\\\
7359 -- Move to the parent scope\\\
7360 currentScope = scope.ParentScope\\\
7361\\\
7362 return scope\\\
7363 end\\\
7364 pushScope() -- push initial scope\\\
7365\\\
7366 -- Add / reference variables\\\
7367 local function addLocalVar(name, setNameFunc, localInfo)\\\
7368 assert(localInfo, \\\"Misisng localInfo\\\")\\\
7369 assert(name, \\\"Missing local var name\\\")\\\
7370 local var = {\\\
7371 Type = 'Local';\\\
7372 Name = name;\\\
7373 RenameList = {setNameFunc};\\\
7374 AssignedTo = false;\\\
7375 Info = localInfo;\\\
7376 UseCount = 0;\\\
7377 Scope = currentScope;\\\
7378 BeginLocation = markLocation();\\\
7379 EndLocation = markLocation();\\\
7380 ReferenceLocationList = {markLocation()};\\\
7381 }\\\
7382 function var:Rename(newName)\\\
7383 self.Name = newName\\\
7384 for _, renameFunc in pairs(self.RenameList) do\\\
7385 renameFunc(newName)\\\
7386 end\\\
7387 end\\\
7388 function var:Reference()\\\
7389 self.UseCount = self.UseCount + 1\\\
7390 end\\\
7391 table.insert(currentScope.VariableList, var)\\\
7392 return var\\\
7393 end\\\
7394 local function getGlobalVar(name)\\\
7395 for _, var in pairs(globalVars) do\\\
7396 if var.Name == name then\\\
7397 return var\\\
7398 end\\\
7399 end\\\
7400 local var = {\\\
7401 Type = 'Global';\\\
7402 Name = name;\\\
7403 RenameList = {};\\\
7404 AssignedTo = false;\\\
7405 UseCount = 0;\\\
7406 Scope = nil; -- Globals have no scope\\\
7407 BeginLocation = markLocation();\\\
7408 EndLocation = markLocation();\\\
7409 ReferenceLocationList = {};\\\
7410 }\\\
7411 function var:Rename(newName)\\\
7412 self.Name = newName\\\
7413 for _, renameFunc in pairs(self.RenameList) do\\\
7414 renameFunc(newName)\\\
7415 end\\\
7416 end\\\
7417 function var:Reference()\\\
7418 self.UseCount = self.UseCount + 1\\\
7419 end\\\
7420 table.insert(globalVars, var)\\\
7421 return var\\\
7422 end\\\
7423 local function addGlobalReference(name, setNameFunc)\\\
7424 assert(name, \\\"Missing var name\\\")\\\
7425 local var = getGlobalVar(name)\\\
7426 table.insert(var.RenameList, setNameFunc)\\\
7427 return var\\\
7428 end\\\
7429 local function getLocalVar(scope, name)\\\
7430 -- First search this scope\\\
7431 -- Note: Reverse iterate here because Lua does allow shadowing a local\\\
7432 -- within the same scope, and the later defined variable should\\\
7433 -- be the one referenced.\\\
7434 for i = #scope.VariableList, 1, -1 do\\\
7435 if scope.VariableList[i].Name == name then\\\
7436 return scope.VariableList[i]\\\
7437 end\\\
7438 end\\\
7439\\\
7440 -- Then search parent scope\\\
7441 if scope.ParentScope then\\\
7442 local var = getLocalVar(scope.ParentScope, name)\\\
7443 if var then\\\
7444 return var\\\
7445 end\\\
7446 end\\\
7447\\\
7448 -- Then\\\
7449 return nil\\\
7450 end\\\
7451 local function referenceVariable(name, setNameFunc)\\\
7452 assert(name, \\\"Missing var name\\\")\\\
7453 local var = getLocalVar(currentScope, name)\\\
7454 if var then\\\
7455 table.insert(var.RenameList, setNameFunc)\\\
7456 else\\\
7457 var = addGlobalReference(name, setNameFunc)\\\
7458 end\\\
7459 -- Update the end location of where this variable is used, and\\\
7460 -- add this location to the list of references to this variable.\\\
7461 local curLocation = markLocation()\\\
7462 var.EndLocation = curLocation\\\
7463 table.insert(var.ReferenceLocationList, var.EndLocation)\\\
7464 return var\\\
7465 end\\\
7466\\\
7467 local visitor = {}\\\
7468 visitor.FunctionLiteral = {\\\
7469 -- Function literal adds a new scope and adds the function literal arguments\\\
7470 -- as local variables in the scope.\\\
7471 Pre = function(expr)\\\
7472 pushScope()\\\
7473 for index, ident in pairs(expr.ArgList) do\\\
7474 addLocalVar(ident.Source, function(name)\\\
7475 ident.Source = name\\\
7476 end, {\\\
7477 Type = 'Argument';\\\
7478 Index = index;\\\
7479 })\\\
7480 end\\\
7481 end;\\\
7482 Post = function()\\\
7483 popScope()\\\
7484 end;\\\
7485 }\\\
7486 visitor.VariableExpr = function(expr)\\\
7487 -- Variable expression references from existing local varibales\\\
7488 -- in the current scope, annotating the variable usage with variable\\\
7489 -- information.\\\
7490 expr.Variable = referenceVariable(expr.Token.Source, function(newName)\\\
7491 expr.Token.Source = newName\\\
7492 end)\\\
7493 end\\\
7494 visitor.StatList = {\\\
7495 -- StatList adds a new scope\\\
7496 Pre = function()\\\
7497 pushScope()\\\
7498 end;\\\
7499 Post = function()\\\
7500 popScope()\\\
7501 end;\\\
7502 }\\\
7503 visitor.LocalVarStat = {\\\
7504 Post = function(stat)\\\
7505 -- Local var stat adds the local variables to the current scope as locals\\\
7506 -- We need to visit the subexpressions first, because these new locals\\\
7507 -- will not be in scope for the initialization value expressions. That is:\\\
7508 -- `local bar = bar + 1`\\\
7509 -- Is valid code\\\
7510 for varNum, ident in pairs(stat.VarList) do\\\
7511 addLocalVar(ident.Source, function(name)\\\
7512 stat.VarList[varNum].Source = name\\\
7513 end, {\\\
7514 Type = 'Local';\\\
7515 })\\\
7516 end\\\
7517 end;\\\
7518 }\\\
7519 visitor.LocalFunctionStat = {\\\
7520 Pre = function(stat)\\\
7521 -- Local function stat adds the function itself to the current scope as\\\
7522 -- a local variable, and creates a new scope with the function arguments\\\
7523 -- as local variables.\\\
7524 addLocalVar(stat.FunctionStat.NameChain[1].Source, function(name)\\\
7525 stat.FunctionStat.NameChain[1].Source = name\\\
7526 end, {\\\
7527 Type = 'LocalFunction';\\\
7528 })\\\
7529 pushScope()\\\
7530 for index, ident in pairs(stat.FunctionStat.ArgList) do\\\
7531 addLocalVar(ident.Source, function(name)\\\
7532 ident.Source = name\\\
7533 end, {\\\
7534 Type = 'Argument';\\\
7535 Index = index;\\\
7536 })\\\
7537 end\\\
7538 end;\\\
7539 Post = function()\\\
7540 popScope()\\\
7541 end;\\\
7542 }\\\
7543 visitor.FunctionStat = {\\\
7544 Pre = function(stat)\\\
7545 -- Function stat adds a new scope containing the function arguments\\\
7546 -- as local variables.\\\
7547 -- A function stat may also assign to a global variable if it is in\\\
7548 -- the form `function foo()` with no additional dots/colons in the\\\
7549 -- name chain.\\\
7550 local nameChain = stat.NameChain\\\
7551 local var;\\\
7552 if #nameChain == 1 then\\\
7553 -- If there is only one item in the name chain, then the first item\\\
7554 -- is a reference to a global variable.\\\
7555 var = addGlobalReference(nameChain[1].Source, function(name)\\\
7556 nameChain[1].Source = name\\\
7557 end)\\\
7558 else\\\
7559 var = referenceVariable(nameChain[1].Source, function(name)\\\
7560 nameChain[1].Source = name\\\
7561 end)\\\
7562 end\\\
7563 var.AssignedTo = true\\\
7564 pushScope()\\\
7565 for index, ident in pairs(stat.ArgList) do\\\
7566 addLocalVar(ident.Source, function(name)\\\
7567 ident.Source = name\\\
7568 end, {\\\
7569 Type = 'Argument';\\\
7570 Index = index;\\\
7571 })\\\
7572 end\\\
7573 end;\\\
7574 Post = function()\\\
7575 popScope()\\\
7576 end;\\\
7577 }\\\
7578 visitor.GenericForStat = {\\\
7579 Pre = function(stat)\\\
7580 -- Generic fors need an extra scope holding the range variables\\\
7581 -- Need a custom visitor so that the generator expressions can be\\\
7582 -- visited before we push a scope, but the body can be visited\\\
7583 -- after we push a scope.\\\
7584 for _, ex in pairs(stat.GeneratorList) do\\\
7585 VisitAst(ex, visitor)\\\
7586 end\\\
7587 pushScope()\\\
7588 for index, ident in pairs(stat.VarList) do\\\
7589 addLocalVar(ident.Source, function(name)\\\
7590 ident.Source = name\\\
7591 end, {\\\
7592 Type = 'ForRange';\\\
7593 Index = index;\\\
7594 })\\\
7595 end\\\
7596 VisitAst(stat.Body, visitor)\\\
7597 popScope()\\\
7598 return true -- Custom visit\\\
7599 end;\\\
7600 }\\\
7601 visitor.NumericForStat = {\\\
7602 Pre = function(stat)\\\
7603 -- Numeric fors need an extra scope holding the range variables\\\
7604 -- Need a custom visitor so that the generator expressions can be\\\
7605 -- visited before we push a scope, but the body can be visited\\\
7606 -- after we push a scope.\\\
7607 for _, ex in pairs(stat.RangeList) do\\\
7608 VisitAst(ex, visitor)\\\
7609 end\\\
7610 pushScope()\\\
7611 for index, ident in pairs(stat.VarList) do\\\
7612 addLocalVar(ident.Source, function(name)\\\
7613 ident.Source = name\\\
7614 end, {\\\
7615 Type = 'ForRange';\\\
7616 Index = index;\\\
7617 })\\\
7618 end\\\
7619 VisitAst(stat.Body, visitor)\\\
7620 popScope()\\\
7621 return true -- Custom visit\\\
7622 end;\\\
7623 }\\\
7624 visitor.AssignmentStat = {\\\
7625 Post = function(stat)\\\
7626 -- For an assignment statement we need to mark the\\\
7627 -- \\\"assigned to\\\" flag on variables.\\\
7628 for _, ex in pairs(stat.Lhs) do\\\
7629 if ex.Variable then\\\
7630 ex.Variable.AssignedTo = true\\\
7631 end\\\
7632 end\\\
7633 end;\\\
7634 }\\\
7635\\\
7636 VisitAst(ast, visitor)\\\
7637\\\
7638 return globalVars, popScope()\\\
7639end\\\
7640\\\
7641-- Prints out an AST to a string\\\
7642local function PrintAst(ast, out)\\\
7643\\\
7644 local printStat, printExpr;\\\
7645\\\
7646 local function printt(tk)\\\
7647 if not tk.LeadingWhite or not tk.Source then\\\
7648 error(\\\"Bad token: \\\"..FormatTable(tk))\\\
7649 end\\\
7650 table.insert(out, tk.LeadingWhite)\\\
7651 table.insert(out, tk.Source)\\\
7652 end\\\
7653\\\
7654 printExpr = function(expr)\\\
7655 if expr.Type == 'BinopExpr' then\\\
7656 printExpr(expr.Lhs)\\\
7657 printt(expr.Token_Op)\\\
7658 printExpr(expr.Rhs)\\\
7659 elseif expr.Type == 'UnopExpr' then\\\
7660 printt(expr.Token_Op)\\\
7661 printExpr(expr.Rhs)\\\
7662 elseif expr.Type == 'NumberLiteral' or expr.Type == 'StringLiteral' or\\\
7663 expr.Type == 'NilLiteral' or expr.Type == 'BooleanLiteral' or\\\
7664 expr.Type == 'VargLiteral'\\\
7665 then\\\
7666 -- Just print the token\\\
7667 printt(expr.Token)\\\
7668 elseif expr.Type == 'FieldExpr' then\\\
7669 printExpr(expr.Base)\\\
7670 printt(expr.Token_Dot)\\\
7671 printt(expr.Field)\\\
7672 elseif expr.Type == 'IndexExpr' then\\\
7673 printExpr(expr.Base)\\\
7674 printt(expr.Token_OpenBracket)\\\
7675 printExpr(expr.Index)\\\
7676 printt(expr.Token_CloseBracket)\\\
7677 elseif expr.Type == 'MethodExpr' or expr.Type == 'CallExpr' then\\\
7678 printExpr(expr.Base)\\\
7679 if expr.Type == 'MethodExpr' then\\\
7680 printt(expr.Token_Colon)\\\
7681 printt(expr.Method)\\\
7682 end\\\
7683 if expr.FunctionArguments.CallType == 'StringCall' then\\\
7684 printt(expr.FunctionArguments.Token)\\\
7685 elseif expr.FunctionArguments.CallType == 'ArgCall' then\\\
7686 printt(expr.FunctionArguments.Token_OpenParen)\\\
7687 for index, argExpr in pairs(expr.FunctionArguments.ArgList) do\\\
7688 printExpr(argExpr)\\\
7689 local sep = expr.FunctionArguments.Token_CommaList[index]\\\
7690 if sep then\\\
7691 printt(sep)\\\
7692 end\\\
7693 end\\\
7694 printt(expr.FunctionArguments.Token_CloseParen)\\\
7695 elseif expr.FunctionArguments.CallType == 'TableCall' then\\\
7696 printExpr(expr.FunctionArguments.TableExpr)\\\
7697 end\\\
7698 elseif expr.Type == 'FunctionLiteral' then\\\
7699 printt(expr.Token_Function)\\\
7700 printt(expr.Token_OpenParen)\\\
7701 for index, arg in pairs(expr.ArgList) do\\\
7702 printt(arg)\\\
7703 local comma = expr.Token_ArgCommaList[index]\\\
7704 if comma then\\\
7705 printt(comma)\\\
7706 end\\\
7707 end\\\
7708 printt(expr.Token_CloseParen)\\\
7709 printStat(expr.Body)\\\
7710 printt(expr.Token_End)\\\
7711 elseif expr.Type == 'VariableExpr' then\\\
7712 printt(expr.Token)\\\
7713 elseif expr.Type == 'ParenExpr' then\\\
7714 printt(expr.Token_OpenParen)\\\
7715 printExpr(expr.Expression)\\\
7716 printt(expr.Token_CloseParen)\\\
7717 elseif expr.Type == 'TableLiteral' then\\\
7718 printt(expr.Token_OpenBrace)\\\
7719 for index, entry in pairs(expr.EntryList) do\\\
7720 if entry.EntryType == 'Field' then\\\
7721 printt(entry.Field)\\\
7722 printt(entry.Token_Equals)\\\
7723 printExpr(entry.Value)\\\
7724 elseif entry.EntryType == 'Index' then\\\
7725 printt(entry.Token_OpenBracket)\\\
7726 printExpr(entry.Index)\\\
7727 printt(entry.Token_CloseBracket)\\\
7728 printt(entry.Token_Equals)\\\
7729 printExpr(entry.Value)\\\
7730 elseif entry.EntryType == 'Value' then\\\
7731 printExpr(entry.Value)\\\
7732 else\\\
7733 assert(false, \\\"unreachable\\\")\\\
7734 end\\\
7735 local sep = expr.Token_SeparatorList[index]\\\
7736 if sep then\\\
7737 printt(sep)\\\
7738 end\\\
7739 end\\\
7740 printt(expr.Token_CloseBrace)\\\
7741 else\\\
7742 assert(false, \\\"unreachable, type: \\\"..expr.Type..\\\":\\\"..FormatTable(expr))\\\
7743 end\\\
7744 end\\\
7745\\\
7746 printStat = function(stat)\\\
7747 if stat.Type == 'StatList' then\\\
7748 for index, ch in pairs(stat.StatementList) do\\\
7749 printStat(ch)\\\
7750 if stat.SemicolonList[index] then\\\
7751 printt(stat.SemicolonList[index])\\\
7752 end\\\
7753 end\\\
7754 elseif stat.Type == 'BreakStat' then\\\
7755 printt(stat.Token_Break)\\\
7756 elseif stat.Type == 'ReturnStat' then\\\
7757 printt(stat.Token_Return)\\\
7758 for index, expr in pairs(stat.ExprList) do\\\
7759 printExpr(expr)\\\
7760 if stat.Token_CommaList[index] then\\\
7761 printt(stat.Token_CommaList[index])\\\
7762 end\\\
7763 end\\\
7764 elseif stat.Type == 'LocalVarStat' then\\\
7765 printt(stat.Token_Local)\\\
7766 for index, var in pairs(stat.VarList) do\\\
7767 printt(var)\\\
7768 local comma = stat.Token_VarCommaList[index]\\\
7769 if comma then\\\
7770 printt(comma)\\\
7771 end\\\
7772 end\\\
7773 if stat.Token_Equals then\\\
7774 printt(stat.Token_Equals)\\\
7775 for index, expr in pairs(stat.ExprList) do\\\
7776 printExpr(expr)\\\
7777 local comma = stat.Token_ExprCommaList[index]\\\
7778 if comma then\\\
7779 printt(comma)\\\
7780 end\\\
7781 end\\\
7782 end\\\
7783 elseif stat.Type == 'LocalFunctionStat' then\\\
7784 printt(stat.Token_Local)\\\
7785 printt(stat.FunctionStat.Token_Function)\\\
7786 printt(stat.FunctionStat.NameChain[1])\\\
7787 printt(stat.FunctionStat.Token_OpenParen)\\\
7788 for index, arg in pairs(stat.FunctionStat.ArgList) do\\\
7789 printt(arg)\\\
7790 local comma = stat.FunctionStat.Token_ArgCommaList[index]\\\
7791 if comma then\\\
7792 printt(comma)\\\
7793 end\\\
7794 end\\\
7795 printt(stat.FunctionStat.Token_CloseParen)\\\
7796 printStat(stat.FunctionStat.Body)\\\
7797 printt(stat.FunctionStat.Token_End)\\\
7798 elseif stat.Type == 'FunctionStat' then\\\
7799 printt(stat.Token_Function)\\\
7800 for index, part in pairs(stat.NameChain) do\\\
7801 printt(part)\\\
7802 local sep = stat.Token_NameChainSeparator[index]\\\
7803 if sep then\\\
7804 printt(sep)\\\
7805 end\\\
7806 end\\\
7807 printt(stat.Token_OpenParen)\\\
7808 for index, arg in pairs(stat.ArgList) do\\\
7809 printt(arg)\\\
7810 local comma = stat.Token_ArgCommaList[index]\\\
7811 if comma then\\\
7812 printt(comma)\\\
7813 end\\\
7814 end\\\
7815 printt(stat.Token_CloseParen)\\\
7816 printStat(stat.Body)\\\
7817 printt(stat.Token_End)\\\
7818 elseif stat.Type == 'RepeatStat' then\\\
7819 printt(stat.Token_Repeat)\\\
7820 printStat(stat.Body)\\\
7821 printt(stat.Token_Until)\\\
7822 printExpr(stat.Condition)\\\
7823 elseif stat.Type == 'GenericForStat' then\\\
7824 printt(stat.Token_For)\\\
7825 for index, var in pairs(stat.VarList) do\\\
7826 printt(var)\\\
7827 local sep = stat.Token_VarCommaList[index]\\\
7828 if sep then\\\
7829 printt(sep)\\\
7830 end\\\
7831 end\\\
7832 printt(stat.Token_In)\\\
7833 for index, expr in pairs(stat.GeneratorList) do\\\
7834 printExpr(expr)\\\
7835 local sep = stat.Token_GeneratorCommaList[index]\\\
7836 if sep then\\\
7837 printt(sep)\\\
7838 end\\\
7839 end\\\
7840 printt(stat.Token_Do)\\\
7841 printStat(stat.Body)\\\
7842 printt(stat.Token_End)\\\
7843 elseif stat.Type == 'NumericForStat' then\\\
7844 printt(stat.Token_For)\\\
7845 for index, var in pairs(stat.VarList) do\\\
7846 printt(var)\\\
7847 local sep = stat.Token_VarCommaList[index]\\\
7848 if sep then\\\
7849 printt(sep)\\\
7850 end\\\
7851 end\\\
7852 printt(stat.Token_Equals)\\\
7853 for index, expr in pairs(stat.RangeList) do\\\
7854 printExpr(expr)\\\
7855 local sep = stat.Token_RangeCommaList[index]\\\
7856 if sep then\\\
7857 printt(sep)\\\
7858 end\\\
7859 end\\\
7860 printt(stat.Token_Do)\\\
7861 printStat(stat.Body)\\\
7862 printt(stat.Token_End)\\\
7863 elseif stat.Type == 'WhileStat' then\\\
7864 printt(stat.Token_While)\\\
7865 printExpr(stat.Condition)\\\
7866 printt(stat.Token_Do)\\\
7867 printStat(stat.Body)\\\
7868 printt(stat.Token_End)\\\
7869 elseif stat.Type == 'DoStat' then\\\
7870 printt(stat.Token_Do)\\\
7871 printStat(stat.Body)\\\
7872 printt(stat.Token_End)\\\
7873 elseif stat.Type == 'IfStat' then\\\
7874 printt(stat.Token_If)\\\
7875 printExpr(stat.Condition)\\\
7876 printt(stat.Token_Then)\\\
7877 printStat(stat.Body)\\\
7878 for _, clause in pairs(stat.ElseClauseList) do\\\
7879 printt(clause.Token)\\\
7880 if clause.Condition then\\\
7881 printExpr(clause.Condition)\\\
7882 printt(clause.Token_Then)\\\
7883 end\\\
7884 printStat(clause.Body)\\\
7885 end\\\
7886 printt(stat.Token_End)\\\
7887 elseif stat.Type == 'CallExprStat' then\\\
7888 printExpr(stat.Expression)\\\
7889 elseif stat.Type == 'AssignmentStat' then\\\
7890 for index, ex in pairs(stat.Lhs) do\\\
7891 printExpr(ex)\\\
7892 local sep = stat.Token_LhsSeparatorList[index]\\\
7893 if sep then\\\
7894 printt(sep)\\\
7895 end\\\
7896 end\\\
7897 printt(stat.Token_Equals)\\\
7898 for index, ex in pairs(stat.Rhs) do\\\
7899 printExpr(ex)\\\
7900 local sep = stat.Token_RhsSeparatorList[index]\\\
7901 if sep then\\\
7902 printt(sep)\\\
7903 end\\\
7904 end\\\
7905 else\\\
7906 assert(false, \\\"unreachable\\\")\\\
7907 end\\\
7908 end\\\
7909\\\
7910 printStat(ast)\\\
7911end\\\
7912\\\
7913-- Adds / removes whitespace in an AST to put it into a \\\"standard formatting\\\"\\\
7914local function FormatAst(ast)\\\
7915 local formatStat, formatExpr;\\\
7916\\\
7917 local currentIndent = 0\\\
7918\\\
7919 local function applyIndent(token)\\\
7920 local indentString = '\\\\n'..('\\\\t'):rep(currentIndent)\\\
7921 if token.LeadingWhite == '' or (token.LeadingWhite:sub(-#indentString, -1) ~= indentString) then\\\
7922 -- Trim existing trailing whitespace on LeadingWhite\\\
7923 -- Trim trailing tabs and spaces, and up to one newline\\\
7924 token.LeadingWhite = token.LeadingWhite:gsub(\\\"\\\\n?[\\\\t ]*$\\\", \\\"\\\")\\\
7925 token.LeadingWhite = token.LeadingWhite..indentString\\\
7926 end\\\
7927 end\\\
7928\\\
7929 local function indent()\\\
7930 currentIndent = currentIndent + 1\\\
7931 end\\\
7932\\\
7933 local function undent()\\\
7934 currentIndent = currentIndent - 1\\\
7935 assert(currentIndent >= 0, \\\"Undented too far\\\")\\\
7936 end\\\
7937\\\
7938 local function leadingChar(tk)\\\
7939 if #tk.LeadingWhite > 0 then\\\
7940 return tk.LeadingWhite:sub(1,1)\\\
7941 else\\\
7942 return tk.Source:sub(1,1)\\\
7943 end\\\
7944 end\\\
7945\\\
7946 local function padToken(tk)\\\
7947 if not WhiteChars[leadingChar(tk)] then\\\
7948 tk.LeadingWhite = ' '..tk.LeadingWhite\\\
7949 end\\\
7950 end\\\
7951\\\
7952 local function padExpr(expr)\\\
7953 padToken(expr:GetFirstToken())\\\
7954 end\\\
7955\\\
7956 local function formatBody(_, bodyStat, closeToken)\\\
7957 indent()\\\
7958 formatStat(bodyStat)\\\
7959 undent()\\\
7960 applyIndent(closeToken)\\\
7961 end\\\
7962\\\
7963 formatExpr = function(expr)\\\
7964 if expr.Type == 'BinopExpr' then\\\
7965 formatExpr(expr.Lhs)\\\
7966 formatExpr(expr.Rhs)\\\
7967 if expr.Token_Op.Source == '..' then\\\
7968 -- No padding on ..\\\
7969 else\\\
7970 padExpr(expr.Rhs)\\\
7971 padToken(expr.Token_Op)\\\
7972 end\\\
7973 elseif expr.Type == 'UnopExpr' then\\\
7974 formatExpr(expr.Rhs)\\\
7975 --(expr.Token_Op)\\\
7976 elseif expr.Type == 'NumberLiteral' or expr.Type == 'StringLiteral' or\\\
7977 expr.Type == 'NilLiteral' or expr.Type == 'BooleanLiteral' or\\\
7978 expr.Type == 'VargLiteral'\\\
7979 then\\\
7980 -- Nothing to do\\\
7981 --(expr.Token)\\\
7982 elseif expr.Type == 'FieldExpr' then\\\
7983 formatExpr(expr.Base)\\\
7984 --(expr.Token_Dot)\\\
7985 --(expr.Field)\\\
7986 elseif expr.Type == 'IndexExpr' then\\\
7987 formatExpr(expr.Base)\\\
7988 formatExpr(expr.Index)\\\
7989 --(expr.Token_OpenBracket)\\\
7990 --(expr.Token_CloseBracket)\\\
7991 elseif expr.Type == 'MethodExpr' or expr.Type == 'CallExpr' then\\\
7992 formatExpr(expr.Base)\\\
7993 if expr.FunctionArguments.CallType == 'StringCall' then\\\
7994 --(expr.FunctionArguments.Token)\\\
7995 elseif expr.FunctionArguments.CallType == 'ArgCall' then\\\
7996 --(expr.FunctionArguments.Token_OpenParen)\\\
7997 for index, argExpr in pairs(expr.FunctionArguments.ArgList) do\\\
7998 formatExpr(argExpr)\\\
7999 if index > 1 then\\\
8000 padExpr(argExpr)\\\
8001 end\\\
8002 end\\\
8003 --(expr.FunctionArguments.Token_CloseParen)\\\
8004 elseif expr.FunctionArguments.CallType == 'TableCall' then\\\
8005 formatExpr(expr.FunctionArguments.TableExpr)\\\
8006 end\\\
8007 elseif expr.Type == 'FunctionLiteral' then\\\
8008 --(expr.Token_Function)\\\
8009 --(expr.Token_OpenParen)\\\
8010 for index, arg in pairs(expr.ArgList) do\\\
8011 --(arg)\\\
8012 if index > 1 then\\\
8013 padToken(arg)\\\
8014 end\\\
8015 end\\\
8016 --(expr.Token_CloseParen)\\\
8017 formatBody(expr.Token_CloseParen, expr.Body, expr.Token_End)\\\
8018 elseif expr.Type == 'VariableExpr' then\\\
8019 --(expr.Token)\\\
8020 elseif expr.Type == 'ParenExpr' then\\\
8021 formatExpr(expr.Expression)\\\
8022 --(expr.Token_OpenParen)\\\
8023 --(expr.Token_CloseParen)\\\
8024 elseif expr.Type == 'TableLiteral' then\\\
8025 --(expr.Token_OpenBrace)\\\
8026 if #expr.EntryList == 0 then\\\
8027 -- Nothing to do\\\
8028 else\\\
8029 indent()\\\
8030 for _, entry in pairs(expr.EntryList) do\\\
8031 if entry.EntryType == 'Field' then\\\
8032 applyIndent(entry.Field)\\\
8033 padToken(entry.Token_Equals)\\\
8034 formatExpr(entry.Value)\\\
8035 padExpr(entry.Value)\\\
8036 elseif entry.EntryType == 'Index' then\\\
8037 applyIndent(entry.Token_OpenBracket)\\\
8038 formatExpr(entry.Index)\\\
8039 --(entry.Token_CloseBracket)\\\
8040 padToken(entry.Token_Equals)\\\
8041 formatExpr(entry.Value)\\\
8042 padExpr(entry.Value)\\\
8043 elseif entry.EntryType == 'Value' then\\\
8044 formatExpr(entry.Value)\\\
8045 applyIndent(entry.Value:GetFirstToken())\\\
8046 else\\\
8047 assert(false, \\\"unreachable\\\")\\\
8048 end\\\
8049 end\\\
8050 undent()\\\
8051 applyIndent(expr.Token_CloseBrace)\\\
8052 end\\\
8053 --(expr.Token_CloseBrace)\\\
8054 else\\\
8055 assert(false, \\\"unreachable, type: \\\"..expr.Type..\\\":\\\"..FormatTable(expr))\\\
8056 end\\\
8057 end\\\
8058\\\
8059 formatStat = function(stat)\\\
8060 if stat.Type == 'StatList' then\\\
8061 for _, stat in pairs(stat.StatementList) do\\\
8062 formatStat(stat)\\\
8063 applyIndent(stat:GetFirstToken())\\\
8064 end\\\
8065\\\
8066 elseif stat.Type == 'BreakStat' then\\\
8067 --(stat.Token_Break)\\\
8068\\\
8069 elseif stat.Type == 'ReturnStat' then\\\
8070 --(stat.Token_Return)\\\
8071 for _, expr in pairs(stat.ExprList) do\\\
8072 formatExpr(expr)\\\
8073 padExpr(expr)\\\
8074 end\\\
8075 elseif stat.Type == 'LocalVarStat' then\\\
8076 --(stat.Token_Local)\\\
8077 for _, var in pairs(stat.VarList) do\\\
8078 padToken(var)\\\
8079 end\\\
8080 if stat.Token_Equals then\\\
8081 padToken(stat.Token_Equals)\\\
8082 for _, expr in pairs(stat.ExprList) do\\\
8083 formatExpr(expr)\\\
8084 padExpr(expr)\\\
8085 end\\\
8086 end\\\
8087 elseif stat.Type == 'LocalFunctionStat' then\\\
8088 --(stat.Token_Local)\\\
8089 padToken(stat.FunctionStat.Token_Function)\\\
8090 padToken(stat.FunctionStat.NameChain[1])\\\
8091 --(stat.FunctionStat.Token_OpenParen)\\\
8092 for index, arg in pairs(stat.FunctionStat.ArgList) do\\\
8093 if index > 1 then\\\
8094 padToken(arg)\\\
8095 end\\\
8096 end\\\
8097 --(stat.FunctionStat.Token_CloseParen)\\\
8098 formatBody(stat.FunctionStat.Token_CloseParen, stat.FunctionStat.Body, stat.FunctionStat.Token_End)\\\
8099 elseif stat.Type == 'FunctionStat' then\\\
8100 --(stat.Token_Function)\\\
8101 for index, part in pairs(stat.NameChain) do\\\
8102 if index == 1 then\\\
8103 padToken(part)\\\
8104 end\\\
8105 end\\\
8106 --(stat.Token_OpenParen)\\\
8107 for index, arg in pairs(stat.ArgList) do\\\
8108 if index > 1 then\\\
8109 padToken(arg)\\\
8110 end\\\
8111 end\\\
8112 --(stat.Token_CloseParen)\\\
8113 formatBody(stat.Token_CloseParen, stat.Body, stat.Token_End)\\\
8114 elseif stat.Type == 'RepeatStat' then\\\
8115 --(stat.Token_Repeat)\\\
8116 formatBody(stat.Token_Repeat, stat.Body, stat.Token_Until)\\\
8117 formatExpr(stat.Condition)\\\
8118 padExpr(stat.Condition)\\\
8119 elseif stat.Type == 'GenericForStat' then\\\
8120 --(stat.Token_For)\\\
8121 for _, var in pairs(stat.VarList) do\\\
8122 padToken(var)\\\
8123 end\\\
8124 padToken(stat.Token_In)\\\
8125 for _, expr in pairs(stat.GeneratorList) do\\\
8126 formatExpr(expr)\\\
8127 padExpr(expr)\\\
8128 end\\\
8129 padToken(stat.Token_Do)\\\
8130 formatBody(stat.Token_Do, stat.Body, stat.Token_End)\\\
8131 elseif stat.Type == 'NumericForStat' then\\\
8132 --(stat.Token_For)\\\
8133 for _, var in pairs(stat.VarList) do\\\
8134 padToken(var)\\\
8135 end\\\
8136 padToken(stat.Token_Equals)\\\
8137 for _, expr in pairs(stat.RangeList) do\\\
8138 formatExpr(expr)\\\
8139 padExpr(expr)\\\
8140 end\\\
8141 padToken(stat.Token_Do)\\\
8142 formatBody(stat.Token_Do, stat.Body, stat.Token_End)\\\
8143 elseif stat.Type == 'WhileStat' then\\\
8144 --(stat.Token_While)\\\
8145 formatExpr(stat.Condition)\\\
8146 padExpr(stat.Condition)\\\
8147 padToken(stat.Token_Do)\\\
8148 formatBody(stat.Token_Do, stat.Body, stat.Token_End)\\\
8149 elseif stat.Type == 'DoStat' then\\\
8150 --(stat.Token_Do)\\\
8151 formatBody(stat.Token_Do, stat.Body, stat.Token_End)\\\
8152 elseif stat.Type == 'IfStat' then\\\
8153 --(stat.Token_If)\\\
8154 formatExpr(stat.Condition)\\\
8155 padExpr(stat.Condition)\\\
8156 padToken(stat.Token_Then)\\\
8157 --\\\
8158 local lastBodyOpen = stat.Token_Then\\\
8159 local lastBody = stat.Body\\\
8160 --\\\
8161 for _, clause in pairs(stat.ElseClauseList) do\\\
8162 formatBody(lastBodyOpen, lastBody, clause.Token)\\\
8163 lastBodyOpen = clause.Token\\\
8164 --\\\
8165 if clause.Condition then\\\
8166 formatExpr(clause.Condition)\\\
8167 padExpr(clause.Condition)\\\
8168 padToken(clause.Token_Then)\\\
8169 lastBodyOpen = clause.Token_Then\\\
8170 end\\\
8171 lastBody = clause.Body\\\
8172 end\\\
8173 --\\\
8174 formatBody(lastBodyOpen, lastBody, stat.Token_End)\\\
8175\\\
8176 elseif stat.Type == 'CallExprStat' then\\\
8177 formatExpr(stat.Expression)\\\
8178 elseif stat.Type == 'AssignmentStat' then\\\
8179 for index, ex in pairs(stat.Lhs) do\\\
8180 formatExpr(ex)\\\
8181 if index > 1 then\\\
8182 padExpr(ex)\\\
8183 end\\\
8184 end\\\
8185 padToken(stat.Token_Equals)\\\
8186 for _, ex in pairs(stat.Rhs) do\\\
8187 formatExpr(ex)\\\
8188 padExpr(ex)\\\
8189 end\\\
8190 else\\\
8191 assert(false, \\\"unreachable\\\")\\\
8192 end\\\
8193 end\\\
8194\\\
8195 formatStat(ast)\\\
8196end\\\
8197\\\
8198-- Strips as much whitespace off of tokens in an AST as possible without causing problems\\\
8199local function StripAst(ast)\\\
8200 local stripStat, stripExpr;\\\
8201\\\
8202 local function stript(token)\\\
8203 token.LeadingWhite = ''\\\
8204 end\\\
8205\\\
8206 -- Make to adjacent tokens as close as possible\\\
8207 local function joint(tokenA, tokenB)\\\
8208 -- Strip the second token's whitespace\\\
8209 stript(tokenB)\\\
8210\\\
8211 -- Get the trailing A <-> leading B character pair\\\
8212 local lastCh = tokenA.Source:sub(-1, -1)\\\
8213 local firstCh = tokenB.Source:sub(1, 1)\\\
8214\\\
8215 -- Cases to consider:\\\
8216 -- Touching minus signs -> comment: `- -42` -> `--42' is invalid\\\
8217 -- Touching words: `a b` -> `ab` is invalid\\\
8218 -- Touching digits: `2 3`, can't occurr in the Lua syntax as number literals aren't a primary expression\\\
8219 -- Abiguous syntax: `f(x)\\\\n(x)()` is already disallowed, we can't cause a problem by removing newlines\\\
8220\\\
8221 -- Figure out what separation is needed\\\
8222 if\\\
8223 (lastCh == '-' and firstCh == '-') or\\\
8224 (AllIdentChars[lastCh] and AllIdentChars[firstCh])\\\
8225 then\\\
8226 tokenB.LeadingWhite = ' ' -- Use a separator\\\
8227 else\\\
8228 tokenB.LeadingWhite = '' -- Don't use a separator\\\
8229 end\\\
8230 end\\\
8231\\\
8232 -- Join up a statement body and it's opening / closing tokens\\\
8233 local function bodyjoint(open, body, close)\\\
8234 stripStat(body)\\\
8235 stript(close)\\\
8236 local bodyFirst = body:GetFirstToken()\\\
8237 local bodyLast = body:GetLastToken()\\\
8238 if bodyFirst then\\\
8239 -- Body is non-empty, join body to open / close\\\
8240 joint(open, bodyFirst)\\\
8241 joint(bodyLast, close)\\\
8242 else\\\
8243 -- Body is empty, just join open and close token together\\\
8244 joint(open, close)\\\
8245 end\\\
8246 end\\\
8247\\\
8248 stripExpr = function(expr)\\\
8249 if expr.Type == 'BinopExpr' then\\\
8250 stripExpr(expr.Lhs)\\\
8251 stript(expr.Token_Op)\\\
8252 stripExpr(expr.Rhs)\\\
8253 -- Handle the `a - -b` -/-> `a--b` case which would otherwise incorrectly generate a comment\\\
8254 -- Also handles operators \\\"or\\\" / \\\"and\\\" which definitely need joining logic in a bunch of cases\\\
8255 joint(expr.Token_Op, expr.Rhs:GetFirstToken())\\\
8256 joint(expr.Lhs:GetLastToken(), expr.Token_Op)\\\
8257 elseif expr.Type == 'UnopExpr' then\\\
8258 stript(expr.Token_Op)\\\
8259 stripExpr(expr.Rhs)\\\
8260 -- Handle the `- -b` -/-> `--b` case which would otherwise incorrectly generate a comment\\\
8261 joint(expr.Token_Op, expr.Rhs:GetFirstToken())\\\
8262 elseif expr.Type == 'NumberLiteral' or expr.Type == 'StringLiteral' or\\\
8263 expr.Type == 'NilLiteral' or expr.Type == 'BooleanLiteral' or\\\
8264 expr.Type == 'VargLiteral'\\\
8265 then\\\
8266 -- Just print the token\\\
8267 stript(expr.Token)\\\
8268 elseif expr.Type == 'FieldExpr' then\\\
8269 stripExpr(expr.Base)\\\
8270 stript(expr.Token_Dot)\\\
8271 stript(expr.Field)\\\
8272 elseif expr.Type == 'IndexExpr' then\\\
8273 stripExpr(expr.Base)\\\
8274 stript(expr.Token_OpenBracket)\\\
8275 stripExpr(expr.Index)\\\
8276 stript(expr.Token_CloseBracket)\\\
8277 elseif expr.Type == 'MethodExpr' or expr.Type == 'CallExpr' then\\\
8278 stripExpr(expr.Base)\\\
8279 if expr.Type == 'MethodExpr' then\\\
8280 stript(expr.Token_Colon)\\\
8281 stript(expr.Method)\\\
8282 end\\\
8283 if expr.FunctionArguments.CallType == 'StringCall' then\\\
8284 stript(expr.FunctionArguments.Token)\\\
8285 elseif expr.FunctionArguments.CallType == 'ArgCall' then\\\
8286 stript(expr.FunctionArguments.Token_OpenParen)\\\
8287 for index, argExpr in pairs(expr.FunctionArguments.ArgList) do\\\
8288 stripExpr(argExpr)\\\
8289 local sep = expr.FunctionArguments.Token_CommaList[index]\\\
8290 if sep then\\\
8291 stript(sep)\\\
8292 end\\\
8293 end\\\
8294 stript(expr.FunctionArguments.Token_CloseParen)\\\
8295 elseif expr.FunctionArguments.CallType == 'TableCall' then\\\
8296 stripExpr(expr.FunctionArguments.TableExpr)\\\
8297 end\\\
8298 elseif expr.Type == 'FunctionLiteral' then\\\
8299 stript(expr.Token_Function)\\\
8300 stript(expr.Token_OpenParen)\\\
8301 for index, arg in pairs(expr.ArgList) do\\\
8302 stript(arg)\\\
8303 local comma = expr.Token_ArgCommaList[index]\\\
8304 if comma then\\\
8305 stript(comma)\\\
8306 end\\\
8307 end\\\
8308 stript(expr.Token_CloseParen)\\\
8309 bodyjoint(expr.Token_CloseParen, expr.Body, expr.Token_End)\\\
8310 elseif expr.Type == 'VariableExpr' then\\\
8311 stript(expr.Token)\\\
8312 elseif expr.Type == 'ParenExpr' then\\\
8313 stript(expr.Token_OpenParen)\\\
8314 stripExpr(expr.Expression)\\\
8315 stript(expr.Token_CloseParen)\\\
8316 elseif expr.Type == 'TableLiteral' then\\\
8317 stript(expr.Token_OpenBrace)\\\
8318 for index, entry in pairs(expr.EntryList) do\\\
8319 if entry.EntryType == 'Field' then\\\
8320 stript(entry.Field)\\\
8321 stript(entry.Token_Equals)\\\
8322 stripExpr(entry.Value)\\\
8323 elseif entry.EntryType == 'Index' then\\\
8324 stript(entry.Token_OpenBracket)\\\
8325 stripExpr(entry.Index)\\\
8326 stript(entry.Token_CloseBracket)\\\
8327 stript(entry.Token_Equals)\\\
8328 stripExpr(entry.Value)\\\
8329 elseif entry.EntryType == 'Value' then\\\
8330 stripExpr(entry.Value)\\\
8331 else\\\
8332 assert(false, \\\"unreachable\\\")\\\
8333 end\\\
8334 local sep = expr.Token_SeparatorList[index]\\\
8335 if sep then\\\
8336 stript(sep)\\\
8337 end\\\
8338 end\\\
8339 stript(expr.Token_CloseBrace)\\\
8340 else\\\
8341 assert(false, \\\"unreachable, type: \\\"..expr.Type..\\\":\\\"..FormatTable(expr))\\\
8342 end\\\
8343 end\\\
8344\\\
8345 stripStat = function(stat)\\\
8346 if stat.Type == 'StatList' then\\\
8347 -- Strip all surrounding whitespace on statement lists along with separating whitespace\\\
8348 for i = 1, #stat.StatementList do\\\
8349 local chStat = stat.StatementList[i]\\\
8350\\\
8351 -- Strip the statement and it's whitespace\\\
8352 stripStat(chStat)\\\
8353 stript(chStat:GetFirstToken())\\\
8354\\\
8355 -- If there was a last statement, join them appropriately\\\
8356 local lastChStat = stat.StatementList[i-1]\\\
8357 if lastChStat then\\\
8358 -- See if we can remove a semi-colon, the only case where we can't is if\\\
8359 -- this and the last statement have a `);(` pair, where removing the semi-colon\\\
8360 -- would introduce ambiguous syntax.\\\
8361 if stat.SemicolonList[i-1] and\\\
8362 (lastChStat:GetLastToken().Source ~= ')' or chStat:GetFirstToken().Source ~= ')')\\\
8363 then\\\
8364 stat.SemicolonList[i-1] = nil\\\
8365 end\\\
8366\\\
8367 -- If there isn't a semi-colon, we should safely join the two statements\\\
8368 -- (If there is one, then no whitespace leading chStat is always okay)\\\
8369 if not stat.SemicolonList[i-1] then\\\
8370 joint(lastChStat:GetLastToken(), chStat:GetFirstToken())\\\
8371 end\\\
8372 end\\\
8373 end\\\
8374\\\
8375 -- A semi-colon is never needed on the last stat in a statlist:\\\
8376 stat.SemicolonList[#stat.StatementList] = nil\\\
8377\\\
8378 -- The leading whitespace on the statlist should be stripped\\\
8379 if #stat.StatementList > 0 then\\\
8380 stript(stat.StatementList[1]:GetFirstToken())\\\
8381 end\\\
8382\\\
8383 elseif stat.Type == 'BreakStat' then\\\
8384 stript(stat.Token_Break)\\\
8385\\\
8386 elseif stat.Type == 'ReturnStat' then\\\
8387 stript(stat.Token_Return)\\\
8388 for index, expr in pairs(stat.ExprList) do\\\
8389 stripExpr(expr)\\\
8390 if stat.Token_CommaList[index] then\\\
8391 stript(stat.Token_CommaList[index])\\\
8392 end\\\
8393 end\\\
8394 if #stat.ExprList > 0 then\\\
8395 joint(stat.Token_Return, stat.ExprList[1]:GetFirstToken())\\\
8396 end\\\
8397 elseif stat.Type == 'LocalVarStat' then\\\
8398 stript(stat.Token_Local)\\\
8399 for index, var in pairs(stat.VarList) do\\\
8400 if index == 1 then\\\
8401 joint(stat.Token_Local, var)\\\
8402 else\\\
8403 stript(var)\\\
8404 end\\\
8405 local comma = stat.Token_VarCommaList[index]\\\
8406 if comma then\\\
8407 stript(comma)\\\
8408 end\\\
8409 end\\\
8410 if stat.Token_Equals then\\\
8411 stript(stat.Token_Equals)\\\
8412 for index, expr in pairs(stat.ExprList) do\\\
8413 stripExpr(expr)\\\
8414 local comma = stat.Token_ExprCommaList[index]\\\
8415 if comma then\\\
8416 stript(comma)\\\
8417 end\\\
8418 end\\\
8419 end\\\
8420 elseif stat.Type == 'LocalFunctionStat' then\\\
8421 stript(stat.Token_Local)\\\
8422 joint(stat.Token_Local, stat.FunctionStat.Token_Function)\\\
8423 joint(stat.FunctionStat.Token_Function, stat.FunctionStat.NameChain[1])\\\
8424 joint(stat.FunctionStat.NameChain[1], stat.FunctionStat.Token_OpenParen)\\\
8425 for index, arg in pairs(stat.FunctionStat.ArgList) do\\\
8426 stript(arg)\\\
8427 local comma = stat.FunctionStat.Token_ArgCommaList[index]\\\
8428 if comma then\\\
8429 stript(comma)\\\
8430 end\\\
8431 end\\\
8432 stript(stat.FunctionStat.Token_CloseParen)\\\
8433 bodyjoint(stat.FunctionStat.Token_CloseParen, stat.FunctionStat.Body, stat.FunctionStat.Token_End)\\\
8434 elseif stat.Type == 'FunctionStat' then\\\
8435 stript(stat.Token_Function)\\\
8436 for index, part in pairs(stat.NameChain) do\\\
8437 if index == 1 then\\\
8438 joint(stat.Token_Function, part)\\\
8439 else\\\
8440 stript(part)\\\
8441 end\\\
8442 local sep = stat.Token_NameChainSeparator[index]\\\
8443 if sep then\\\
8444 stript(sep)\\\
8445 end\\\
8446 end\\\
8447 stript(stat.Token_OpenParen)\\\
8448 for index, arg in pairs(stat.ArgList) do\\\
8449 stript(arg)\\\
8450 local comma = stat.Token_ArgCommaList[index]\\\
8451 if comma then\\\
8452 stript(comma)\\\
8453 end\\\
8454 end\\\
8455 stript(stat.Token_CloseParen)\\\
8456 bodyjoint(stat.Token_CloseParen, stat.Body, stat.Token_End)\\\
8457 elseif stat.Type == 'RepeatStat' then\\\
8458 stript(stat.Token_Repeat)\\\
8459 bodyjoint(stat.Token_Repeat, stat.Body, stat.Token_Until)\\\
8460 stripExpr(stat.Condition)\\\
8461 joint(stat.Token_Until, stat.Condition:GetFirstToken())\\\
8462 elseif stat.Type == 'GenericForStat' then\\\
8463 stript(stat.Token_For)\\\
8464 for index, var in pairs(stat.VarList) do\\\
8465 if index == 1 then\\\
8466 joint(stat.Token_For, var)\\\
8467 else\\\
8468 stript(var)\\\
8469 end\\\
8470 local sep = stat.Token_VarCommaList[index]\\\
8471 if sep then\\\
8472 stript(sep)\\\
8473 end\\\
8474 end\\\
8475 joint(stat.VarList[#stat.VarList], stat.Token_In)\\\
8476 for index, expr in pairs(stat.GeneratorList) do\\\
8477 stripExpr(expr)\\\
8478 if index == 1 then\\\
8479 joint(stat.Token_In, expr:GetFirstToken())\\\
8480 end\\\
8481 local sep = stat.Token_GeneratorCommaList[index]\\\
8482 if sep then\\\
8483 stript(sep)\\\
8484 end\\\
8485 end\\\
8486 joint(stat.GeneratorList[#stat.GeneratorList]:GetLastToken(), stat.Token_Do)\\\
8487 bodyjoint(stat.Token_Do, stat.Body, stat.Token_End)\\\
8488 elseif stat.Type == 'NumericForStat' then\\\
8489 stript(stat.Token_For)\\\
8490 for index, var in pairs(stat.VarList) do\\\
8491 if index == 1 then\\\
8492 joint(stat.Token_For, var)\\\
8493 else\\\
8494 stript(var)\\\
8495 end\\\
8496 local sep = stat.Token_VarCommaList[index]\\\
8497 if sep then\\\
8498 stript(sep)\\\
8499 end\\\
8500 end\\\
8501 joint(stat.VarList[#stat.VarList], stat.Token_Equals)\\\
8502 for index, expr in pairs(stat.RangeList) do\\\
8503 stripExpr(expr)\\\
8504 if index == 1 then\\\
8505 joint(stat.Token_Equals, expr:GetFirstToken())\\\
8506 end\\\
8507 local sep = stat.Token_RangeCommaList[index]\\\
8508 if sep then\\\
8509 stript(sep)\\\
8510 end\\\
8511 end\\\
8512 joint(stat.RangeList[#stat.RangeList]:GetLastToken(), stat.Token_Do)\\\
8513 bodyjoint(stat.Token_Do, stat.Body, stat.Token_End)\\\
8514 elseif stat.Type == 'WhileStat' then\\\
8515 stript(stat.Token_While)\\\
8516 stripExpr(stat.Condition)\\\
8517 stript(stat.Token_Do)\\\
8518 joint(stat.Token_While, stat.Condition:GetFirstToken())\\\
8519 joint(stat.Condition:GetLastToken(), stat.Token_Do)\\\
8520 bodyjoint(stat.Token_Do, stat.Body, stat.Token_End)\\\
8521 elseif stat.Type == 'DoStat' then\\\
8522 stript(stat.Token_Do)\\\
8523 stript(stat.Token_End)\\\
8524 bodyjoint(stat.Token_Do, stat.Body, stat.Token_End)\\\
8525 elseif stat.Type == 'IfStat' then\\\
8526 stript(stat.Token_If)\\\
8527 stripExpr(stat.Condition)\\\
8528 joint(stat.Token_If, stat.Condition:GetFirstToken())\\\
8529 joint(stat.Condition:GetLastToken(), stat.Token_Then)\\\
8530 --\\\
8531 local lastBodyOpen = stat.Token_Then\\\
8532 local lastBody = stat.Body\\\
8533 --\\\
8534 for _, clause in pairs(stat.ElseClauseList) do\\\
8535 bodyjoint(lastBodyOpen, lastBody, clause.Token)\\\
8536 lastBodyOpen = clause.Token\\\
8537 --\\\
8538 if clause.Condition then\\\
8539 stripExpr(clause.Condition)\\\
8540 joint(clause.Token, clause.Condition:GetFirstToken())\\\
8541 joint(clause.Condition:GetLastToken(), clause.Token_Then)\\\
8542 lastBodyOpen = clause.Token_Then\\\
8543 end\\\
8544 stripStat(clause.Body)\\\
8545 lastBody = clause.Body\\\
8546 end\\\
8547 --\\\
8548 bodyjoint(lastBodyOpen, lastBody, stat.Token_End)\\\
8549\\\
8550 elseif stat.Type == 'CallExprStat' then\\\
8551 stripExpr(stat.Expression)\\\
8552 elseif stat.Type == 'AssignmentStat' then\\\
8553 for index, ex in pairs(stat.Lhs) do\\\
8554 stripExpr(ex)\\\
8555 local sep = stat.Token_LhsSeparatorList[index]\\\
8556 if sep then\\\
8557 stript(sep)\\\
8558 end\\\
8559 end\\\
8560 stript(stat.Token_Equals)\\\
8561 for index, ex in pairs(stat.Rhs) do\\\
8562 stripExpr(ex)\\\
8563 local sep = stat.Token_RhsSeparatorList[index]\\\
8564 if sep then\\\
8565 stript(sep)\\\
8566 end\\\
8567 end\\\
8568 else\\\
8569 assert(false, \\\"unreachable\\\")\\\
8570 end\\\
8571 end\\\
8572\\\
8573 stripStat(ast)\\\
8574end\\\
8575\\\
8576local VarDigits = {}\\\
8577for i = ('a'):byte(), ('z'):byte() do table.insert(VarDigits, string.char(i)) end\\\
8578for i = ('A'):byte(), ('Z'):byte() do table.insert(VarDigits, string.char(i)) end\\\
8579for i = ('0'):byte(), ('9'):byte() do table.insert(VarDigits, string.char(i)) end\\\
8580table.insert(VarDigits, '_')\\\
8581local VarStartDigits = {}\\\
8582for i = ('a'):byte(), ('z'):byte() do table.insert(VarStartDigits, string.char(i)) end\\\
8583for i = ('A'):byte(), ('Z'):byte() do table.insert(VarStartDigits, string.char(i)) end\\\
8584local function indexToVarName(index)\\\
8585 local id = ''\\\
8586 local d = index % #VarStartDigits\\\
8587 index = (index - d) / #VarStartDigits\\\
8588 id = id..VarStartDigits[d+1]\\\
8589 while index > 0 do\\\
8590 local d = index % #VarDigits\\\
8591 index = (index - d) / #VarDigits\\\
8592 id = id..VarDigits[d+1]\\\
8593 end\\\
8594 return id\\\
8595end\\\
8596local function MinifyVariables(globalScope, rootScope)\\\
8597 -- externalGlobals is a set of global variables that have not been assigned to, that is\\\
8598 -- global variables defined \\\"externally to the script\\\". We are not going to be renaming\\\
8599 -- those, and we have to make sure that we don't collide with them when renaming\\\
8600 -- things so we keep track of them in this set.\\\
8601 local externalGlobals = {}\\\
8602\\\
8603 -- First we want to rename all of the variables to unique temoraries, so that we can\\\
8604 -- easily use the scope::GetVar function to check whether renames are valid.\\\
8605 local temporaryIndex = 0\\\
8606 for _, var in pairs(globalScope) do\\\
8607 if var.AssignedTo then\\\
8608 var:Rename('_TMP_'..temporaryIndex..'_')\\\
8609 temporaryIndex = temporaryIndex + 1\\\
8610 else\\\
8611 -- Not assigned to, external global\\\
8612 externalGlobals[var.Name] = true\\\
8613 end\\\
8614 end\\\
8615\\\
8616 -- Now we go through renaming, first do globals, we probably want them\\\
8617 -- to have shorter names in general.\\\
8618 -- TODO: Rename all vars based on frequency patterns, giving variables\\\
8619 -- used more shorter names.\\\
8620 local nextFreeNameIndex = 0\\\
8621 for _, var in pairs(globalScope) do\\\
8622 if var.AssignedTo then\\\
8623 local varName\\\
8624 repeat\\\
8625 varName = indexToVarName(nextFreeNameIndex)\\\
8626 nextFreeNameIndex = nextFreeNameIndex + 1\\\
8627 until not Keywords[varName] and not externalGlobals[varName]\\\
8628 var:Rename(varName)\\\
8629 end\\\
8630 end\\\
8631\\\
8632 -- Now rename all local vars\\\
8633 rootScope.FirstFreeName = nextFreeNameIndex\\\
8634 local function doRenameScope(scope)\\\
8635 for _, var in pairs(scope.VariableList) do\\\
8636 local varName\\\
8637 repeat\\\
8638 varName = indexToVarName(scope.FirstFreeName)\\\
8639 scope.FirstFreeName = scope.FirstFreeName + 1\\\
8640 until not Keywords[varName] and not externalGlobals[varName]\\\
8641 var:Rename(varName)\\\
8642 end\\\
8643 for _, childScope in pairs(scope.ChildScopeList) do\\\
8644 childScope.FirstFreeName = scope.FirstFreeName\\\
8645 doRenameScope(childScope)\\\
8646 end\\\
8647 end\\\
8648 doRenameScope(rootScope)\\\
8649end\\\
8650\\\
8651local function BeautifyVariables(globalScope, rootScope)\\\
8652 local localNumber = 1\\\
8653 local globalNumber = 1\\\
8654\\\
8655 local function setVarName(var, name)\\\
8656 var.Name = name\\\
8657 for _, setter in pairs(var.RenameList) do\\\
8658 setter(name)\\\
8659 end\\\
8660 end\\\
8661\\\
8662 for _, var in pairs(globalScope) do\\\
8663 if var.AssignedTo then\\\
8664 setVarName(var, 'G_'..globalNumber)\\\
8665 globalNumber = globalNumber + 1\\\
8666 end\\\
8667 end\\\
8668\\\
8669 local function modify(scope)\\\
8670 for _, var in pairs(scope.VariableList) do\\\
8671 local name = 'L_'..localNumber..'_'\\\
8672 if var.Info.Type == 'Argument' then\\\
8673 name = name..'arg'..var.Info.Index\\\
8674 elseif var.Info.Type == 'LocalFunction' then\\\
8675 name = name..'func'\\\
8676 elseif var.Info.Type == 'ForRange' then\\\
8677 name = name..'forvar'..var.Info.Index\\\
8678 end\\\
8679 setVarName(var, name)\\\
8680 localNumber = localNumber + 1\\\
8681 end\\\
8682 for _, scope in pairs(scope.ChildScopeList) do\\\
8683 modify(scope)\\\
8684 end\\\
8685 end\\\
8686 modify(rootScope)\\\
8687end\\\
8688\\\
8689local function usageError()\\\
8690 printError(\\\
8691 \\\"\\\\nusage: minify <file> or unminify <file>\\\\n\\\" ..\\\
8692 \\\" The modified code will be printed to the stdout, pipe it to a file, the\\\\n\\\" ..\\\
8693 \\\" lua interpreter, or something else as desired EG:\\\\n\\\\n\\\" ..\\\
8694 \\\" lua minify.lua minify input.lua > output.lua\\\\n\\\\n\\\" ..\\\
8695 \\\" * minify will minify the code in the file.\\\\n\\\" ..\\\
8696 \\\" * unminify will beautify the code and replace the variable names with easily\\\\n\\\" ..\\\
8697 \\\" find-replacable ones to aide in reverse engineering minified code.\\\\n\\\")\\\
8698end\\\
8699\\\
8700local args = {...}\\\
8701if #args ~= 2 then\\\
8702 usageError()\\\
8703 error('Invalid arguments')\\\
8704end\\\
8705\\\
8706local sourceFile = io.open(args[2], 'r')\\\
8707if not sourceFile then\\\
8708 error(\\\"Could not open the input file `\\\" .. args[2] .. \\\"`\\\")\\\
8709end\\\
8710\\\
8711local data = sourceFile:read('*all')\\\
8712sourceFile:close()\\\
8713\\\
8714local ast = CreateLuaParser(data)\\\
8715local global_scope, root_scope = AddVariableInfo(ast)\\\
8716local out = { }\\\
8717\\\
8718if args[1] == 'minify' then\\\
8719 MinifyVariables(global_scope, root_scope)\\\
8720 StripAst(ast)\\\
8721elseif args[1] == 'unminify' then\\\
8722 BeautifyVariables(global_scope, root_scope)\\\
8723 FormatAst(ast)\\\
8724else\\\
8725 usageError()\\\
8726end\\\
8727\\\
8728PrintAst(ast, out)\\\
8729\\\
8730local destFile = io.open(args[2], 'w')\\\
8731if not destFile then\\\
8732 error(\\\"Could not open the output file `\\\" .. args[3] .. \\\"`\\\")\\\
8733end\\\
8734destFile:write(table.concat(out))\\\
8735destFile:close()\",\
8736 [ \"common/Appstore.lua\" ] = \"local Ansi = require('opus.ansi')\\\
8737local SHA = require('opus.crypto.sha2')\\\
8738local UI = require('opus.ui')\\\
8739local Util = require('opus.util')\\\
8740\\\
8741local fs = _G.fs\\\
8742local http = _G.http\\\
8743local multishell = _ENV.multishell\\\
8744local os = _G.os\\\
8745local shell = _ENV.shell\\\
8746local colors = _G.colors\\\
8747\\\
8748local REGISTRY_DIR = 'usr/.registry'\\\
8749\\\
8750-- FIX SOMEDAY\\\
8751local function registerApp(app, key)\\\
8752 app.key = SHA.compute(key)\\\
8753 Util.writeTable(fs.combine(REGISTRY_DIR, app.key), app)\\\
8754 os.queueEvent('os_register_app')\\\
8755end\\\
8756\\\
8757local function unregisterApp(key)\\\
8758 local filename = fs.combine(REGISTRY_DIR, SHA.compute(key))\\\
8759 if fs.exists(filename) then\\\
8760 fs.delete(filename)\\\
8761 os.queueEvent('os_register_app')\\\
8762 end\\\
8763end\\\
8764\\\
8765multishell.setTitle(multishell.getCurrent(), 'App Store')\\\
8766UI:configure('Appstore', ...)\\\
8767\\\
8768local APP_DIR = 'usr/apps'\\\
8769\\\
8770local source = {\\\
8771 text = \\\"STD Default\\\",\\\
8772 event = 'source',\\\
8773 url = \\\"https://github.com/LDDestroier/STD-GUI/raw/master/list.lua\\\",\\\
8774}\\\
8775\\\
8776shell.setDir(APP_DIR)\\\
8777\\\
8778local function downloadApp(app)\\\
8779 local h\\\
8780\\\
8781 if type(app.url) == \\\"table\\\" then\\\
8782 h = contextualGet(app.url[1])\\\
8783 else\\\
8784 h = http.get(app.url)\\\
8785 end\\\
8786\\\
8787 if h then\\\
8788 local contents = h.readAll()\\\
8789 h:close()\\\
8790 return contents\\\
8791 end\\\
8792end\\\
8793\\\
8794local function runApp(app, checkExists, ...)\\\
8795 local env = shell.makeEnv(_ENV)\\\
8796 local path, fn\\\
8797 local args = { ... }\\\
8798\\\
8799 if checkExists and fs.exists(fs.combine(APP_DIR, app.name)) then\\\
8800 path = fs.combine(APP_DIR, app.name)\\\
8801 else\\\
8802 local program = downloadApp(app)\\\
8803\\\
8804 fn = function()\\\
8805\\\
8806 if not program then\\\
8807 error('Failed to download')\\\
8808 end\\\
8809\\\
8810 fn = _G.loadstring(program, app.name)\\\
8811\\\
8812 if not fn then\\\
8813 error('Failed to download')\\\
8814 end\\\
8815\\\
8816 _G.setfenv(fn, env)\\\
8817 fn(table.unpack(args))\\\
8818 end\\\
8819 end\\\
8820\\\
8821 multishell.openTab(_ENV, {\\\
8822 title = app.name,\\\
8823 path = path,\\\
8824 fn = fn,\\\
8825 focused = true,\\\
8826 })\\\
8827\\\
8828 return true, 'Running program'\\\
8829end\\\
8830\\\
8831local installApp = function(app)\\\
8832\\\
8833 local program = downloadApp(app)\\\
8834 if not program then\\\
8835 return false, \\\"Failed to download\\\"\\\
8836 end\\\
8837\\\
8838 local fullPath = fs.combine(APP_DIR, app.name)\\\
8839 Util.writeFile(fullPath, program)\\\
8840 return true, 'Installed as ' .. fullPath\\\
8841end\\\
8842\\\
8843local viewApp = function(app)\\\
8844\\\
8845 local program = downloadApp(app)\\\
8846 if not program then\\\
8847 return false, \\\"Failed to download\\\"\\\
8848 end\\\
8849\\\
8850 Util.writeFile('/.source', program)\\\
8851 shell.openForegroundTab('edit /.source')\\\
8852 fs.delete('/.source')\\\
8853 return true\\\
8854end\\\
8855\\\
8856local getSourceListing = function()\\\
8857 local contents = http.get(source.url)\\\
8858 if contents then\\\
8859\\\
8860 local fn = _G.loadstring(contents.readAll(), source.text)\\\
8861 contents.close()\\\
8862\\\
8863 local env = { std = { } }\\\
8864 setmetatable(env, { __index = _G })\\\
8865 _G.setfenv(fn, env)\\\
8866 fn()\\\
8867\\\
8868 if env.contextualGet then\\\
8869 contextualGet = env.contextualGet\\\
8870 end\\\
8871\\\
8872 source.storeURLs = env.std.storeURLs\\\
8873 source.storeCatagoryNames = env.std.storeCatagoryNames\\\
8874\\\
8875 if source.storeURLs and source.storeCatagoryNames then\\\
8876 for k,v in pairs(source.storeURLs) do\\\
8877 if source.generateName then\\\
8878 v.name = v.title:match('(%w+)')\\\
8879 if not v.name or #v.name == 0 then\\\
8880 v.name = tostring(k)\\\
8881 else\\\
8882 v.name = v.name:lower()\\\
8883 end\\\
8884 else\\\
8885 v.name = k\\\
8886 end\\\
8887 v.categoryName = source.storeCatagoryNames[v.catagory]\\\
8888 v.ltitle = v.title:lower()\\\
8889 end\\\
8890 end\\\
8891 end\\\
8892end\\\
8893\\\
8894getSourceListing()\\\
8895\\\
8896if not source.storeURLs then\\\
8897 error('Unable to download application list')\\\
8898end\\\
8899\\\
8900local buttons = { }\\\
8901for k,v in Util.spairs(source.storeCatagoryNames,\\\
8902 function(a, b) return a:lower() < b:lower() end) do\\\
8903\\\
8904 if v ~= 'Operating System' then\\\
8905 table.insert(buttons, {\\\
8906 text = v,\\\
8907 event = 'category',\\\
8908 index = k,\\\
8909 })\\\
8910 end\\\
8911end\\\
8912source.index, source.name = Util.first(source.storeCatagoryNames)\\\
8913\\\
8914local appPage = UI.Page {\\\
8915 menuBar = UI.MenuBar {\\\
8916 buttons = {\\\
8917 { text = '\\\\027', event = 'back' },\\\
8918 { text = 'Install', event = 'install' },\\\
8919 { text = 'Run', event = 'run' },\\\
8920 { text = 'View', event = 'view' },\\\
8921 { text = 'Remove', event = 'uninstall', name = 'removeButton' },\\\
8922 },\\\
8923 },\\\
8924 container = UI.Window {\\\
8925 x = 2, y = 3, ex = -2, ey = -3,\\\
8926 viewport = UI.Viewport(),\\\
8927 },\\\
8928 notification = UI.Notification(),\\\
8929 accelerators = {\\\
8930 [ 'control-q' ] = 'back',\\\
8931 backspace = 'back',\\\
8932 },\\\
8933}\\\
8934\\\
8935function appPage.container.viewport:draw()\\\
8936 local app = self.parent.parent.app\\\
8937 local str = string.format(\\\
8938 '%s \\\\nBy: %s \\\\nCategory: %s\\\\nFile name: %s\\\\n\\\\n%s',\\\
8939 Ansi.yellow .. app.title .. Ansi.reset,\\\
8940 app.creator,\\\
8941 app.categoryName, app.name,\\\
8942 Ansi.yellow .. app.description .. Ansi.reset)\\\
8943\\\
8944 self:clear()\\\
8945 self:print(str)\\\
8946\\\
8947 if appPage.notification.enabled then\\\
8948 appPage.notification:draw()\\\
8949 end\\\
8950end\\\
8951\\\
8952function appPage:enable(app)\\\
8953 self.source = source\\\
8954 self.app = app\\\
8955 UI.Page.enable(self)\\\
8956\\\
8957 self.container.viewport:setScrollPosition(0)\\\
8958 if fs.exists(fs.combine(APP_DIR, app.name)) then\\\
8959 self.menuBar.removeButton:enable('Remove')\\\
8960 else\\\
8961 self.menuBar.removeButton:disable('Remove')\\\
8962 end\\\
8963end\\\
8964\\\
8965function appPage:eventHandler(event)\\\
8966 if event.type == 'back' then\\\
8967 UI:setPreviousPage()\\\
8968\\\
8969 elseif event.type == 'run' then\\\
8970 self.notification:info('Running program', 3)\\\
8971 self:sync()\\\
8972 runApp(self.app, true)\\\
8973\\\
8974 elseif event.type == 'view' then\\\
8975 self.notification:info('Downloading program', 3)\\\
8976 self:sync()\\\
8977 viewApp(self.app)\\\
8978\\\
8979 elseif event.type == 'uninstall' then\\\
8980 if self.app.runOnly then\\\
8981 runApp(self.app, false, 'uninstall')\\\
8982 else\\\
8983 fs.delete(fs.combine(APP_DIR, self.app.name))\\\
8984 self.notification:success(\\\"Uninstalled \\\" .. self.app.name, 3)\\\
8985 self:focusFirst(self)\\\
8986 self.menuBar.removeButton:disable('Remove')\\\
8987 self.menuBar:draw()\\\
8988\\\
8989 unregisterApp(self.app.creator .. '.' .. self.app.name)\\\
8990 end\\\
8991\\\
8992 elseif event.type == 'install' then\\\
8993 self.notification:info(\\\"Installing\\\", 3)\\\
8994 self:sync()\\\
8995 local s, m\\\
8996 if self.app.runOnly then\\\
8997 s,m = runApp(self.app, false)\\\
8998 else\\\
8999 s,m = installApp(self.app)\\\
9000 end\\\
9001 if s then\\\
9002 self.notification:success(m, 3)\\\
9003\\\
9004 if not self.app.runOnly then\\\
9005 self.menuBar.removeButton:enable('Remove')\\\
9006 self.menuBar:draw()\\\
9007\\\
9008 local category = 'Apps'\\\
9009 if self.app.catagoryName == 'Game' then\\\
9010 category = 'Games'\\\
9011 end\\\
9012\\\
9013 registerApp({\\\
9014 run = fs.combine(APP_DIR, self.app.name),\\\
9015 title = self.app.title,\\\
9016 category = category,\\\
9017 icon = self.app.icon,\\\
9018 }, self.app.creator .. '.' .. self.app.name)\\\
9019 end\\\
9020 else\\\
9021 self.notification:error(m, 3)\\\
9022 end\\\
9023 else\\\
9024 return UI.Page.eventHandler(self, event)\\\
9025 end\\\
9026 return true\\\
9027end\\\
9028\\\
9029local categoryPage = UI.Page {\\\
9030 menuBar = UI.MenuBar {\\\
9031 buttons = {\\\
9032 { text = 'Category', name = 'categoryButton', dropdown = buttons },\\\
9033 },\\\
9034 },\\\
9035 grid = UI.ScrollingGrid {\\\
9036 y = 2, ey = -2,\\\
9037 columns = {\\\
9038 { heading = 'Title', key = 'title' },\\\
9039 },\\\
9040 sortColumn = 'title',\\\
9041 },\\\
9042 statusBar = UI.StatusBar(),\\\
9043 accelerators = {\\\
9044 l = 'lua',\\\
9045 [ 'control-q' ] = 'quit',\\\
9046 },\\\
9047 source = source,\\\
9048}\\\
9049\\\
9050function categoryPage:setCategory(name, index)\\\
9051 self.grid.values = { }\\\
9052 for _,v in pairs(source.storeURLs) do\\\
9053 if index == 0 or index == v.catagory then\\\
9054 table.insert(self.grid.values, v)\\\
9055 end\\\
9056 end\\\
9057 self.statusBar:setStatus(string.format('%s: %s', self.source.text or '', name))\\\
9058 self.grid:update()\\\
9059 self.grid:setIndex(1)\\\
9060end\\\
9061\\\
9062function categoryPage.grid:sortCompare(a, b)\\\
9063 return a.ltitle < b.ltitle\\\
9064end\\\
9065\\\
9066function categoryPage.grid:getRowTextColor(row, selected)\\\
9067 if fs.exists(fs.combine(APP_DIR, row.name)) then\\\
9068 return colors.orange\\\
9069 end\\\
9070 return UI.Grid:getRowTextColor(row, selected)\\\
9071end\\\
9072\\\
9073function categoryPage:eventHandler(event)\\\
9074 if event.type == 'grid_select' or event.type == 'select' then\\\
9075 UI:setPage(appPage, self.grid:getSelected())\\\
9076\\\
9077 elseif event.type == 'category' then\\\
9078 self:setCategory(event.button.text, event.button.index)\\\
9079 self:setFocus(self.grid)\\\
9080 self:draw()\\\
9081\\\
9082 elseif event.type == 'source' then\\\
9083 self:setFocus(self.grid)\\\
9084 self:setSource(event.button)\\\
9085 self:draw()\\\
9086\\\
9087 elseif event.type == 'quit' then\\\
9088 UI:quit()\\\
9089\\\
9090 else\\\
9091 return UI.Page.eventHandler(self, event)\\\
9092 end\\\
9093 return true\\\
9094end\\\
9095\\\
9096print(\\\"Retrieving catalog list\\\")\\\
9097categoryPage:setCategory(source.name, source.index)\\\
9098\\\
9099UI:setPage(categoryPage)\\\
9100UI:start()\",\
9101 [ \"common/Turtles.lua\" ] = \"local Config = require('opus.config')\\\
9102local Event = require('opus.event')\\\
9103local itemDB = require('core.itemDB')\\\
9104local Socket = require('opus.socket')\\\
9105local UI = require('opus.ui')\\\
9106local Util = require('opus.util')\\\
9107\\\
9108local fs = _G.fs\\\
9109local multishell = _ENV.multishell\\\
9110local network = _G.network\\\
9111local os = _G.os\\\
9112\\\
9113UI:configure('Turtles', ...)\\\
9114\\\
9115local config = { }\\\
9116Config.load('Turtles', config)\\\
9117\\\
9118local options = {\\\
9119 turtle = { arg = 'i', type = 'number', value = config.id or -1,\\\
9120 desc = 'Turtle ID' },\\\
9121 tab = { arg = 's', type = 'string', value = config.tab or 'Sel',\\\
9122 desc = 'Selected tab to display' },\\\
9123 help = { arg = 'h', type = 'flag', value = false,\\\
9124 desc = 'Displays the options' },\\\
9125}\\\
9126\\\
9127local SCRIPTS_PATH = 'packages/common/etc/scripts'\\\
9128\\\
9129local socket, turtle, page\\\
9130\\\
9131page = UI.Page {\\\
9132 coords = UI.Window {\\\
9133 backgroundColor = 'black',\\\
9134 height = 3,\\\
9135 marginTop = 1, marginLeft = 1,\\\
9136 draw = function(self)\\\
9137 local t = turtle\\\
9138 self:clear()\\\
9139 if t then\\\
9140 self:setCursorPos(2, 2)\\\
9141 local ind = 'GPS'\\\
9142 if not t.point.gps then\\\
9143 ind = 'REL'\\\
9144 end\\\
9145 self:print(string.format('%s : %d,%d,%d',\\\
9146 ind, t.point.x, t.point.y, t.point.z))\\\
9147 end\\\
9148 end,\\\
9149 },\\\
9150 tabs = UI.Tabs {\\\
9151 x = 1, y = 4, ey = -2,\\\
9152 UI.Tab {\\\
9153 title = 'Run',\\\
9154 scripts = UI.ScrollingGrid {\\\
9155 backgroundColor = 'primary',\\\
9156 columns = {\\\
9157 { heading = '', key = 'label' },\\\
9158 },\\\
9159 disableHeader = true,\\\
9160 sortColumn = 'label',\\\
9161 autospace = true,\\\
9162 draw = function(self)\\\
9163 Util.clear(self.values)\\\
9164 local files = fs.list(SCRIPTS_PATH)\\\
9165 for _,path in pairs(files) do\\\
9166 table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) })\\\
9167 end\\\
9168 self:update()\\\
9169 UI.ScrollingGrid.draw(self)\\\
9170 end,\\\
9171 eventHandler = function(self, event)\\\
9172 if event.type == 'grid_select' then\\\
9173 page:runScript(event.selected.label)\\\
9174 else\\\
9175 return UI.ScrollingGrid.eventHandler(self, event)\\\
9176 end\\\
9177 return true\\\
9178 end,\\\
9179 },\\\
9180 },\\\
9181 UI.Tab {\\\
9182 title = 'Select',\\\
9183 turtles = UI.ScrollingGrid {\\\
9184 backgroundColor = 'primary',\\\
9185 columns = {\\\
9186 { heading = 'label', key = 'label' },\\\
9187 { heading = 'Dist', key = 'distance' },\\\
9188 { heading = 'Status', key = 'status' },\\\
9189 { heading = 'Fuel', key = 'fuel' },\\\
9190 },\\\
9191 disableHeader = true,\\\
9192 sortColumn = 'label',\\\
9193 autospace = true,\\\
9194 getDisplayValues = function(_, row)\\\
9195 row = Util.shallowCopy(row)\\\
9196 if row.fuel then\\\
9197 row.fuel = Util.toBytes(row.fuel)\\\
9198 end\\\
9199 if row.distance then\\\
9200 row.distance = Util.round(row.distance, 1)\\\
9201 end\\\
9202 return row\\\
9203 end,\\\
9204 draw = function(self)\\\
9205 Util.clear(self.values)\\\
9206 for _,v in pairs(network) do\\\
9207 if v.fuel then\\\
9208 table.insert(self.values, v)\\\
9209 end\\\
9210 end\\\
9211 self:update()\\\
9212 UI.ScrollingGrid.draw(self)\\\
9213 end,\\\
9214 eventHandler = function(self, event)\\\
9215 if event.type == 'grid_select' then\\\
9216 turtle = event.selected\\\
9217 config.id = event.selected.id\\\
9218 Config.update('Turtles', config)\\\
9219 multishell.setTitle(multishell.getCurrent(), turtle.label)\\\
9220 if socket then\\\
9221 socket:close()\\\
9222 socket = nil\\\
9223 end\\\
9224 else\\\
9225 return UI.ScrollingGrid.eventHandler(self, event)\\\
9226 end\\\
9227 return true\\\
9228 end,\\\
9229 },\\\
9230 },\\\
9231 UI.Tab {\\\
9232 title = 'Inv',\\\
9233 inventory = UI.ScrollingGrid {\\\
9234 backgroundColor = 'primary',\\\
9235 columns = {\\\
9236 { heading = '', key = 'index', width = 2 },\\\
9237 { heading = '', key = 'count', width = 2 },\\\
9238 { heading = 'Inventory', key = 'key' },\\\
9239 },\\\
9240 disableHeader = true,\\\
9241 sortColumn = 'index',\\\
9242 getRowTextColor = function(self, row, selected)\\\
9243 if turtle and row.selected then\\\
9244 return 'yellow'\\\
9245 end\\\
9246 return UI.ScrollingGrid.getRowTextColor(self, row, selected)\\\
9247 end,\\\
9248 draw = function(self)\\\
9249 local t = turtle\\\
9250 Util.clear(self.values)\\\
9251 if t then\\\
9252 for k,v in pairs(t.inv or { }) do -- new method (less data)\\\
9253 local index, count = k:match('(%d+),(%d+)')\\\
9254 v = {\\\
9255 index = tonumber(index),\\\
9256 key = v,\\\
9257 count = tonumber(count),\\\
9258 }\\\
9259 table.insert(self.values, v)\\\
9260 end\\\
9261\\\
9262 for _,v in pairs(t.inventory or { }) do\\\
9263 if v.count > 0 then\\\
9264 table.insert(self.values, v)\\\
9265 end\\\
9266 end\\\
9267\\\
9268 for _,v in pairs(self.values) do\\\
9269 if v.index == t.slotIndex then\\\
9270 v.selected = true\\\
9271 end\\\
9272 if v.key then\\\
9273 v.key = itemDB:getName(v.key)\\\
9274 end\\\
9275 end\\\
9276 end\\\
9277 self:adjustWidth()\\\
9278 self:update()\\\
9279 UI.ScrollingGrid.draw(self)\\\
9280 end,\\\
9281 eventHandler = function(self, event)\\\
9282 if event.type == 'grid_select' then\\\
9283 local fn = string.format('turtle.select(%d)', event.selected.index)\\\
9284 page:runFunction(fn)\\\
9285 else\\\
9286 return UI.ScrollingGrid.eventHandler(self, event)\\\
9287 end\\\
9288 return true\\\
9289 end,\\\
9290 },\\\
9291 },\\\
9292 UI.Tab {\\\
9293 title = 'Action',\\\
9294 backgroundColor = 'primary',\\\
9295 moveUp = UI.Button {\\\
9296 x = 5, y = 2,\\\
9297 text = 'up',\\\
9298 fn = 'turtle.up',\\\
9299 },\\\
9300 moveDown = UI.Button {\\\
9301 x = 5, y = 4,\\\
9302 text = 'dn',\\\
9303 fn = 'turtle.down',\\\
9304 },\\\
9305 moveForward = UI.Button {\\\
9306 x = 9, y = 3,\\\
9307 text = 'f',\\\
9308 fn = 'turtle.forward',\\\
9309 },\\\
9310 moveBack = UI.Button {\\\
9311 x = 2, y = 3,\\\
9312 text = 'b',\\\
9313 fn = 'turtle.back',\\\
9314 },\\\
9315 turnLeft = UI.Button {\\\
9316 x = 2, y = 6,\\\
9317 text = 'lt',\\\
9318 fn = 'turtle.turnLeft',\\\
9319 },\\\
9320 turnRight = UI.Button {\\\
9321 x = 8, y = 6,\\\
9322 text = 'rt',\\\
9323 fn = 'turtle.turnRight',\\\
9324 },\\\
9325 info = UI.TextArea {\\\
9326 x = 15, y = 1,\\\
9327 inactive = true,\\\
9328 },\\\
9329 showBlocks = function(self)\\\
9330 local script = [[\\\
9331 local function inspect(direction)\\\
9332 local s,b = turtle['inspect' .. (direction or '')]()\\\
9333 if not s then\\\
9334 return 'minecraft:air:0'\\\
9335 end\\\
9336 return string.format('%s:%d', b.name, b.metadata)\\\
9337 end\\\
9338\\\
9339 local bu, bf, bd = inspect('Up'), inspect(), inspect('Down')\\\
9340 return string.format('%s\\\\n%s\\\\n%s', bu, bf, bd)\\\
9341 ]]\\\
9342\\\
9343 local s, m = page:runFunction(script, true)\\\
9344 self.info:setText(s or m)\\\
9345 end,\\\
9346 eventHandler = function(self, event)\\\
9347 if event.type == 'button_press' then\\\
9348 if event.button.fn then\\\
9349 page:runFunction(event.button.fn, event.button.nowrap)\\\
9350 self:showBlocks()\\\
9351 end\\\
9352 return true\\\
9353 end\\\
9354 return UI.Tab.eventHandler(self, event)\\\
9355 end,\\\
9356 },\\\
9357 enable = function(self)\\\
9358 if config.tab then\\\
9359 self:selectTab(Util.find(self, 'title', config.tab))\\\
9360 end\\\
9361 UI.Tabs.enable(self)\\\
9362 end\\\
9363 },\\\
9364 statusBar = UI.StatusBar {\\\
9365 values = { },\\\
9366 columns = {\\\
9367 { key = 'status' },\\\
9368 { key = 'distance', width = 6 },\\\
9369 { key = 'fuel', width = 6 },\\\
9370 },\\\
9371 draw = function(self)\\\
9372 local t = turtle\\\
9373 if t then\\\
9374 self.values.status = t.status\\\
9375 self.values.distance = t.distance and Util.round(t.distance, 2)\\\
9376 self.values.fuel = Util.toBytes(t.fuel)\\\
9377 end\\\
9378 UI.StatusBar.draw(self)\\\
9379 end,\\\
9380 },\\\
9381 notification = UI.Notification(),\\\
9382 accelerators = {\\\
9383 [ 'control-q' ] = 'quit',\\\
9384 },\\\
9385}\\\
9386\\\
9387function page:runFunction(script, nowrap)\\\
9388 for _ = 1, 2 do\\\
9389 if not socket then\\\
9390 socket = Socket.connect(turtle.id, 161)\\\
9391 end\\\
9392\\\
9393 if socket then\\\
9394 if not nowrap then\\\
9395 script = 'turtle.run(' .. script .. ')'\\\
9396 end\\\
9397 if socket:write({ type = 'scriptEx', args = script }) then\\\
9398 local t = socket:read(3)\\\
9399 if t then\\\
9400 return table.unpack(t)\\\
9401 end\\\
9402 return false, 'Socket timeout'\\\
9403 end\\\
9404 end\\\
9405 socket = nil\\\
9406 end\\\
9407 self.notification:error('Unable to connect')\\\
9408end\\\
9409\\\
9410function page:runScript(scriptName)\\\
9411 if turtle then\\\
9412 self.notification:info('Connecting')\\\
9413 self:sync()\\\
9414\\\
9415 local script = Util.readFile(fs.combine(SCRIPTS_PATH, scriptName))\\\
9416 if not script then\\\
9417 print('Unable to read script file')\\\
9418 end\\\
9419\\\
9420 local function processVariables()\\\
9421 local variables = {\\\
9422 COMPUTER_ID = os.getComputerID,\\\
9423 GPS = function()\\\
9424 local pt = require('opus.gps').getPoint()\\\
9425 if not pt then\\\
9426 error('Unable to determine location')\\\
9427 end\\\
9428 return _G.textutils.serialize(pt)\\\
9429 end,\\\
9430 }\\\
9431 for k,v in pairs(variables) do\\\
9432 local token = string.format('{%s}', k)\\\
9433 if script:find(token, 1, true) then\\\
9434 local s, m = pcall(v)\\\
9435 if not s then\\\
9436 self.notification:error(m)\\\
9437 return\\\
9438 end\\\
9439 script = script:gsub(token, m)\\\
9440 end\\\
9441 end\\\
9442 return true\\\
9443 end\\\
9444\\\
9445 if processVariables(script) then\\\
9446 local socket = Socket.connect(turtle.id, 161)\\\
9447 if not socket then\\\
9448 self.notification:error('Unable to connect')\\\
9449 return\\\
9450 end\\\
9451 socket:write({ type = 'script', args = script })\\\
9452 socket:close()\\\
9453\\\
9454 self.notification:success('Sent')\\\
9455 end\\\
9456 end\\\
9457end\\\
9458\\\
9459function page:eventHandler(event)\\\
9460 if event.type == 'quit' then\\\
9461 UI:quit()\\\
9462\\\
9463 elseif event.type == 'tab_select' then\\\
9464 config.tab = event.button.text\\\
9465 Config.update('Turtles', config)\\\
9466\\\
9467 else\\\
9468 return UI.Page.eventHandler(self, event)\\\
9469 end\\\
9470 return true\\\
9471end\\\
9472\\\
9473if not Util.getOptions(options, { ... }, true) then\\\
9474 return\\\
9475end\\\
9476\\\
9477if options.turtle.value >= 0 then\\\
9478 for _ = 1, 10 do\\\
9479 turtle = _G.network[options.turtle.value]\\\
9480 if turtle then\\\
9481 break\\\
9482 end\\\
9483 os.sleep(1)\\\
9484 end\\\
9485end\\\
9486\\\
9487Event.onInterval(1, function()\\\
9488 if turtle then\\\
9489 --local t = _G.network[turtle.id]\\\
9490 --turtle = t\\\
9491 page:draw()\\\
9492 page:sync()\\\
9493 end\\\
9494end)\\\
9495\\\
9496UI:setPage(page)\\\
9497UI:start()\",\
9498 [ \"core/apis/swarm.lua\" ] = \"local class = require('opus.class')\\\
9499local Event = require('opus.event')\\\
9500local Map = require('opus.map')\\\
9501local Proxy = require('core.proxy')\\\
9502\\\
9503local Swarm = class()\\\
9504function Swarm:init(args)\\\
9505 self.pool = { }\\\
9506 Map.merge(self, args)\\\
9507end\\\
9508\\\
9509function Swarm:add(id, args)\\\
9510 local member = Map.shallowCopy(args or { })\\\
9511 member.id = id\\\
9512 self.pool[id] = member\\\
9513end\\\
9514\\\
9515function Swarm:remove(id, s, m)\\\
9516 local member = self.pool[id]\\\
9517 if member then\\\
9518 self.pool[id] = nil\\\
9519 self:onRemove(member, s, m)\\\
9520 if member.socket then\\\
9521 member.socket:close()\\\
9522 member.socket = nil\\\
9523 end\\\
9524 if member.handler then\\\
9525 member.handler:terminate()\\\
9526 member.handler = nil\\\
9527 end\\\
9528 end\\\
9529end\\\
9530\\\
9531function Swarm:run(fn)\\\
9532 for id, member in pairs(self.pool) do\\\
9533 if not member.socket then\\\
9534 member.handler = Event.addRoutine(function()\\\
9535 local s, m = pcall(function()\\\
9536 member.turtle, member.socket = Proxy.create(id, 'turtle')\\\
9537\\\
9538 fn(member)\\\
9539 end)\\\
9540 self:remove(id, s, m)\\\
9541 end)\\\
9542 end\\\
9543 end\\\
9544end\\\
9545\\\
9546function Swarm:stop()\\\
9547 for _, member in pairs(self.pool) do\\\
9548 if member.socket then\\\
9549 member.socket:close()\\\
9550 member.socket = nil\\\
9551 end\\\
9552 end\\\
9553end\\\
9554\\\
9555-- Override\\\
9556function Swarm:onRemove(member, success, msg)\\\
9557 print('removed from pool: ' .. member.id)\\\
9558 if not success then\\\
9559 _G.printError(msg)\\\
9560 end\\\
9561end\\\
9562\\\
9563return Swarm\",\
9564 [ \"common/etc/scripts/moveTo.lua\" ] = \"local turtle = _G.turtle\\\
9565\\\
9566turtle.run(function()\\\
9567 local GPS = require('opus.gps')\\\
9568\\\
9569 if not turtle.enableGPS() then\\\
9570 error('turtle: No GPS found')\\\
9571 end\\\
9572\\\
9573 local pt = {GPS}\\\
9574\\\
9575 if not turtle.pathfind(pt) then\\\
9576 error('Unable to go to location')\\\
9577 end\\\
9578end)\",\
9579 [ \"monitor/mirror.lua\" ] = \"local Terminal = require('opus.terminal')\\\
9580local Util = require('opus.util')\\\
9581\\\
9582local device = _G.device\\\
9583local os = _G.os\\\
9584local parallel = _G.parallel\\\
9585local shell = _ENV.shell\\\
9586local term = _G.term\\\
9587\\\
9588-- Example usage: mirror -r -e \\\"vnc 1\\\"\\\
9589\\\
9590local options = {\\\
9591 scale = { arg = 's', type = 'flag', value = false,\\\
9592 desc = 'Set monitor to .5 text scaling' },\\\
9593 resize = { arg = 'r', type = 'flag', value = false,\\\
9594 desc = 'Resize terminal to monitor size' },\\\
9595 execute = { arg = 'e', type = 'string',\\\
9596 desc = 'Execute a program' },\\\
9597 monitor = { arg = 'm', type = 'string', value = 'monitor',\\\
9598 desc = 'Name of monitor' },\\\
9599 help = { arg = 'h', type = 'flag', value = false,\\\
9600 desc = 'Displays the options' },\\\
9601}\\\
9602\\\
9603local args = { ... }\\\
9604if not Util.getOptions(options, args) then\\\
9605 return\\\
9606end\\\
9607\\\
9608local mon\\\
9609for k,v in pairs(device) do\\\
9610 if k == options.monitor.value or v.side == options.monitor.value then\\\
9611 mon = v\\\
9612 break\\\
9613 end\\\
9614end\\\
9615\\\
9616if not mon then\\\
9617 error('mirror: Invalid device')\\\
9618end\\\
9619\\\
9620mon.clear()\\\
9621\\\
9622mon.setTextScale(options.scale.value and .5 or 1)\\\
9623mon.setCursorPos(1, 1)\\\
9624\\\
9625local oterm = term.current()\\\
9626\\\
9627if options.resize.value then\\\
9628 oterm.reposition(1, 1, mon.getSize())\\\
9629end\\\
9630\\\
9631local mirror = Terminal.mirror(term.current(), mon)\\\
9632\\\
9633term.redirect(mirror)\\\
9634\\\
9635if options.execute.value then\\\
9636 parallel.waitForAny(\\\
9637 function()\\\
9638 shell.run(options.execute.value)\\\
9639 end,\\\
9640\\\
9641 function()\\\
9642 while true do\\\
9643 local event, side, x, y = os.pullEventRaw('monitor_touch')\\\
9644\\\
9645 if event == 'monitor_touch' and side == mon.side then\\\
9646 os.queueEvent('mouse_click', 1, x, y + 1)\\\
9647 os.queueEvent('mouse_up', 1, x, y + 1)\\\
9648 end\\\
9649 end\\\
9650 end\\\
9651 )\\\
9652\\\
9653 term.redirect(oterm)\\\
9654\\\
9655 mon.setCursorBlink(false)\\\
9656end\",\
9657 [ \"common/etc/sounds.txt\" ] = \"block.anvil.break\\\
9658block.anvil.destroy\\\
9659block.anvil.fall\\\
9660block.anvil.hit\\\
9661block.anvil.land\\\
9662block.anvil.place\\\
9663block.anvil.step\\\
9664block.anvil.use\\\
9665block.boat.place\\\
9666entity.boat.paddle_land\\\
9667entity.boat.paddle_water\\\
9668block.brewing_stand.brew\\\
9669block.chest.close\\\
9670block.chest.locked\\\
9671block.chest.open\\\
9672block.cloth.break\\\
9673block.cloth.fall\\\
9674block.cloth.hit\\\
9675block.cloth.place\\\
9676block.cloth.step\\\
9677block.comparator.click\\\
9678block.dispenser.dispense\\\
9679block.dispenser.fail\\\
9680block.dispenser.launch\\\
9681block.enchantment_table.use\\\
9682block.end_gateway.spawn\\\
9683block.end_portal.spawn\\\
9684block.end_portal_frame.fill\\\
9685block.enderchest.close\\\
9686block.enderchest.open\\\
9687block.fence_gate.close\\\
9688block.fence_gate.open\\\
9689block.fire.ambient\\\
9690block.fire.extinguish\\\
9691block.furnace.fire_crackle\\\
9692block.glass.break\\\
9693block.glass.fall\\\
9694block.glass.hit\\\
9695block.glass.place\\\
9696block.glass.step\\\
9697block.grass.break\\\
9698block.grass.fall\\\
9699block.grass.hit\\\
9700block.grass.place\\\
9701block.grass.step\\\
9702block.gravel.break\\\
9703block.gravel.fall\\\
9704block.gravel.hit\\\
9705block.gravel.place\\\
9706block.gravel.step\\\
9707block.iron_door.close\\\
9708block.iron_door.open\\\
9709block.ladder.break\\\
9710block.ladder.fall\\\
9711block.ladder.hit\\\
9712block.ladder.place\\\
9713block.ladder.step\\\
9714block.lava.ambient\\\
9715block.lava.extinguish\\\
9716block.lava.pop\\\
9717block.lever.click\\\
9718block.metal.break\\\
9719block.metal.fall\\\
9720block.metal.hit\\\
9721block.metal.place\\\
9722block.metal.step\\\
9723block.metal_pressureplate.click_off\\\
9724block.metal_pressureplate.click_on\\\
9725block.note.basedrum\\\
9726block.note.bass\\\
9727block.note.bell\\\
9728block.note.chime\\\
9729block.note.flute\\\
9730block.note.guitar\\\
9731block.note.harp\\\
9732block.note.hat\\\
9733block.note.pling\\\
9734block.note.snare\\\
9735block.note.xylophone\\\
9736block.piston.contract\\\
9737block.piston.extend\\\
9738block.portal.ambient\\\
9739block.portal.travel\\\
9740block.portal.trigger\\\
9741block.redstone_torch.burnout\\\
9742block.sand.break\\\
9743block.sand.fall\\\
9744block.sand.hit\\\
9745block.sand.place\\\
9746block.sand.step\\\
9747block.shulker_box.close\\\
9748block.shulker_box.open\\\
9749block.slime.break\\\
9750block.slime.fall\\\
9751block.slime.hit\\\
9752block.slime.place\\\
9753block.slime.step\\\
9754block.snow.break\\\
9755block.snow.fall\\\
9756block.snow.hit\\\
9757block.snow.place\\\
9758block.snow.step\\\
9759block.stone.break\\\
9760block.stone.fall\\\
9761block.stone.hit\\\
9762block.stone.place\\\
9763block.stone.step\\\
9764block.stone_button.click_off\\\
9765block.stone_button.click_on\\\
9766block.stone_pressureplate.click_off\\\
9767block.stone_pressureplate.click_on\\\
9768block.wooden_trapdoor.close\\\
9769block.wooden_trapdoor.open\\\
9770block.iron_trapdoor.close\\\
9771block.iron_trapdoor.open\\\
9772block.tripwire.attach\\\
9773block.tripwire.click_off\\\
9774block.tripwire.click_on\\\
9775block.tripwire.detach\\\
9776block.water.ambient\\\
9777block.waterlily.place\\\
9778block.wood.break\\\
9779block.wood.fall\\\
9780block.wood.hit\\\
9781block.wood.place\\\
9782block.wood.step\\\
9783block.wood_button.click_off\\\
9784block.wood_button.click_on\\\
9785block.wood_pressureplate.click_off\\\
9786block.wood_pressureplate.click_on\\\
9787block.wooden_door.close\\\
9788block.wooden_door.open\\\
9789entity.arrow.hit\\\
9790entity.arrow.shoot\\\
9791entity.arrow.successful_hit\\\
9792entity.bat.ambient\\\
9793entity.armorstand.break\\\
9794entity.armorstand.fall\\\
9795entity.armorstand.hit\\\
9796entity.armorstand.place\\\
9797entity.bat.death\\\
9798entity.bat.hurt\\\
9799entity.bat.takeoff\\\
9800entity.blaze.ambient\\\
9801entity.blaze.burn\\\
9802entity.blaze.death\\\
9803entity.blaze.hurt\\\
9804entity.blaze.shoot\\\
9805entity.bobber.splash\\\
9806entity.bobber.throw\\\
9807entity.bobber.retrieve\\\
9808entity.cat.ambient\\\
9809entity.cat.death\\\
9810entity.cat.hiss\\\
9811entity.cat.hurt\\\
9812entity.cat.purr\\\
9813entity.cat.purreow\\\
9814entity.chicken.ambient\\\
9815entity.chicken.death\\\
9816entity.chicken.egg\\\
9817entity.chicken.hurt\\\
9818entity.chicken.step\\\
9819entity.cow.ambient\\\
9820entity.cow.death\\\
9821entity.cow.hurt\\\
9822entity.cow.milk\\\
9823entity.cow.step\\\
9824entity.creeper.death\\\
9825entity.creeper.hurt\\\
9826entity.creeper.primed\\\
9827entity.donkey.ambient\\\
9828entity.donkey.angry\\\
9829entity.donkey.chest\\\
9830entity.donkey.death\\\
9831entity.donkey.hurt\\\
9832entity.egg.throw\\\
9833entity.elder_guardian.ambient\\\
9834entity.elder_guardian.ambient_land\\\
9835entity.elder_guardian.curse\\\
9836entity.elder_guardian.death\\\
9837entity.elder_guardian.death_land\\\
9838entity.elder_guardian.hurt\\\
9839entity.elder_guardian.hurt_land\\\
9840entity.enderdragon.ambient\\\
9841entity.enderdragon.death\\\
9842entity.enderdragon.flap\\\
9843entity.enderdragon.growl\\\
9844entity.enderdragon.hurt\\\
9845entity.enderdragon.shoot\\\
9846entity.enderdragon_fireball.explode\\\
9847entity.endereye.launch\\\
9848entity.endereye.death\\\
9849entity.endermen.death\\\
9850entity.endermen.hurt\\\
9851entity.endermen.scream\\\
9852entity.endermen.stare\\\
9853entity.endermen.teleport\\\
9854entity.endermite.ambient\\\
9855entity.endermite.death\\\
9856entity.endermite.hurt\\\
9857entity.endermite.step\\\
9858entity.enderpearl.throw\\\
9859entity.evokation_illager.ambient\\\
9860entity.evokation_illager.hurt\\\
9861entity.evokation_illager.death\\\
9862entity.evokation_illager.cast_spell\\\
9863entity.evokation_illager.prepare_attack\\\
9864entity.evokation_illager.prepare_summon\\\
9865entity.evokation_illager.cast_spell\\\
9866entity.evokation_fangs.attack\\\
9867entity.experience_bottle.throw\\\
9868entity.experience_orb.pickup\\\
9869entity.firework.blast\\\
9870entity.firework.blast_far\\\
9871entity.firework.large_blast\\\
9872entity.firework.large_blast_far\\\
9873entity.firework.launch\\\
9874entity.firework.shoot\\\
9875entity.firework.twinkle\\\
9876entity.firework.twinkle_far\\\
9877entity.generic.big_fall\\\
9878entity.generic.burn\\\
9879entity.generic.death\\\
9880entity.generic.drink\\\
9881entity.generic.eat\\\
9882entity.generic.explode\\\
9883entity.generic.extinguish_fire\\\
9884entity.generic.hurt\\\
9885entity.generic.small_fall\\\
9886entity.generic.splash\\\
9887entity.generic.swim\\\
9888entity.ghast.ambient\\\
9889entity.ghast.death\\\
9890entity.ghast.hurt\\\
9891entity.ghast.shoot\\\
9892entity.ghast.warn\\\
9893entity.guardian.ambient\\\
9894entity.guardian.ambient_land\\\
9895entity.guardian.attack\\\
9896entity.guardian.death\\\
9897entity.guardian.death_land\\\
9898entity.guardian.flop\\\
9899entity.guardian.hurt\\\
9900entity.guardian.hurt_land\\\
9901entity.horse.ambient\\\
9902entity.horse.angry\\\
9903entity.horse.armor\\\
9904entity.horse.breathe\\\
9905entity.horse.death\\\
9906entity.horse.eat\\\
9907entity.horse.gallop\\\
9908entity.horse.hurt\\\
9909entity.horse.jump\\\
9910entity.horse.land\\\
9911entity.horse.saddle\\\
9912entity.horse.step\\\
9913entity.horse.step_wood\\\
9914entity.hostile.big_fall\\\
9915entity.hostile.death\\\
9916entity.hostile.hurt\\\
9917entity.hostile.hurt\\\
9918entity.hostile.splash\\\
9919entity.hostile.swim\\\
9920entity.husk.ambient\\\
9921entity.husk.death\\\
9922entity.husk.hurt\\\
9923entity.husk.step\\\
9924entity.illusion_illager.ambient\\\
9925entity.illusion_illager.cast_spell\\\
9926entity.illusion_illager.death\\\
9927entity.illusion_illager.hurt\\\
9928entity.illusion_illager.mirror_moveentity.illusion_illager.prepare_blindness\\\
9929entity.illusion_illager.prepare_mirror\\\
9930entity.irongolem.attack\\\
9931entity.irongolem.death\\\
9932entity.irongolem.hurt\\\
9933entity.irongolem.step\\\
9934entity.item.break\\\
9935entity.item.pickup\\\
9936entity.itemframe.add_item\\\
9937entity.itemframe.break\\\
9938entity.itemframe.place\\\
9939entity.itemframe.remove_item\\\
9940entity.itemframe.rotate_item\\\
9941entity.llama.ambient\\\
9942entity.llama.angry\\\
9943entity.llama.death\\\
9944entity.llama.eat\\\
9945entity.llama.hurt\\\
9946entity.llama.spit\\\
9947entity.llama.step\\\
9948entity.llama.swag\\\
9949entity.leashknot.break\\\
9950entity.leashknot.place\\\
9951entity.lightning.impact\\\
9952entity.lightning.thunder\\\
9953entity.lingeringpotion.throw\\\
9954entity.magmacube.death\\\
9955entity.magmacube.hurt\\\
9956entity.magmacube.jump\\\
9957entity.magmacube.squish\\\
9958entity.minecart.inside\\\
9959entity.minecart.riding\\\
9960entity.mooshroom.shear\\\
9961entity.mule.ambient\\\
9962entity.mule.death\\\
9963entity.mule.hurt\\\
9964entity.painting.break\\\
9965entity.painting.place\\\
9966entity.parrot.ambient\\\
9967entity.parrot.death\\\
9968entity.parrot.eat\\\
9969entity.parrot.fly\\\
9970entity.parrot.hurt\\\
9971entity.parrot.step\\\
9972entity.pig.ambient\\\
9973entity.pig.death\\\
9974entity.pig.hurt\\\
9975entity.pig.saddle\\\
9976entity.pig.step\\\
9977entity.player.attack.crit\\\
9978entity.player.attack.knockback\\\
9979entity.player.attack.nodamage\\\
9980entity.player.attack.strong\\\
9981entity.player.attack.sweep\\\
9982entity.player.attack.weak\\\
9983entity.player.big_fall\\\
9984entity.player.burp\\\
9985entity.player.death\\\
9986entity.player.hurt\\\
9987entity.player.levelup\\\
9988entity.player.small_fall\\\
9989entity.player.splash\\\
9990entity.player.swim\\\
9991entity.polar_bear.ambient\\\
9992entity.polar_bear.baby_ambient\\\
9993entity.polar_bear.death\\\
9994entity.polar_bear.hurt\\\
9995entity.polar_bear.step\\\
9996entity.polar_bear.warning\\\
9997entity.rabbit.attack\\\
9998entity.rabbit.ambient\\\
9999entity.rabbit.death\\\
10000entity.rabbit.hurt\\\
10001entity.rabbit.jump\\\
10002entity.sheep.death\\\
10003entity.sheep.hurt\\\
10004entity.sheep.shear\\\
10005entity.sheep.step\\\
10006entity.shield.break\\\
10007entity.shield.block\\\
10008entity.shulker.ambient\\\
10009entity.shulker_bullet.hit\\\
10010entity.shulker_bullet.hurt\\\
10011entity.shulker.death\\\
10012entity.shulker.close\\\
10013entity.shulker.hit\\\
10014entity.shulker.hurt\\\
10015entity.shulker.hurt_closed\\\
10016entity.shulker.shoot\\\
10017entity.shulker.teleport\\\
10018entity.silverfish.ambient\\\
10019entity.silverfish.death\\\
10020entity.silverfish.hurt\\\
10021entity.silverfish.step\\\
10022entity.skeleton.ambient\\\
10023entity.skeleton.death\\\
10024entity.skeleton.hurt\\\
10025entity.skeleton.shoot\\\
10026entity.skeleton.step\\\
10027entity.skeleton_horse.ambient\\\
10028entity.skeleton_horse.death\\\
10029entity.skeleton_horse.hurt\\\
10030entity.slime.attack\\\
10031entity.slime.death\\\
10032entity.slime.hurt\\\
10033entity.slime.jump\\\
10034entity.slime.squish\\\
10035entity.small_magmacube.death\\\
10036entity.small_magmacube.hurt\\\
10037entity.small_magmacube.squish\\\
10038entity.small_slime.death\\\
10039entity.small_slime.hurt\\\
10040entity.small_slime.squish\\\
10041entity.snowball.throw\\\
10042entity.snowman.ambient\\\
10043entity.snowman.death\\\
10044entity.snowman.hurt\\\
10045entity.snowman.shoot\\\
10046entity.spider.ambient\\\
10047entity.spider.death\\\
10048entity.spider.hurt\\\
10049entity.spider.step\\\
10050entity.splash_potion.break\\\
10051entity.splash_potion.throw\\\
10052entity.squid.ambient\\\
10053entity.squid.death\\\
10054entity.squid.hurt\\\
10055entity.stray.ambient\\\
10056entity.stray.death\\\
10057entity.stray.hurt\\\
10058entity.stray.step\\\
10059entity.tnt.primed\\\
10060entity.vex.ambient\\\
10061entity.vex.charge\\\
10062entity.vex.hurt\\\
10063entity.vex.death\\\
10064entity.vindication_illager.ambient\\\
10065entity.vindication_illager.hurt\\\
10066entity.vindication_illager.death\\\
10067entity.villager.ambient\\\
10068entity.villager.death\\\
10069entity.villager.hurt\\\
10070entity.villager.no\\\
10071entity.villager.trading\\\
10072entity.villager.yes\\\
10073entity.witch.ambient\\\
10074entity.witch.death\\\
10075entity.witch.drink\\\
10076entity.witch.hurt\\\
10077entity.witch.throw\\\
10078entity.wither.ambient\\\
10079entity.wither.break_block\\\
10080entity.wither.death\\\
10081entity.wither.hurt\\\
10082entity.wither.shoot\\\
10083entity.wither.spawn\\\
10084entity.wither_skeleton.ambient\\\
10085entity.wither_skeleton.death\\\
10086entity.wither_skeleton.hurt\\\
10087entity.wither_skeleton.step\\\
10088entity.wolf.ambient\\\
10089entity.wolf.death\\\
10090entity.wolf.growl\\\
10091entity.wolf.hurt\\\
10092entity.wolf.pant\\\
10093entity.wolf.shake\\\
10094entity.wolf.step\\\
10095entity.wolf.whine\\\
10096entity.zombie.ambient\\\
10097entity.zombie.attack_door_wood\\\
10098entity.zombie.attack_iron_door\\\
10099entity.zombie.break_door_wood\\\
10100entity.zombie.cure\\\
10101entity.zombie.death\\\
10102entity.zombie.hurt\\\
10103entity.zombie.step\\\
10104entity.zombie_horse.ambient\\\
10105entity.zombie_horse.death\\\
10106entity.zombie_horse.hurt\\\
10107entity.zombie.infect\\\
10108entity.zombie_pig.ambient\\\
10109entity.zombie_pig.angry\\\
10110entity.zombie_pig.death\\\
10111entity.zombie_pig.hurt\\\
10112enchant.thorns.hit\\\
10113music.creative\\\
10114music.credits\\\
10115music.dragon\\\
10116music.end\\\
10117music.game\\\
10118music.menu\\\
10119music.nether\\\
10120record.11\\\
10121record.13\\\
10122record.blocks\\\
10123record.cat\\\
10124record.chirp\\\
10125record.far\\\
10126record.mall\\\
10127record.mellohi\\\
10128record.stal\\\
10129record.strad\\\
10130record.wait\\\
10131record.ward\\\
10132item.armor.equip_chain\\\
10133item.armor.equip_diamond\\\
10134item.armor.equip_generic\\\
10135item.armor.equip_gold\\\
10136item.armor.equip_iron\\\
10137item.armor.equip_leather\\\
10138item.bottle.fill\\\
10139item.bottle.fill_dragonbreath\\\
10140item.bucket.empty\\\
10141item.bucket.empty_lava\\\
10142item.bucket.fill\\\
10143item.bucket.fill_lava\\\
10144item.chorus_fruit.teleport\\\
10145item.elytra.flying\\\
10146item.firecharge.use\\\
10147item.flintandsteel.use\\\
10148item.hoe.till\\\
10149item.shovel.flatten\\\
10150item.totem.use\\\
10151weather.rain\\\
10152weather.rain.above\\\
10153ambient.cave\\\
10154ui.button.click\",\
10155 [ \"common/edit.lua\" ] = \"local Array = require('opus.array')\\\
10156local Config = require('opus.config')\\\
10157local fuzzy = require('opus.fuzzy')\\\
10158local UI = require('opus.ui')\\\
10159local Util = require('opus.util')\\\
10160\\\
10161local device = _G.device\\\
10162local fs = _G.fs\\\
10163local keys = _G.keys\\\
10164local multishell = _ENV.multishell\\\
10165local os = _G.os\\\
10166local shell = _ENV.shell\\\
10167local term = _G.term.current()\\\
10168local textutils = _G.textutils\\\
10169\\\
10170local _format = string.format\\\
10171local _rep = string.rep\\\
10172local _sub = string.sub\\\
10173local _concat = table.concat\\\
10174local _insert = table.insert\\\
10175local _remove = table.remove\\\
10176local _unpack = table.unpack\\\
10177\\\
10178local config = Config.load('editor')\\\
10179\\\
10180local x, y = 1, 1\\\
10181local w, h = term.getSize()\\\
10182local scrollX = 0\\\
10183local scrollY = 0\\\
10184local lastPos = { x = 1, y = 1 }\\\
10185local tLines = { }\\\
10186local fileInfo\\\
10187local actions\\\
10188local lastSave\\\
10189local dirty = { y = 1, ey = h }\\\
10190local mark = { }\\\
10191local searchPattern\\\
10192local undo = { chain = { }, redo = { } }\\\
10193\\\
10194h = h - 1\\\
10195\\\
10196local bgColor = 'gray'\\\
10197local color = {\\\
10198 text = '0',\\\
10199 keyword = '2',\\\
10200 comment = 'd',\\\
10201 string = '1',\\\
10202 mark = '8',\\\
10203 bg = '7',\\\
10204}\\\
10205\\\
10206if not term.isColor() then\\\
10207 bgColor = 'black'\\\
10208 color = {\\\
10209 text = '0',\\\
10210 keyword = '8',\\\
10211 comment = '8',\\\
10212 string = '8',\\\
10213 mark = '7',\\\
10214 bg = 'f',\\\
10215 }\\\
10216end\\\
10217\\\
10218local keyMapping = {\\\
10219 -- movement\\\
10220 up = 'up',\\\
10221 down = 'down',\\\
10222 left = 'left',\\\
10223 right = 'right',\\\
10224 pageUp = 'page_up',\\\
10225 [ 'control-b' ] = 'page_up',\\\
10226 pageDown = 'page_down',\\\
10227 home = 'home',\\\
10228 [ 'end' ] = 'toend',\\\
10229 [ 'control-home' ] = 'top',\\\
10230 [ 'control-end' ] = 'bottom',\\\
10231 [ 'control-right' ] = 'word',\\\
10232 [ 'control-left' ] = 'backword',\\\
10233 [ 'scroll_up' ] = 'scroll_up',\\\
10234 [ 'control-up' ] = 'scroll_up',\\\
10235 [ 'scroll_down' ] = 'scroll_down',\\\
10236 [ 'control-down' ] = 'scroll_down',\\\
10237 [ 'mouse_click' ] = 'go_to',\\\
10238 [ 'control-g' ] = 'goto_line',\\\
10239\\\
10240 -- marking\\\
10241 [ 'shift-up' ] = 'mark_up',\\\
10242 [ 'shift-down' ] = 'mark_down',\\\
10243 [ 'shift-left' ] = 'mark_left',\\\
10244 [ 'shift-right' ] = 'mark_right',\\\
10245 [ 'mouse_drag' ] = 'mark_to',\\\
10246 [ 'shift-mouse_click' ] = 'mark_to',\\\
10247 [ 'control-a' ] = 'mark_all',\\\
10248 [ 'control-shift-right' ] = 'mark_word',\\\
10249 [ 'control-shift-left' ] = 'mark_backword',\\\
10250 [ 'shift-end' ] = 'mark_end',\\\
10251 [ 'shift-home' ] = 'mark_home',\\\
10252 [ 'mouse_down' ] = 'mark_anchor',\\\
10253 [ 'mouse_doubleclick' ] = 'mark_current_word',\\\
10254 [ 'mouse_tripleclick' ] = 'mark_line',\\\
10255\\\
10256 -- editing\\\
10257 delete = 'delete',\\\
10258 backspace = 'backspace',\\\
10259 enter = 'enter',\\\
10260 char = 'char',\\\
10261 paste = 'paste',\\\
10262 tab = 'tab',\\\
10263 [ 'control-z' ] = 'undo',\\\
10264 [ 'control-Z' ] = 'redo',\\\
10265 [ 'control-space' ] = 'autocomplete',\\\
10266 [ 'control-shift-space' ] = 'peripheral',\\\
10267\\\
10268 -- copy/paste\\\
10269 [ 'control-x' ] = 'cut',\\\
10270 [ 'control-c' ] = 'copy',\\\
10271 [ 'control-y' ] = 'paste_internal',\\\
10272\\\
10273 -- file\\\
10274 [ 'control-s' ] = 'save',\\\
10275 [ 'control-S' ] = 'save_as',\\\
10276 [ 'control-q' ] = 'exit',\\\
10277 [ 'control-enter' ] = 'run',\\\
10278 [ 'control-p' ] = 'quick_open',\\\
10279\\\
10280 -- search\\\
10281 [ 'control-f' ] = 'find_prompt',\\\
10282 [ 'control-slash' ] = 'find_prompt',\\\
10283 [ 'control-n' ] = 'find_next',\\\
10284\\\
10285 -- misc\\\
10286 [ 'control-i' ] = 'status',\\\
10287 [ 'control-r' ] = 'refresh',\\\
10288}\\\
10289\\\
10290local page = UI.Page {\\\
10291 menuBar = UI.MenuBar {\\\
10292 transitionHint = 'slideLeft',\\\
10293 buttons = {\\\
10294 { text = 'File', dropdown = {\\\
10295 { text = 'New ', event = 'menu_action', action = 'file_new' },\\\
10296 { text = 'Open... ', event = 'menu_action', action = 'file_open' },\\\
10297 { text = 'Quick Open... ^p', event = 'menu_action', action = 'quick_open' },\\\
10298 { text = 'Recent... ', event = 'menu_action', action = 'recent' },\\\
10299 { spacer = true },\\\
10300 { text = 'Save ^s', event = 'menu_action', action = 'save' },\\\
10301 { text = 'Save As... ^S', event = 'menu_action', action = 'save_as' },\\\
10302 { spacer = true },\\\
10303 { text = 'Quit ^q', event = 'menu_action', action = 'exit' },\\\
10304 } },\\\
10305 { text = 'Edit', dropdown = {\\\
10306 { text = 'Cut ^x', event = 'menu_action', action = 'cut' },\\\
10307 { text = 'Copy ^c', event = 'menu_action', action = 'copy' },\\\
10308 { text = 'Paste ^y,^V', event = 'menu_action', action = 'paste_internal' },\\\
10309 { spacer = true },\\\
10310 { text = 'Find... ^f', event = 'menu_action', action = 'find_prompt' },\\\
10311 { text = 'Find Next ^n', event = 'menu_action', action = 'find_next' },\\\
10312 { spacer = true },\\\
10313 { text = 'Go to line... ^g', event = 'menu_action', action = 'goto_line' },\\\
10314 { text = 'Mark all ^a', event = 'menu_action', action = 'mark_all' },\\\
10315 } },\\\
10316 { text = 'Code', dropdown = {\\\
10317 { text = 'Complete ^space', event = 'menu_action', action = 'autocomplete' },\\\
10318 { text = 'Run ^enter', event = 'menu_action', action = 'run' },\\\
10319 { spacer = true },\\\
10320 { text = 'Peripheral ^SPACE', event = 'menu_action', action = 'peripheral' },\\\
10321 } },\\\
10322 },\\\
10323 status = UI.Text {\\\
10324 textColor = 'gray',\\\
10325 x = -9, width = 9,\\\
10326 align = 'right',\\\
10327 },\\\
10328 },\\\
10329 gotoLine = UI.MiniSlideOut {\\\
10330 x = -15, y = -2,\\\
10331 label = 'Line',\\\
10332 lineNo = UI.TextEntry {\\\
10333 x = 7, width = 7,\\\
10334 limit = 5,\\\
10335 transform = 'number',\\\
10336 accelerators = {\\\
10337 [ 'enter' ] = 'accept',\\\
10338 },\\\
10339 },\\\
10340 show = function(self)\\\
10341 self.lineNo:reset()\\\
10342 UI.MiniSlideOut.show(self)\\\
10343 end,\\\
10344 eventHandler = function(self, event)\\\
10345 if event.type == 'accept' then\\\
10346 if self.lineNo.value then\\\
10347 actions.process('go_to', 1, self.lineNo.value)\\\
10348 end\\\
10349 self:hide()\\\
10350 return true\\\
10351 end\\\
10352 return UI.MiniSlideOut.eventHandler(self, event)\\\
10353 end,\\\
10354 },\\\
10355 search = UI.MiniSlideOut {\\\
10356 x = '50%', y = -2,\\\
10357 label = 'Find',\\\
10358 search = UI.TextEntry {\\\
10359 x = 7, ex = -3,\\\
10360 accelerators = {\\\
10361 [ 'enter' ] = 'accept',\\\
10362 },\\\
10363 },\\\
10364 show = function(self)\\\
10365 self.search:markAll()\\\
10366 UI.MiniSlideOut.show(self)\\\
10367 end,\\\
10368 eventHandler = function(self, event)\\\
10369 if event.type == 'accept' then\\\
10370 local text = self.search.value\\\
10371 if text and #text > 0 then\\\
10372 searchPattern = text:lower()\\\
10373 if searchPattern then\\\
10374 actions.unmark()\\\
10375 actions.process('find', searchPattern, x)\\\
10376 end\\\
10377 end\\\
10378 self:hide()\\\
10379 return true\\\
10380 end\\\
10381 return UI.MiniSlideOut.eventHandler(self, event)\\\
10382 end,\\\
10383 },\\\
10384 save_as = UI.MiniSlideOut {\\\
10385 x = '30%', y = -2,\\\
10386 label = 'Save',\\\
10387 filename = UI.TextEntry {\\\
10388 x = 7, ex = -3,\\\
10389 accelerators = {\\\
10390 [ 'enter' ] = 'accept',\\\
10391 },\\\
10392 },\\\
10393 show = function(self)\\\
10394 self.filename:setValue(fileInfo.path)\\\
10395 self.filename:setPosition(#self.filename.value)\\\
10396 UI.MiniSlideOut.show(self)\\\
10397 end,\\\
10398 eventHandler = function(self, event)\\\
10399 if event.type == 'accept' then\\\
10400 local text = self.filename.value\\\
10401 if text and #text > 0 then\\\
10402 actions.save('/' .. text)\\\
10403 end\\\
10404 self:hide()\\\
10405 return true\\\
10406 end\\\
10407 return UI.MiniSlideOut.eventHandler(self, event)\\\
10408 end,\\\
10409 },\\\
10410 unsaved = UI.Question {\\\
10411 x = -25, y = -2,\\\
10412 label = 'Save',\\\
10413 cancel = UI.Button {\\\
10414 x = 16,\\\
10415 text = 'Cancel',\\\
10416 backgroundColor = 'primary',\\\
10417 event = 'question_cancel',\\\
10418 },\\\
10419 show = function(self, action)\\\
10420 self.action = action\\\
10421 UI.MiniSlideOut.show(self)\\\
10422 end,\\\
10423 eventHandler = function(self, event)\\\
10424 if event.type == 'question_yes' then\\\
10425 if actions.save() then\\\
10426 self:hide()\\\
10427 actions.process(self.action)\\\
10428 end\\\
10429 elseif event.type == 'question_no' then\\\
10430 actions.process(self.action, true)\\\
10431 self:hide()\\\
10432 elseif event.type == 'question_cancel' then\\\
10433 self:hide()\\\
10434 end\\\
10435 return UI.MiniSlideOut.eventHandler(self, event)\\\
10436 end,\\\
10437 },\\\
10438 file_open = UI.FileSelect {\\\
10439 modal = true,\\\
10440 enable = function() end,\\\
10441 show = function(self)\\\
10442 UI.FileSelect.enable(self, fs.getDir(fileInfo.path))\\\
10443 self:focusFirst()\\\
10444 self:draw()\\\
10445 self:addTransition('expandUp', { easing = 'outBounce', ticks = 12 })\\\
10446 end,\\\
10447 eventHandler = function(self, event)\\\
10448 if event.type == 'select_cancel' then\\\
10449 self:disable()\\\
10450 elseif event.type == 'select_file' then\\\
10451 self:disable()\\\
10452 actions.process('open', event.file)\\\
10453 end\\\
10454 return UI.FileSelect.eventHandler(self, event)\\\
10455 end,\\\
10456 },\\\
10457 recent = UI.SlideOut {\\\
10458 grid = UI.Grid {\\\
10459 x = 2, y = 2, ey = -4, ex = -2,\\\
10460 columns = {\\\
10461 { key = 'name', heading = 'Name' },\\\
10462 { key = 'dir', heading = 'Directory', textColor = 'lightGray' },\\\
10463 },\\\
10464 accelerators = {\\\
10465 backspace = 'slide_hide',\\\
10466 },\\\
10467 },\\\
10468 cancel = UI.Button {\\\
10469 x = -9, y = -2,\\\
10470 text = 'Cancel',\\\
10471 event = 'slide_hide',\\\
10472 },\\\
10473 show = function(self)\\\
10474 local t = { }\\\
10475 for _,v in pairs(config.recent or { }) do\\\
10476 _insert(t, { name = fs.getName(v), dir = fs.getDir(v), path = v })\\\
10477 end\\\
10478 self.grid:setValues(t)\\\
10479 self.grid:setIndex(1)\\\
10480 UI.SlideOut.show(self)\\\
10481 self:addTransition('expandUp', { easing = 'outBounce', ticks = 12 })\\\
10482 end,\\\
10483 eventHandler = function(self, event)\\\
10484 if event.type == 'grid_select' then\\\
10485 actions.process('open', event.selected.path)\\\
10486 self:hide()\\\
10487 return true\\\
10488 end\\\
10489 return UI.SlideOut.eventHandler(self, event)\\\
10490 end,\\\
10491 },\\\
10492 quick_open = UI.QuickSelect {\\\
10493 modal = true,\\\
10494 enable = function() end,\\\
10495 show = function(self)\\\
10496 UI.QuickSelect.enable(self)\\\
10497 self:focusFirst()\\\
10498 self:draw()\\\
10499 self:addTransition('expandUp', { easing = 'outBounce', ticks = 12 })\\\
10500 end,\\\
10501 eventHandler = function(self, event)\\\
10502 if event.type == 'select_cancel' then\\\
10503 self:disable()\\\
10504 elseif event.type == 'select_file' then\\\
10505 self:disable()\\\
10506 actions.process('open', event.file)\\\
10507 end\\\
10508 return UI.QuickSelect.eventHandler(self, event)\\\
10509 end,\\\
10510 },\\\
10511 completions = UI.SlideOut {\\\
10512 x = -12, y = 2,\\\
10513 transitionHint = 'slideLeft',\\\
10514 grid = UI.Grid {\\\
10515 x = 2, y = 2, ey = -2,\\\
10516 columns = {\\\
10517 { key = 'text', heading = 'Completion' },\\\
10518 },\\\
10519 accelerators = {\\\
10520 [ ' ' ] = 'down',\\\
10521 backspace = 'slide_hide',\\\
10522 },\\\
10523 },\\\
10524 cancel = UI.Button {\\\
10525 y = -1, x = -9,\\\
10526 text = 'Cancel',\\\
10527 backgroundColor = 'black',\\\
10528 backgroundFocusColor = 'black',\\\
10529 textColor = 'lightGray',\\\
10530 event = 'slide_hide',\\\
10531 },\\\
10532 show = function(self, values)\\\
10533 local m = Util.reduce(values, function(m, v)\\\
10534 return #v.text > m and #v.text or m\\\
10535 end, 12)\\\
10536 m = m + 3\\\
10537 m = m > self.parent.width and self.parent.width or m\\\
10538 self.ox = -m\\\
10539 self:resize()\\\
10540 self.grid:setValues(values)\\\
10541 self.grid:setIndex(1)\\\
10542 UI.SlideOut.show(self)\\\
10543 end,\\\
10544 eventHandler = function(self, event)\\\
10545 if event.type == 'grid_select' then\\\
10546 actions.process('insertText', x, y, event.selected.complete)\\\
10547 self:hide()\\\
10548 return true\\\
10549 end\\\
10550 return UI.SlideOut.eventHandler(self, event)\\\
10551 end,\\\
10552 },\\\
10553 peripheral = UI.SlideOut {\\\
10554 x = '20%', y = 2,\\\
10555 transitionHint = 'slideLeft',\\\
10556 grid1 = UI.Grid {\\\
10557 x = 2, y = 2, ey = 5,\\\
10558 sortColumn = 'name',\\\
10559 columns = {\\\
10560 { key = 'name', heading = 'Peripheral' },\\\
10561 },\\\
10562 accelerators = {\\\
10563 [ ' ' ] = 'down',\\\
10564 backspace = 'slide_hide',\\\
10565 grid_focus_row = 'select_peripheral',\\\
10566 grid_select = 'complete',\\\
10567 },\\\
10568 scan = function(self)\\\
10569 self.values = { }\\\
10570 for k, v in pairs(device) do\\\
10571 if type(v.side) == 'string' then\\\
10572 _insert(self.values, { name = k, complete = 'peripheral.wrap(\\\"' .. v.side .. '\\\")' })\\\
10573 end\\\
10574 end\\\
10575 end,\\\
10576 postInit = function(self)\\\
10577 self:scan()\\\
10578 end,\\\
10579 },\\\
10580 grid2 = UI.Grid {\\\
10581 x = 2, y = 6, ey = -2,\\\
10582 sortColumn = 'method',\\\
10583 columns = {\\\
10584 { key = 'method', heading = 'Method' },\\\
10585 },\\\
10586 accelerators = {\\\
10587 [ ' ' ] = 'down',\\\
10588 backspace = 'slide_hide',\\\
10589 grid_select = 'complete',\\\
10590 },\\\
10591 showMethods = function(self)\\\
10592 local dev = device[self.parent.grid1:getSelected().name]\\\
10593 local t = { }\\\
10594 if dev then\\\
10595 pcall(function()\\\
10596 local docs = dev.getDocs and dev.getDocs()\\\
10597 for k, v in pairs(dev) do\\\
10598 if type(v) == 'function' then\\\
10599 local m = docs and docs[k] and docs[k]:match('^function%((.+)%).+')\\\
10600 _insert(t, { method = k, complete = k .. '(' .. (m or '') .. ')' })\\\
10601 end\\\
10602 end\\\
10603 end)\\\
10604 end\\\
10605 self:setValues(t)\\\
10606 end,\\\
10607 enable = function(self)\\\
10608 self:showMethods()\\\
10609 UI.Grid.enable(self)\\\
10610 end,\\\
10611 },\\\
10612 cancel = UI.Button {\\\
10613 y = -1, x = -9,\\\
10614 text = 'Cancel',\\\
10615 backgroundColor = 'black',\\\
10616 backgroundFocusColor = 'black',\\\
10617 textColor = 'lightGray',\\\
10618 event = 'slide_hide',\\\
10619 },\\\
10620 eventHandler = function(self, event)\\\
10621 if event.type == 'complete' then\\\
10622 actions.process('insertText', x, y, event.selected.complete)\\\
10623 actions.process('left')\\\
10624 self:hide()\\\
10625 return true\\\
10626 elseif event.type == 'select_peripheral' then\\\
10627 self.grid2:showMethods()\\\
10628 self.grid2:setIndex(1)\\\
10629 self.grid2:update()\\\
10630 self.grid2:draw()\\\
10631 end\\\
10632 return UI.SlideOut.eventHandler(self, event)\\\
10633 end,\\\
10634 },\\\
10635 editor = UI.Window {\\\
10636 y = 2,\\\
10637 backgroundColor = bgColor,\\\
10638 transitionHint = 'slideRight',\\\
10639 cursorBlink = true,\\\
10640 focus = function(self)\\\
10641 if self.focused then\\\
10642 self:setCursorPos(x - scrollX, y - scrollY)\\\
10643 end\\\
10644 end,\\\
10645 resize = function(self)\\\
10646 UI.Window.resize(self)\\\
10647\\\
10648 w, h = self.width, self.height\\\
10649 actions.set_cursor(x, y)\\\
10650 actions.dirty_all()\\\
10651 actions.redraw()\\\
10652 end,\\\
10653 setCursorPos = function(self, cx, cy)\\\
10654 self.cursorBlink = cy >= 1 and cy <= self.height\\\
10655 UI.Window.setCursorPos(self, cx, cy)\\\
10656 end,\\\
10657 draw = function()\\\
10658 actions.redraw()\\\
10659 end,\\\
10660 eventHandler = function(_, event)\\\
10661 if event.ie then\\\
10662 local action, param, param2\\\
10663 local ie = event.ie\\\
10664\\\
10665 if ie.code == 'char' then\\\
10666 action = keyMapping.char\\\
10667 param = ie.ch\\\
10668\\\
10669 elseif ie.code == \\\"mouse_click\\\" or\\\
10670 ie.code == 'mouse_drag' or\\\
10671 ie.code == 'shift-mouse_click' or\\\
10672 ie.code == 'mouse_down' or\\\
10673 ie.code == 'mouse_doubleclick' then\\\
10674\\\
10675 action = keyMapping[ie.code]\\\
10676 param = ie.x + scrollX\\\
10677 param2 = ie.y + scrollY\\\
10678\\\
10679 elseif event.type == 'paste' then\\\
10680 action = keyMapping.paste\\\
10681 param = event.text\\\
10682\\\
10683 else\\\
10684 action = keyMapping[ie.code]\\\
10685 end\\\
10686\\\
10687 if action then\\\
10688 actions.process(action, param, param2)\\\
10689 return true\\\
10690 end\\\
10691 end\\\
10692 end,\\\
10693 },\\\
10694 notification = UI.Notification { },\\\
10695 enable = function(self)\\\
10696 UI.Page.enable(self)\\\
10697 self:setFocus(self.editor)\\\
10698 end,\\\
10699 checkFocus = function(self)\\\
10700 if not self.focused or not self.focused.enabled then\\\
10701 -- if no current focus, set it to the editor\\\
10702 self:setFocus(self.editor)\\\
10703 end\\\
10704 end,\\\
10705 eventHandler = function(self, event)\\\
10706 if event.type == 'menu_action' then\\\
10707 actions.process(event.element.action)\\\
10708 return true\\\
10709 end\\\
10710 return UI.Page.eventHandler(self, event)\\\
10711 end,\\\
10712}\\\
10713\\\
10714local function getFileInfo(path)\\\
10715 path = fs.combine('/', path)\\\
10716\\\
10717 local fi = {\\\
10718 path = path,\\\
10719 isNew = not fs.exists(path),\\\
10720 dirExists = fs.exists(fs.getDir(path)),\\\
10721 isReadOnly = fs.isReadOnly(path),\\\
10722 }\\\
10723\\\
10724 if path ~= config.filename then\\\
10725 config.filename = path\\\
10726 config.recent = config.recent or { }\\\
10727\\\
10728 Array.removeByValue(config.recent, path)\\\
10729 _insert(config.recent, 1, path)\\\
10730 while #config.recent > 10 do\\\
10731 _remove(config.recent)\\\
10732 end\\\
10733\\\
10734 Config.update('editor', config)\\\
10735 end\\\
10736\\\
10737 if multishell then\\\
10738 multishell.setTitle(multishell.getCurrent(), fs.getName(fi.path))\\\
10739 end\\\
10740\\\
10741 return fi\\\
10742end\\\
10743\\\
10744local keywords = Util.transpose {\\\
10745 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',\\\
10746 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while'\\\
10747}\\\
10748\\\
10749local function writeHighlighted(sLine, ny, dy)\\\
10750 local buffer = { fg = { }, text = { } }\\\
10751\\\
10752 local function tryWrite(line, regex, fgcolor)\\\
10753 local match = line:match(regex)\\\
10754 if match then\\\
10755 local fg = type(fgcolor) == \\\"string\\\" and fgcolor or fgcolor(match)\\\
10756 _insert(buffer.text, match)\\\
10757 _insert(buffer.fg, _rep(fg, #match))\\\
10758 return _sub(line, #match + 1)\\\
10759 end\\\
10760 return nil\\\
10761 end\\\
10762\\\
10763 while #sLine > 0 do\\\
10764 sLine =\\\
10765 -- tryWrite(sLine, \\\"^[%\\\\26]\\\", '7' ) or\\\
10766 tryWrite(sLine, \\\"^%-%-%[%[.-%]%]\\\", color.comment ) or\\\
10767 tryWrite(sLine, \\\"^%-%-.*\\\", color.comment ) or\\\
10768 tryWrite(sLine, \\\"^\\\\\\\".-[^\\\\\\\\]\\\\\\\"\\\", color.string ) or\\\
10769 tryWrite(sLine, \\\"^\\\\'.-[^\\\\\\\\]\\\\'\\\", color.string ) or\\\
10770 tryWrite(sLine, \\\"^%[%[.-%]%]\\\", color.string ) or\\\
10771 tryWrite(sLine, \\\"^[%w_]+\\\", function(match)\\\
10772 return keywords[match] and color.keyword or color.text\\\
10773 end) or\\\
10774 tryWrite(sLine, \\\"^[^%w_]\\\", color.text)\\\
10775 end\\\
10776\\\
10777 buffer.fg = _concat(buffer.fg) .. '7'\\\
10778 buffer.text = _concat(buffer.text) .. '\\\\183'\\\
10779\\\
10780 if mark.active and ny >= mark.y and ny <= mark.ey then\\\
10781 local sx = ny == mark.y and mark.x or 1\\\
10782 local ex = ny == mark.ey and mark.ex or #buffer.text\\\
10783 buffer.bg = _rep(color.bg, sx - 1) ..\\\
10784 _rep(color.mark, ex - sx) ..\\\
10785 _rep(color.bg, #buffer.text - ex + 1)\\\
10786 else\\\
10787 buffer.bg = _rep(color.bg, #buffer.text)\\\
10788 end\\\
10789\\\
10790 page.editor:blit(1 - scrollX, dy, buffer.text, buffer.bg, buffer.fg)\\\
10791end\\\
10792\\\
10793local function redraw()\\\
10794 if dirty.y > 0 then\\\
10795 for dy = 1, h do\\\
10796 local sLine = tLines[dy + scrollY]\\\
10797 if sLine and #sLine > 0 then\\\
10798 if dy + scrollY >= dirty.y and dy + scrollY <= dirty.ey then\\\
10799 page.editor:clearLine(dy)\\\
10800 writeHighlighted(sLine, dy + scrollY, dy)\\\
10801 end\\\
10802 else\\\
10803 page.editor:clearLine(dy)\\\
10804 end\\\
10805 end\\\
10806 end\\\
10807\\\
10808 local modifiedIndicator = undo.chain[#undo.chain] == lastSave and ' ' or '*'\\\
10809 page.menuBar.status.value = _format('%d:%d%s', y, x, modifiedIndicator)\\\
10810 page.menuBar.status:draw()\\\
10811\\\
10812 if page.editor.focused then\\\
10813 page.editor:setCursorPos(x - scrollX, y - scrollY)\\\
10814 end\\\
10815\\\
10816 dirty.y, dirty.ey = 0, 0\\\
10817end\\\
10818\\\
10819local function nextWord(line, cx)\\\
10820 local result = { line:find(\\\"(%w+)\\\", cx) }\\\
10821 if #result > 1 and result[2] > cx then\\\
10822 return result[2] + 1\\\
10823 elseif #result > 0 and result[1] == cx then\\\
10824 result = { line:find(\\\"(%w+)\\\", result[2] + 1) }\\\
10825 if #result > 0 then\\\
10826 return result[1]\\\
10827 end\\\
10828 end\\\
10829end\\\
10830\\\
10831actions = {\\\
10832 info = function(pattern, ...)\\\
10833 page.notification:info(_format(pattern, ...))\\\
10834 end,\\\
10835\\\
10836 error = function(pattern, ...)\\\
10837 page.notification:error(_format(pattern, ...))\\\
10838 end,\\\
10839\\\
10840 undo = function()\\\
10841 local last = _remove(undo.chain)\\\
10842 if last then\\\
10843 undo.active = true\\\
10844 _insert(undo.redo, { })\\\
10845 for i = #last, 1, -1 do\\\
10846 local u = last[i]\\\
10847 actions[u.action](_unpack(u.args))\\\
10848 end\\\
10849 undo.active = false\\\
10850 else\\\
10851 actions.info('already at oldest change')\\\
10852 end\\\
10853 end,\\\
10854\\\
10855 undo_add = function(entry)\\\
10856 if undo.active then\\\
10857 local last = undo.redo[#undo.redo]\\\
10858 _insert(last, entry)\\\
10859 else\\\
10860 if not undo.redo_active then\\\
10861 undo.redo = { }\\\
10862 end\\\
10863 local last = undo.chain[#undo.chain]\\\
10864 if last and undo.continue then\\\
10865 _insert(last, entry)\\\
10866 else\\\
10867 _insert(undo.chain, { entry })\\\
10868 end\\\
10869 end\\\
10870 end,\\\
10871\\\
10872 redo = function()\\\
10873 local last = _remove(undo.redo)\\\
10874 if last then\\\
10875 -- too many flags !\\\
10876 undo.redo_active = true\\\
10877 undo.continue = false\\\
10878 for i = #last, 1, -1 do\\\
10879 local u = last[i]\\\
10880 actions[u.action](_unpack(u.args))\\\
10881 undo.continue = true\\\
10882 end\\\
10883 undo.redo_active = false\\\
10884 else\\\
10885 actions.info('already at newest change')\\\
10886 end\\\
10887 end,\\\
10888\\\
10889 autocomplete = function()\\\
10890 local sLine = tLines[y]:sub(1, x - 1):match(\\\"[a-zA-Z0-9_%.]+$\\\")\\\
10891 local results = sLine and textutils.complete(sLine, _ENV) or { }\\\
10892\\\
10893 if #results == 0 then\\\
10894 actions.error('no completions available')\\\
10895\\\
10896 elseif #results == 1 then\\\
10897 actions.insertText(x, y, results[1])\\\
10898\\\
10899 elseif #results > 1 then\\\
10900 local prefix = sLine:match('^.+%.(.*)$') or sLine\\\
10901 for i = 1, #results do\\\
10902 results[i] = {\\\
10903 text = prefix .. results[i],\\\
10904 complete = results[i],\\\
10905 }\\\
10906 end\\\
10907 page.completions:show(results)\\\
10908 end\\\
10909 end,\\\
10910\\\
10911 peripheral = function()\\\
10912 page.peripheral:show()\\\
10913 end,\\\
10914\\\
10915 refresh = function()\\\
10916 actions.dirty_all()\\\
10917 mark.continue = mark.active\\\
10918 actions.info('refreshed')\\\
10919 end,\\\
10920\\\
10921 goto_line = function()\\\
10922 page.gotoLine:show()\\\
10923 end,\\\
10924\\\
10925 find = function(pattern, sx)\\\
10926 local nLines = #tLines\\\
10927 for i = 1, nLines + 1 do\\\
10928 local ny = y + i - 1\\\
10929 if ny > nLines then\\\
10930 ny = ny - nLines\\\
10931 end\\\
10932 local nx = tLines[ny]:lower():find(pattern, sx, true)\\\
10933 if nx then\\\
10934 if ny < y or ny == y and nx <= x then\\\
10935 actions.info('search hit BOTTOM, continuing at TOP')\\\
10936 end\\\
10937 actions.go_to(nx, ny)\\\
10938 actions.mark_to(nx + #pattern, ny)\\\
10939 return\\\
10940 end\\\
10941 sx = 1\\\
10942 end\\\
10943 actions.error('pattern not found')\\\
10944 end,\\\
10945\\\
10946 find_next = function()\\\
10947 if searchPattern then\\\
10948 actions.unmark()\\\
10949 actions.find(searchPattern, x + 1)\\\
10950 end\\\
10951 end,\\\
10952\\\
10953 find_prompt = function()\\\
10954 page.search:show()\\\
10955 end,\\\
10956\\\
10957 quick_open = function(force)\\\
10958 if not force and undo.chain[#undo.chain] ~= lastSave then\\\
10959 page.unsaved:show('quick_open')\\\
10960 else\\\
10961 page.quick_open:show()\\\
10962 end\\\
10963 end,\\\
10964\\\
10965 file_open = function(force)\\\
10966 if not force and undo.chain[#undo.chain] ~= lastSave then\\\
10967 page.unsaved:show('file_open')\\\
10968 else\\\
10969 page.file_open:show()\\\
10970 end\\\
10971 end,\\\
10972\\\
10973 recent = function(force)\\\
10974 if not force and undo.chain[#undo.chain] ~= lastSave then\\\
10975 page.unsaved:show('recent')\\\
10976 else\\\
10977 page.recent:show()\\\
10978 end\\\
10979 end,\\\
10980\\\
10981 file_new = function(force)\\\
10982 if not force and undo.chain[#undo.chain] ~= lastSave then\\\
10983 page.unsaved:show('file_new')\\\
10984 else\\\
10985 actions.open('/untitled.lua')\\\
10986 end\\\
10987 end,\\\
10988\\\
10989 open = function(filename)\\\
10990 if not actions.load(filename) then\\\
10991 actions.error('unable to load file')\\\
10992 end\\\
10993 end,\\\
10994\\\
10995 load = function(path)\\\
10996 if not path or (fs.exists(path) and fs.isDir(path)) then\\\
10997 return false\\\
10998 end\\\
10999 fileInfo = getFileInfo(path)\\\
11000\\\
11001 x, y = 1, 1\\\
11002 scrollX, scrollY = 0, 0\\\
11003 lastPos = { x = 1, y = 1 }\\\
11004 lastSave = nil\\\
11005 dirty = { y = 1, ey = h }\\\
11006 mark = { }\\\
11007 undo = { chain = { }, redo = { } }\\\
11008\\\
11009 tLines = Util.readLines(fileInfo.path) or { }\\\
11010 if #tLines == 0 then\\\
11011 _insert(tLines, '')\\\
11012 end\\\
11013\\\
11014 --[[\\\
11015 local function detabify(l)\\\
11016 return l:gsub('\\\\26\\\\26', '\\\\9'):gsub('\\\\26', '\\\\9')\\\
11017 end ]]\\\
11018\\\
11019 -- since we can't handle tabs, convert them to spaces :(\\\
11020 local t1, t2 = ' ', ' '\\\
11021 local function tabify(l)\\\
11022 repeat\\\
11023 local i = l:find('\\\\9')\\\
11024 if i then\\\
11025 local tabs = (i - 1) % 2 == 0 and t2 or t1\\\
11026 l = l:sub(1, i - 1) .. tabs .. l:sub(i + 1)\\\
11027 end\\\
11028 until not i\\\
11029 return l\\\
11030 end\\\
11031\\\
11032 for k, v in pairs(tLines) do\\\
11033 tLines[k] = tabify(v)\\\
11034 end\\\
11035\\\
11036 local name = fileInfo.path\\\
11037 if fileInfo.isNew then\\\
11038 if not fileInfo.dirExists then\\\
11039 actions.info('\\\"%s\\\" [New DIRECTORY]', name)\\\
11040 else\\\
11041 actions.info('\\\"%s\\\" [New File]', name)\\\
11042 end\\\
11043 elseif fileInfo.isReadOnly then\\\
11044 actions.info('\\\"%s\\\" [readonly] %dL, %dC',\\\
11045 name, #tLines, fs.getSize(fileInfo.path))\\\
11046 else\\\
11047 actions.info('\\\"%s\\\" %dL, %dC',\\\
11048 name, #tLines, fs.getSize(fileInfo.path))\\\
11049 end\\\
11050\\\
11051 return true\\\
11052 end,\\\
11053\\\
11054 save = function(filename)\\\
11055 filename = filename or fileInfo.path\\\
11056 if fs.isReadOnly(filename) then\\\
11057 actions.error(\\\"access denied\\\")\\\
11058 else\\\
11059 local s, m = pcall(function()\\\
11060 if not Util.writeLines(filename, tLines) then\\\
11061 error(\\\"Failed to open \\\" .. filename)\\\
11062 end\\\
11063 end)\\\
11064\\\
11065 if s then\\\
11066 lastSave = undo.chain[#undo.chain]\\\
11067 fileInfo = getFileInfo(filename)\\\
11068 actions.info('\\\"%s\\\" %dL, %dC written',\\\
11069 fileInfo.path, #tLines, fs.getSize(fileInfo.path))\\\
11070 return true\\\
11071 else\\\
11072 actions.error(m)\\\
11073 end\\\
11074 end\\\
11075 end,\\\
11076\\\
11077 save_as = function()\\\
11078 page.save_as:show()\\\
11079 end,\\\
11080\\\
11081 exit = function(force)\\\
11082 if not force and undo.chain[#undo.chain] ~= lastSave then\\\
11083 page.unsaved:show('exit')\\\
11084 else\\\
11085 UI:quit()\\\
11086 end\\\
11087 end,\\\
11088\\\
11089 run = function()\\\
11090 if not multishell then\\\
11091 actions.error('open available with multishell')\\\
11092 return\\\
11093 end\\\
11094 local routine = {\\\
11095 focused = true,\\\
11096 title = fs.getName(fileInfo.path),\\\
11097 chainExit = function(_, result)\\\
11098 -- display results of process before closing window\\\
11099 if result then -- clean exit\\\
11100 -- any errors will be picked up by multishells\\\
11101 -- error handling\\\
11102 print('Press enter to exit')\\\
11103 while true do\\\
11104 local e, code = os.pullEventRaw('key')\\\
11105 if e == 'terminate' or e == 'key' and code == keys.enter then\\\
11106 break\\\
11107 end\\\
11108 end\\\
11109 end\\\
11110 end,\\\
11111 }\\\
11112 if undo.chain[#undo.chain] == lastSave then\\\
11113 routine.path = 'sys/apps/shell.lua'\\\
11114 routine.args = { fileInfo.path }\\\
11115 else\\\
11116 local fn, msg = load(_concat(tLines, '\\\\n'), fileInfo.path)\\\
11117 if not fn then\\\
11118 local ln = msg:match(':(%d+):')\\\
11119 if ln and tonumber(ln) then\\\
11120 actions.go_to(1, tonumber(ln))\\\
11121 end\\\
11122 actions.error(msg)\\\
11123 return\\\
11124 end\\\
11125 routine.fn = fn\\\
11126 end\\\
11127 multishell.openTab(_ENV, routine)\\\
11128 end,\\\
11129\\\
11130 status = function()\\\
11131 local modified = undo.chain[#undo.chain] == lastSave and '' or '[Modified] '\\\
11132 actions.info('\\\"%s\\\" %s%d lines --%d%%--',\\\
11133 fileInfo.path, modified, #tLines,\\\
11134 math.floor((y - 1) / (#tLines - 1) * 100))\\\
11135 end,\\\
11136\\\
11137 dirty_line = function(dy)\\\
11138 if dirty.y == 0 then\\\
11139 dirty.y = dy\\\
11140 dirty.ey = dy\\\
11141 else\\\
11142 dirty.y = math.min(dirty.y, dy)\\\
11143 dirty.ey = math.max(dirty.ey, dy)\\\
11144 end\\\
11145 end,\\\
11146\\\
11147 dirty_range = function(dy, dey)\\\
11148 actions.dirty_line(dy)\\\
11149 actions.dirty_line(dey or #tLines)\\\
11150 end,\\\
11151\\\
11152 dirty = function()\\\
11153 actions.dirty_line(y)\\\
11154 end,\\\
11155\\\
11156 dirty_all = function()\\\
11157 actions.dirty_line(1)\\\
11158 actions.dirty_line(#tLines)\\\
11159 end,\\\
11160\\\
11161 mark_begin = function()\\\
11162 actions.dirty()\\\
11163 if not mark.active then\\\
11164 mark.active = true\\\
11165 mark.anchor = { x = x, y = y }\\\
11166 end\\\
11167 end,\\\
11168\\\
11169 mark_finish = function()\\\
11170 if y == mark.anchor.y then\\\
11171 if x == mark.anchor.x then\\\
11172 mark.active = false\\\
11173 else\\\
11174 mark.x = math.min(mark.anchor.x, x)\\\
11175 mark.y = y\\\
11176 mark.ex = math.max(mark.anchor.x, x)\\\
11177 mark.ey = y\\\
11178 end\\\
11179 elseif y < mark.anchor.y then\\\
11180 mark.x = x\\\
11181 mark.y = y\\\
11182 mark.ex = mark.anchor.x\\\
11183 mark.ey = mark.anchor.y\\\
11184 else\\\
11185 mark.x = mark.anchor.x\\\
11186 mark.y = mark.anchor.y\\\
11187 mark.ex = x\\\
11188 mark.ey = y\\\
11189 end\\\
11190 actions.dirty()\\\
11191 mark.continue = mark.active\\\
11192 end,\\\
11193\\\
11194 unmark = function()\\\
11195 if mark.active then\\\
11196 actions.dirty_range(mark.y, mark.ey)\\\
11197 mark.active = false\\\
11198 end\\\
11199 end,\\\
11200\\\
11201 mark_anchor = function(nx, ny)\\\
11202 actions.go_to(nx, ny)\\\
11203 actions.unmark()\\\
11204 actions.mark_begin()\\\
11205 actions.mark_finish()\\\
11206 end,\\\
11207\\\
11208 mark_to = function(nx, ny)\\\
11209 actions.mark_begin()\\\
11210 actions.go_to(nx, ny)\\\
11211 actions.mark_finish()\\\
11212 end,\\\
11213\\\
11214 mark_up = function()\\\
11215 actions.mark_begin()\\\
11216 actions.up()\\\
11217 actions.mark_finish()\\\
11218 end,\\\
11219\\\
11220 mark_right = function()\\\
11221 actions.mark_begin()\\\
11222 actions.right()\\\
11223 actions.mark_finish()\\\
11224 end,\\\
11225\\\
11226 mark_down = function()\\\
11227 actions.mark_begin()\\\
11228 actions.down()\\\
11229 actions.mark_finish()\\\
11230 end,\\\
11231\\\
11232 mark_left = function()\\\
11233 actions.mark_begin()\\\
11234 actions.left()\\\
11235 actions.mark_finish()\\\
11236 end,\\\
11237\\\
11238 mark_line = function()\\\
11239 actions.home()\\\
11240 actions.mark_begin()\\\
11241 actions.toend()\\\
11242 actions.right()\\\
11243 actions.mark_finish()\\\
11244 end,\\\
11245\\\
11246 mark_word = function()\\\
11247 actions.mark_begin()\\\
11248 actions.word()\\\
11249 actions.mark_finish()\\\
11250 end,\\\
11251\\\
11252 mark_current_word = function(cx, cy)\\\
11253 local index = 1\\\
11254 actions.go_to(cx, cy)\\\
11255 while true do\\\
11256 local s, e = tLines[y]:find('%w+', index)\\\
11257 if not s or s - 1 > x then\\\
11258 break\\\
11259 end\\\
11260 if x >= s and x <= e then\\\
11261 x = s\\\
11262 actions.mark_begin()\\\
11263 x = e + 1\\\
11264 actions.mark_finish()\\\
11265 x, y = cx, cy\\\
11266 break\\\
11267 end\\\
11268 index = e + 1\\\
11269 end\\\
11270 end,\\\
11271\\\
11272 mark_backword = function()\\\
11273 actions.mark_begin()\\\
11274 actions.backword()\\\
11275 actions.mark_finish()\\\
11276 end,\\\
11277\\\
11278 mark_home = function()\\\
11279 actions.mark_begin()\\\
11280 actions.home()\\\
11281 actions.mark_finish()\\\
11282 end,\\\
11283\\\
11284 mark_end = function()\\\
11285 actions.mark_begin()\\\
11286 actions.toend()\\\
11287 actions.mark_finish()\\\
11288 end,\\\
11289\\\
11290 mark_all = function()\\\
11291 mark.anchor = { x = 1, y = 1 }\\\
11292 mark.active = true\\\
11293 mark.continue = true\\\
11294 mark.x = 1\\\
11295 mark.y = 1\\\
11296 mark.ey = #tLines\\\
11297 mark.ex = #tLines[mark.ey] + 1\\\
11298 actions.dirty_all()\\\
11299 end,\\\
11300\\\
11301 set_cursor = function()\\\
11302 lastPos.x = x\\\
11303 lastPos.y = y\\\
11304\\\
11305 local screenX = x - scrollX\\\
11306 local screenY = y - scrollY\\\
11307\\\
11308 if screenX < 1 then\\\
11309 scrollX = math.max(0, x - 4)\\\
11310 actions.dirty_all()\\\
11311 elseif screenX > w then\\\
11312 scrollX = x - w + 3\\\
11313 actions.dirty_all()\\\
11314 end\\\
11315\\\
11316 if screenY < 1 then\\\
11317 scrollY = y - 1\\\
11318 actions.dirty_all()\\\
11319 elseif screenY > h then\\\
11320 scrollY = y - h\\\
11321 actions.dirty_all()\\\
11322 end\\\
11323 end,\\\
11324\\\
11325 top = function()\\\
11326 actions.go_to(1, 1)\\\
11327 end,\\\
11328\\\
11329 bottom = function()\\\
11330 y = #tLines\\\
11331 x = #tLines[y] + 1\\\
11332 end,\\\
11333\\\
11334 up = function()\\\
11335 if y > 1 then\\\
11336 x = math.min(x, #tLines[y - 1] + 1)\\\
11337 y = y - 1\\\
11338 end\\\
11339 end,\\\
11340\\\
11341 down = function()\\\
11342 if y < #tLines then\\\
11343 x = math.min(x, #tLines[y + 1] + 1)\\\
11344 y = y + 1\\\
11345 end\\\
11346 end,\\\
11347\\\
11348 tab = function()\\\
11349 if mark.active then\\\
11350 actions.delete()\\\
11351 end\\\
11352 actions.insertText(x, y, ' ')\\\
11353 end,\\\
11354\\\
11355 page_up = function()\\\
11356 actions.go_to(x, y - h)\\\
11357 end,\\\
11358\\\
11359 page_down = function()\\\
11360 actions.go_to(x, y + h)\\\
11361 end,\\\
11362\\\
11363 home = function()\\\
11364 x = 1\\\
11365 end,\\\
11366\\\
11367 toend = function()\\\
11368 x = #tLines[y] + 1\\\
11369 end,\\\
11370\\\
11371 left = function()\\\
11372 if x > 1 then\\\
11373 x = x - 1\\\
11374 elseif y > 1 then\\\
11375 x = #tLines[y - 1] + 1\\\
11376 y = y - 1\\\
11377 else\\\
11378 return false\\\
11379 end\\\
11380 return true\\\
11381 end,\\\
11382\\\
11383 right = function()\\\
11384 if x < #tLines[y] + 1 then\\\
11385 x = x + 1\\\
11386 elseif y < #tLines then\\\
11387 x = 1\\\
11388 y = y + 1\\\
11389 end\\\
11390 end,\\\
11391\\\
11392 word = function()\\\
11393 local nx = nextWord(tLines[y], x)\\\
11394 if nx then\\\
11395 x = nx\\\
11396 elseif x < #tLines[y] + 1 then\\\
11397 x = #tLines[y] + 1\\\
11398 elseif y < #tLines then\\\
11399 x = 1\\\
11400 y = y + 1\\\
11401 end\\\
11402 end,\\\
11403\\\
11404 backword = function()\\\
11405 if x == 1 then\\\
11406 actions.left()\\\
11407 else\\\
11408 local sLine = tLines[y]\\\
11409 local lx = 1\\\
11410 while true do\\\
11411 local nx = nextWord(sLine, lx)\\\
11412 if not nx or nx >= x then\\\
11413 break\\\
11414 end\\\
11415 lx = nx\\\
11416 end\\\
11417 if not lx then\\\
11418 x = 1\\\
11419 else\\\
11420 x = lx\\\
11421 end\\\
11422 end\\\
11423 end,\\\
11424\\\
11425 insertText = function(sx, sy, text)\\\
11426 x = sx\\\
11427 y = sy\\\
11428 local sLine = tLines[y]\\\
11429\\\
11430 if not text:find('\\\\n') then\\\
11431 tLines[y] = sLine:sub(1, x - 1) .. text .. sLine:sub(x)\\\
11432 actions.dirty_line(y)\\\
11433 x = x + #text\\\
11434 else\\\
11435 local lines = Util.split(text)\\\
11436 local remainder = sLine:sub(x)\\\
11437 tLines[y] = sLine:sub(1, x - 1) .. lines[1]\\\
11438 actions.dirty_range(y, #tLines + #lines)\\\
11439 x = x + #lines[1]\\\
11440 for k = 2, #lines do\\\
11441 y = y + 1\\\
11442 _insert(tLines, y, lines[k])\\\
11443 x = #lines[k] + 1\\\
11444 end\\\
11445 tLines[y] = tLines[y]:sub(1, x) .. remainder\\\
11446 end\\\
11447\\\
11448 actions.undo_add(\\\
11449 { action = 'deleteText', args = { sx, sy, x, y } })\\\
11450 end,\\\
11451\\\
11452 deleteText = function(sx, sy, ex, ey)\\\
11453 x = sx\\\
11454 y = sy\\\
11455\\\
11456 local text = actions.copyText(sx, sy, ex, ey)\\\
11457 actions.undo_add(\\\
11458 { action = 'insertText', args = { sx, sy, text } })\\\
11459\\\
11460 local front = tLines[sy]:sub(1, sx - 1)\\\
11461 local back = tLines[ey]:sub(ex, #tLines[ey])\\\
11462 for _ = 2, ey - sy + 1 do\\\
11463 _remove(tLines, y + 1)\\\
11464 end\\\
11465 tLines[y] = front .. back\\\
11466 if sy ~= ey then\\\
11467 actions.dirty_range(y)\\\
11468 else\\\
11469 actions.dirty()\\\
11470 end\\\
11471 end,\\\
11472\\\
11473 copyText = function(csx, csy, cex, cey)\\\
11474 local count = 0\\\
11475 local lines = { }\\\
11476\\\
11477 for cy = csy, cey do\\\
11478 local line = tLines[cy]\\\
11479 if line then\\\
11480 local cx = 1\\\
11481 local ex = #line\\\
11482 if cy == csy then\\\
11483 cx = csx\\\
11484 end\\\
11485 if cy == cey then\\\
11486 ex = cex - 1\\\
11487 end\\\
11488 local str = line:sub(cx, ex)\\\
11489 count = count + #str\\\
11490 _insert(lines, str)\\\
11491 end\\\
11492 end\\\
11493 return _concat(lines, '\\\\n'), count\\\
11494 end,\\\
11495\\\
11496 delete = function()\\\
11497 if mark.active then\\\
11498 actions.deleteText(mark.x, mark.y, mark.ex, mark.ey)\\\
11499 else\\\
11500 local nLimit = #tLines[y] + 1\\\
11501 if x < nLimit then\\\
11502 actions.deleteText(x, y, x + 1, y)\\\
11503 elseif y < #tLines then\\\
11504 actions.deleteText(x, y, 1, y + 1)\\\
11505 end\\\
11506 end\\\
11507 end,\\\
11508\\\
11509 backspace = function()\\\
11510 if mark.active or actions.left() then\\\
11511 actions.delete()\\\
11512 end\\\
11513 end,\\\
11514\\\
11515 enter = function()\\\
11516 local sLine = tLines[y]\\\
11517 local _,spaces = sLine:find(\\\"^[ ]+\\\")\\\
11518 if not spaces then\\\
11519 spaces = 0\\\
11520 end\\\
11521 spaces = math.min(spaces, x - 1)\\\
11522 if mark.active then\\\
11523 actions.delete()\\\
11524 end\\\
11525 actions.insertText(x, y, '\\\\n' .. _rep(' ', spaces))\\\
11526 end,\\\
11527\\\
11528 char = function(ch)\\\
11529 if mark.active then\\\
11530 actions.delete()\\\
11531 end\\\
11532 actions.insertText(x, y, ch)\\\
11533 end,\\\
11534\\\
11535 copy_marked = function()\\\
11536 local text = actions.copyText(mark.x, mark.y, mark.ex, mark.ey)\\\
11537 os.queueEvent('clipboard_copy', text)\\\
11538 actions.info('shift-^v to paste')\\\
11539 end,\\\
11540\\\
11541 cut = function()\\\
11542 if mark.active then\\\
11543 actions.copy_marked()\\\
11544 actions.delete()\\\
11545 end\\\
11546 end,\\\
11547\\\
11548 copy = function()\\\
11549 if mark.active then\\\
11550 actions.copy_marked()\\\
11551 mark.continue = true\\\
11552 end\\\
11553 end,\\\
11554\\\
11555 paste = function(text)\\\
11556 if mark.active then\\\
11557 actions.delete()\\\
11558 end\\\
11559 if text then\\\
11560 actions.insertText(x, y, text)\\\
11561 actions.info('%d chars added', #text)\\\
11562 else\\\
11563 actions.info('clipboard empty')\\\
11564 end\\\
11565 end,\\\
11566\\\
11567 paste_internal = function()\\\
11568 os.queueEvent('clipboard_paste')\\\
11569 end,\\\
11570\\\
11571 go_to = function(cx, cy)\\\
11572 y = math.min(math.max(cy, 1), #tLines)\\\
11573 x = math.min(math.max(cx, 1), #tLines[y] + 1)\\\
11574 end,\\\
11575\\\
11576 scroll_up = function()\\\
11577 if scrollY > 0 then\\\
11578 scrollY = scrollY - 1\\\
11579 actions.dirty_all()\\\
11580 end\\\
11581 mark.continue = mark.active\\\
11582 end,\\\
11583\\\
11584 scroll_down = function()\\\
11585 local nMaxScroll = #tLines - h\\\
11586 if scrollY < nMaxScroll then\\\
11587 scrollY = scrollY + 1\\\
11588 actions.dirty_all()\\\
11589 end\\\
11590 mark.continue = mark.active\\\
11591 end,\\\
11592\\\
11593 redraw = function()\\\
11594 redraw()\\\
11595 end,\\\
11596\\\
11597 process = function(action, ...)\\\
11598 if not actions[action] then\\\
11599 error('Invaid action: ' .. action)\\\
11600 end\\\
11601\\\
11602 local wasMarking = mark.continue\\\
11603 mark.continue = false\\\
11604\\\
11605 -- for undo purposes, treat tab and enter as char actions\\\
11606 local a = (action == 'tab' or action == 'enter') and 'char' or action\\\
11607 undo.continue = a == undo.lastAction\\\
11608\\\
11609 actions[action](...)\\\
11610\\\
11611 undo.lastAction = a\\\
11612\\\
11613 if x ~= lastPos.x or y ~= lastPos.y then\\\
11614 actions.set_cursor()\\\
11615 end\\\
11616 if not mark.continue and wasMarking then\\\
11617 actions.unmark()\\\
11618 end\\\
11619\\\
11620 actions.redraw()\\\
11621 end,\\\
11622}\\\
11623\\\
11624local args = { ... }\\\
11625local filename = args[1] and shell.resolve(args[1])\\\
11626if not (actions.load(filename) or actions.load(config.filename) or actions.load('untitled.lua')) then\\\
11627 error('Error opening file')\\\
11628end\\\
11629\\\
11630UI:setPage(page)\\\
11631local s, m = pcall(function() UI:start() end)\\\
11632if not s then\\\
11633 actions.save('/crash.txt')\\\
11634 print('Editor has crashed. File saved as /crash.txt')\\\
11635 error(m, -1)\\\
11636end\",\
11637 [ \"core/apis/nameDB.lua\" ] = \"local JSON = require('opus.json')\\\
11638local TableDB = require('core.tableDB')\\\
11639local Util = require('opus.util')\\\
11640\\\
11641local fs = _G.fs\\\
11642\\\
11643local CORE_DIR = '/packages/core/etc/names'\\\
11644local USER_DIR = '/usr/etc/names'\\\
11645\\\
11646local nameDB = TableDB()\\\
11647\\\
11648function nameDB:loadDirectory(directory)\\\
11649 if fs.exists(directory) then\\\
11650 local files = fs.list(directory)\\\
11651 table.sort(files)\\\
11652\\\
11653 for _,file in ipairs(files) do\\\
11654 local mod = file:match('(%S+).json')\\\
11655 if mod then\\\
11656 local blocks = JSON.decodeFromFile(fs.combine(directory, file))\\\
11657\\\
11658 if not blocks then\\\
11659 error('Unable to read ' .. fs.combine(directory, file))\\\
11660 end\\\
11661\\\
11662 for strId, block in pairs(blocks) do\\\
11663 strId = string.format('%s:%s', mod, strId)\\\
11664 if type(block.name) == 'string' then\\\
11665 self.data[strId .. ':0'] = block.name\\\
11666 else\\\
11667 for nid,name in pairs(block.name) do\\\
11668 self.data[strId .. ':' .. (nid-1)] = name\\\
11669 end\\\
11670 end\\\
11671 end\\\
11672\\\
11673 elseif file:match('(%S+).db') then\\\
11674 local names = Util.readTable(fs.combine(directory, file))\\\
11675 if not names then\\\
11676 error('Unable to read ' .. fs.combine(directory, file))\\\
11677 end\\\
11678 for key,name in pairs(names) do\\\
11679 self.data[key] = name\\\
11680 end\\\
11681 end\\\
11682 end\\\
11683 end\\\
11684end\\\
11685\\\
11686function nameDB:load()\\\
11687 self:loadDirectory(CORE_DIR)\\\
11688 self:loadDirectory(USER_DIR)\\\
11689end\\\
11690\\\
11691function nameDB:getName(strId)\\\
11692 return self.data[strId] or strId\\\
11693end\\\
11694\\\
11695nameDB:load()\\\
11696\\\
11697return nameDB\",\
11698 [ \"games/ccTunes.lua\" ] = \"local Sound = require('opus.sound')\\\
11699\\\
11700local os = _G.os\\\
11701\\\
11702local tunes = {\\\
11703 { sound = 'record.11', length = '1:11' },\\\
11704 { sound = 'record.13', length = '2:58' },\\\
11705 { sound = 'record.blocks', length = '5:45' },\\\
11706 { sound = 'record.cat', length = '3:05' },\\\
11707 { sound = 'record.chirp', length = '3:05' },\\\
11708 { sound = 'record.far', length = '2:54' },\\\
11709 { sound = 'record.mall', length = '3:17' },\\\
11710 { sound = 'record.mellohi', length = '1:36' },\\\
11711 { sound = 'record.stal', length = '2:30' },\\\
11712 { sound = 'record.strad', length = '3:08' },\\\
11713 { sound = 'record.wait', length = '3:58' },\\\
11714 { sound = 'record.ward', length = '4:11' },\\\
11715}\\\
11716\\\
11717while true do\\\
11718 local song = tunes[math.random(1, #tunes)]\\\
11719 Sound.play(song.sound)\\\
11720 local min, sec = song.length:match('(%d+):(%d+)')\\\
11721 local length = tonumber(min)*60 + tonumber(sec)\\\
11722 print(string.format('Playing %s (%s)', song.sound, song.length))\\\
11723 os.sleep(length + 3)\\\
11724end\",\
11725 [ \"minify/.package\" ] = \"{\\\
11726 title = 'Lua minification',\\\
11727 repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/minify',\\\
11728 description = [[ Minifies Lua files ]],\\\
11729 license = 'MIT',\\\
11730}\",\
11731 [ \"core/apis/message.lua\" ] = \"local Event = require('opus.event')\\\
11732\\\
11733local Message = { }\\\
11734\\\
11735local messageHandlers = {}\\\
11736\\\
11737function Message.enable()\\\
11738 if not device.wireless_modem.isOpen(os.getComputerID()) then\\\
11739 device.wireless_modem.open(os.getComputerID())\\\
11740 end\\\
11741 if not device.wireless_modem.isOpen(60000) then\\\
11742 device.wireless_modem.open(60000)\\\
11743 end\\\
11744end\\\
11745\\\
11746if device and device.wireless_modem then\\\
11747 Message.enable()\\\
11748end\\\
11749\\\
11750Event.on('device_attach', function(event, deviceName)\\\
11751 if deviceName == 'wireless_modem' then\\\
11752 Message.enable()\\\
11753 end\\\
11754end)\\\
11755\\\
11756function Message.addHandler(type, f)\\\
11757 table.insert(messageHandlers, {\\\
11758 type = type,\\\
11759 f = f,\\\
11760 enabled = true\\\
11761 })\\\
11762end\\\
11763\\\
11764function Message.removeHandler(h)\\\
11765 for k,v in pairs(messageHandlers) do\\\
11766 if v == h then\\\
11767 messageHandlers[k] = nil\\\
11768 break\\\
11769 end\\\
11770 end\\\
11771end\\\
11772\\\
11773Event.on('modem_message',\\\
11774 function(event, side, sendChannel, replyChannel, msg, distance)\\\
11775 if msg and msg.type then -- filter out messages from other systems\\\
11776 local id = replyChannel\\\
11777 for k,h in pairs(messageHandlers) do\\\
11778 if h.type == msg.type then\\\
11779-- should provide msg.contents instead of message - type is already known\\\
11780 h.f(h, id, msg, distance)\\\
11781 end\\\
11782 end\\\
11783 end\\\
11784 end\\\
11785)\\\
11786\\\
11787function Message.send(id, msgType, contents)\\\
11788 if not device.wireless_modem then\\\
11789 error('No modem attached', 2)\\\
11790 end\\\
11791\\\
11792 if id then\\\
11793 device.wireless_modem.transmit(id, os.getComputerID(), {\\\
11794 type = msgType, contents = contents\\\
11795 })\\\
11796 else\\\
11797 device.wireless_modem.transmit(60000, os.getComputerID(), {\\\
11798 type = msgType, contents = contents\\\
11799 })\\\
11800 end\\\
11801end\\\
11802\\\
11803function Message.broadcast(t, contents)\\\
11804 if not device.wireless_modem then\\\
11805 error('No modem attached', 2)\\\
11806 end\\\
11807\\\
11808 Message.send(nil, t, contents)\\\
11809end\\\
11810\\\
11811function Message.waitForMessage(msgType, timeout, fromId)\\\
11812 local timerId = os.startTimer(timeout)\\\
11813 repeat\\\
11814 local e, side, _id, id, msg, distance = os.pullEvent()\\\
11815 if e == 'modem_message' then\\\
11816 if msg and msg.type and msg.type == msgType then\\\
11817 if not fromId or id == fromId then\\\
11818 return e, id, msg, distance\\\
11819 end\\\
11820 end\\\
11821 end\\\
11822 until e == 'timer' and side == timerId\\\
11823end\\\
11824\\\
11825return Message\",\
11826 [ \"common/etc/scripts/shutdown\" ] = \"os.shutdown()\",\
11827 [ \"common/debugMonitor.lua\" ] = \"local Util = require('opus.util')\\\
11828\\\
11829local device = _G.device\\\
11830local os = _G.os\\\
11831local peripheral = _G.peripheral\\\
11832local term = _G.term\\\
11833\\\
11834local args = { ... }\\\
11835local mon = not args[1] and term.current() or\\\
11836 device[args[1]] or\\\
11837 peripheral.wrap(args[1]) or\\\
11838 peripheral.find('monitor') or\\\
11839 error('Syntax: debug <monitor>')\\\
11840\\\
11841mon.clear()\\\
11842if mon.setTextScale then\\\
11843 mon.setTextScale(.5)\\\
11844end\\\
11845mon.setCursorPos(1, 1)\\\
11846\\\
11847local oldDebug = _G._syslog\\\
11848\\\
11849_G._syslog = function(...)\\\
11850 local oldTerm = term.redirect(mon)\\\
11851 Util.print(...)\\\
11852 term.redirect(oldTerm)\\\
11853 oldDebug(...)\\\
11854end\\\
11855\\\
11856repeat\\\
11857 local e, side = os.pullEventRaw('monitor_touch')\\\
11858 if e == 'monitor_touch' and side == mon.side then\\\
11859 mon.clear()\\\
11860 if mon.setTextScale then\\\
11861 mon.setTextScale(.5)\\\
11862 end\\\
11863 mon.setCursorPos(1, 1)\\\
11864 end\\\
11865until e == 'terminate'\\\
11866\\\
11867_G._syslog = oldDebug\",\
11868 [ \"core/apis/proxy.lua\" ] = \"local Socket = require('opus.socket')\\\
11869\\\
11870local Proxy = { }\\\
11871\\\
11872function Proxy.create(remoteId, uri)\\\
11873 local socket, msg = Socket.connect(remoteId, 188)\\\
11874\\\
11875 if not socket then\\\
11876 error(msg)\\\
11877 end\\\
11878\\\
11879 socket.co = coroutine.running()\\\
11880\\\
11881 socket:write(uri)\\\
11882 local methods = socket:read(2) or error('Timed out')\\\
11883\\\
11884 local hijack = { }\\\
11885 for _,method in pairs(methods) do\\\
11886 hijack[method] = function(...)\\\
11887 socket:write({ method, ... })\\\
11888 local resp = socket:read()\\\
11889 if not resp then\\\
11890 error('timed out: ' .. method)\\\
11891 end\\\
11892 return table.unpack(resp)\\\
11893 end\\\
11894 end\\\
11895\\\
11896 return hijack, socket\\\
11897end\\\
11898\\\
11899return Proxy\",\
11900 [ \"common/etc/scripts/setHome\" ] = \"local Config = require('opus.config')\\\
11901local pt = turtle.enableGPS()\\\
11902if pt then\\\
11903 local config = Config.load('gps', { })\\\
11904 config.home = pt\\\
11905 Config.update('gps', config)\\\
11906end\",\
11907 [ \"core/apis/meAdapter.lua\" ] = \"local class = require('opus.class')\\\
11908local itemDB = require('core.itemDB')\\\
11909local Peripheral = require('opus.peripheral')\\\
11910local Util = require('opus.util')\\\
11911\\\
11912local os = _G.os\\\
11913\\\
11914local convertNames = {\\\
11915 name = 'id',\\\
11916 damage = 'dmg',\\\
11917 maxCount = 'max_size',\\\
11918 count = 'qty',\\\
11919 displayName = 'display_name',\\\
11920 maxDamage = 'max_dmg',\\\
11921 nbtHash = 'nbt_hash',\\\
11922}\\\
11923\\\
11924-- Strip off color prefix\\\
11925local function safeString(text)\\\
11926\\\
11927 local val = text:byte(1)\\\
11928\\\
11929 if val < 32 or val > 128 then\\\
11930\\\
11931 local newText = {}\\\
11932 for i = 4, #text do\\\
11933 val = text:byte(i)\\\
11934 newText[i - 3] = (val > 31 and val < 127) and val or 63\\\
11935 end\\\
11936 return string.char(unpack(newText))\\\
11937 end\\\
11938\\\
11939 return text\\\
11940end\\\
11941\\\
11942local function convertItem(item)\\\
11943 for k,v in pairs(convertNames) do\\\
11944 item[k] = item[v]\\\
11945 item[v] = nil\\\
11946 end\\\
11947 item.displayName = safeString(item.displayName)\\\
11948end\\\
11949\\\
11950local MEAdapter = class()\\\
11951\\\
11952function MEAdapter:init(args)\\\
11953 local defaults = {\\\
11954 items = { },\\\
11955 name = 'ME',\\\
11956 jobList = { },\\\
11957 }\\\
11958 Util.merge(self, defaults)\\\
11959 Util.merge(self, args)\\\
11960\\\
11961 local chest\\\
11962\\\
11963 if not self.side then\\\
11964 chest = Peripheral.getByMethod('getAvailableItems')\\\
11965 else\\\
11966 chest = Peripheral.getBySide(self.side)\\\
11967 if chest and not chest.getAvailableItems then\\\
11968 chest = nil\\\
11969 end\\\
11970 end\\\
11971\\\
11972 if chest then\\\
11973 Util.merge(self, chest)\\\
11974 end\\\
11975end\\\
11976\\\
11977function MEAdapter:isValid()\\\
11978 return self.getAvailableItems and self.getAvailableItems()\\\
11979end\\\
11980\\\
11981function MEAdapter:refresh()\\\
11982 self.items = nil\\\
11983 local hasItems, failed\\\
11984\\\
11985 local s, m = pcall(function()\\\
11986 self.items = self.getAvailableItems('all')\\\
11987 for _,v in pairs(self.items) do\\\
11988 Util.merge(v, v.item)\\\
11989 convertItem(v)\\\
11990\\\
11991 -- if power has been interrupted, the list will still be returned\\\
11992 -- but all items will have a 0 quantity\\\
11993 -- ensure that the list is valid\\\
11994 if not hasItems then\\\
11995 hasItems = v.count > 0\\\
11996 end\\\
11997\\\
11998 if not v.fingerprint then\\\
11999 failed = true\\\
12000 break\\\
12001 end\\\
12002\\\
12003 if not itemDB:get(v) then\\\
12004 itemDB:add(v, v)\\\
12005 end\\\
12006 end\\\
12007 end)\\\
12008 itemDB:flush()\\\
12009\\\
12010 if not s and m then\\\
12011 _G._syslog(m)\\\
12012 end\\\
12013\\\
12014 if s and not failed and hasItems and self.items and not Util.empty(self.items) then\\\
12015 return self.items\\\
12016 end\\\
12017 self.items = nil\\\
12018end\\\
12019\\\
12020function MEAdapter:listItems()\\\
12021 self:refresh()\\\
12022 return self.items\\\
12023end\\\
12024\\\
12025function MEAdapter:getItemInfo(item)\\\
12026 for _,i in pairs(self.items) do\\\
12027 if item.name == i.name and\\\
12028 item.damage == i.damage and\\\
12029 item.nbtHash == i.nbtHash then\\\
12030 return i\\\
12031 end\\\
12032 end\\\
12033end\\\
12034\\\
12035function MEAdapter:isCPUAvailable()\\\
12036 local cpus = self.getCraftingCPUs() or { }\\\
12037 local available = false\\\
12038\\\
12039 for cpu,v in pairs(cpus) do\\\
12040 if not v.busy then\\\
12041 available = true\\\
12042 elseif not self.jobList[cpu] then -- something else is crafting something (don't know what)\\\
12043 return false -- return false since we are in an unknown state\\\
12044 end\\\
12045 end\\\
12046 return available\\\
12047end\\\
12048\\\
12049function MEAdapter:craft(item, count)\\\
12050 if not self:isCPUAvailable() then\\\
12051 return false\\\
12052 end\\\
12053\\\
12054 self:refresh()\\\
12055\\\
12056 item = self:getItemInfo(item)\\\
12057 if item and item.is_craftable then\\\
12058\\\
12059 local cpus = self.getCraftingCPUs() or { }\\\
12060 for cpu,v in pairs(cpus) do\\\
12061 if not v.busy then\\\
12062 self.requestCrafting({\\\
12063 id = item.name,\\\
12064 dmg = item.damage,\\\
12065 nbt_hash = item.nbtHash,\\\
12066 },\\\
12067 count or 1,\\\
12068 v.name -- CPUs must be named ! use anvil\\\
12069 )\\\
12070\\\
12071 os.sleep(0) -- needed ?\\\
12072 cpus = self.getCraftingCPUs() or { }\\\
12073\\\
12074 if cpus[cpu].busy then\\\
12075 self.jobList[cpu] = {\\\
12076 name = item.name,\\\
12077 damage = item.damage,\\\
12078 nbtHash = item.nbtHash,\\\
12079 count = count,\\\
12080 }\\\
12081 return true\\\
12082 end\\\
12083 break -- only need to try the first available cpu\\\
12084 end\\\
12085 end\\\
12086 return false\\\
12087 end\\\
12088end\\\
12089\\\
12090function MEAdapter:getJobList()\\\
12091 local cpus = self.getCraftingCPUs() or { }\\\
12092 for cpu,v in pairs(cpus) do\\\
12093 if not v.busy then\\\
12094 self.jobList[cpu] = nil\\\
12095 end\\\
12096 end\\\
12097\\\
12098 return self.jobList\\\
12099end\\\
12100\\\
12101function MEAdapter:isCrafting(item)\\\
12102 for _,v in pairs(self:getJobList()) do\\\
12103 if v.name == item.name and\\\
12104 v.damage == item.damage and\\\
12105 v.nbtHash == item.nbtHash then\\\
12106 return true\\\
12107 end\\\
12108 end\\\
12109end\\\
12110\\\
12111function MEAdapter:craftItems(items)\\\
12112 local cpus = self.getCraftingCPUs() or { }\\\
12113 local count = 0\\\
12114\\\
12115 for _,cpu in pairs(cpus) do\\\
12116 if cpu.busy then\\\
12117 return\\\
12118 end\\\
12119 end\\\
12120\\\
12121 for _,item in pairs(items) do\\\
12122 if count >= #cpus then\\\
12123 break\\\
12124 end\\\
12125 if not self:isCrafting(item) then\\\
12126 if self:craft(item, item.count) then\\\
12127 count = count + 1\\\
12128 end\\\
12129 end\\\
12130 end\\\
12131end\\\
12132\\\
12133function MEAdapter:provide(item, qty, slot, direction)\\\
12134 return pcall(function()\\\
12135 for _,stack in pairs(self.getAvailableItems('all')) do\\\
12136 if stack.item.id == item.name and\\\
12137 (not item.damage or stack.item.dmg == item.damage) and\\\
12138 (not item.nbtHash or stack.item.nbt_hash == item.nbtHash) then\\\
12139 local amount = math.min(qty, stack.item.qty)\\\
12140 if amount > 0 then\\\
12141 self.exportItem(stack.item, direction or self.direction, amount, slot)\\\
12142 end\\\
12143 qty = qty - amount\\\
12144 if qty <= 0 then\\\
12145 break\\\
12146 end\\\
12147 end\\\
12148 end\\\
12149 end)\\\
12150end\\\
12151\\\
12152function MEAdapter:insert(slot, qty, toSlot)\\\
12153 local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot)\\\
12154 if not s and m then\\\
12155 os.sleep(1)\\\
12156 pcall(self.pullItem, self.direction, slot, qty, toSlot)\\\
12157 end\\\
12158end\\\
12159\\\
12160return MEAdapter\",\
12161 [ \"common/autorun/common.lua\" ] = \"local c = function(shell, nIndex, sText)\\\
12162 if nIndex == 1 then\\\
12163 return _G.fs.complete(sText, shell.dir(), true, false)\\\
12164 end\\\
12165end\\\
12166\\\
12167_ENV.shell.setCompletionFunction(\\\"packages/common/edit.lua\\\", c)\\\
12168_ENV.shell.setCompletionFunction(\\\"packages/common/hexedit.lua\\\", c)\",\
12169 [ \"common/etc/scripts/reboot\" ] = \"os.reboot()\",\
12170 [ \"core/apis/itemDB.lua\" ] = \"local Map = require('opus.map')\\\
12171local nameDB = require('core.nameDB')\\\
12172local TableDB = require('core.tableDB')\\\
12173local Util = require('opus.util')\\\
12174\\\
12175local itemDB = TableDB({ fileName = 'usr/config/items.db' })\\\
12176\\\
12177local function safeString(text)\\\
12178 local val = text:byte(1)\\\
12179\\\
12180 if val < 32 or val > 128 then\\\
12181\\\
12182 local newText = { }\\\
12183 local skip = 0\\\
12184 for i = 1, #text do\\\
12185 val = text:byte(i)\\\
12186 if val == 167 then\\\
12187 skip = 2\\\
12188 end\\\
12189 if skip > 0 then\\\
12190 skip = skip - 1\\\
12191 else\\\
12192 if val >= 32 and val <= 128 then\\\
12193 newText[#newText + 1] = val\\\
12194 end\\\
12195 end\\\
12196 end\\\
12197 return string.char(unpack(newText))\\\
12198 end\\\
12199\\\
12200 return text\\\
12201end\\\
12202\\\
12203function itemDB:makeKey(item)\\\
12204 if not item then error('itemDB:makeKey: item is required', 2) end\\\
12205 return table.concat({ item.name, item.damage or '*', item.nbtHash }, ':')\\\
12206end\\\
12207\\\
12208function itemDB:splitKey(key, item)\\\
12209 item = item or { }\\\
12210\\\
12211 local t = Util.split(key, '(.-):')\\\
12212 if #t[#t] > 8 then\\\
12213 item.nbtHash = table.remove(t)\\\
12214 end\\\
12215 local damage = table.remove(t)\\\
12216 if damage ~= '*' then\\\
12217 item.damage = tonumber(damage)\\\
12218 end\\\
12219 item.name = table.concat(t, ':')\\\
12220\\\
12221 return item\\\
12222end\\\
12223\\\
12224function itemDB:get(key, populateFn)\\\
12225 if not key then error('itemDB:get: key is required', 2) end\\\
12226 if type(key) == 'string' then\\\
12227 key = self:splitKey(key)\\\
12228 else\\\
12229 key = Util.shallowCopy(key)\\\
12230 end\\\
12231\\\
12232 local item = self:_get(key)\\\
12233 if not item and populateFn then\\\
12234 item = populateFn()\\\
12235 if item then\\\
12236 item = self:add(item)\\\
12237 end\\\
12238 end\\\
12239\\\
12240 return item and Util.merge(key, item)\\\
12241end\\\
12242\\\
12243function itemDB:_get(key)\\\
12244 if not key then error('itemDB:get: key is required', 2) end\\\
12245 if type(key) == 'string' then\\\
12246 key = self:splitKey(key)\\\
12247 end\\\
12248\\\
12249 local item = TableDB.get(self, self:makeKey(key))\\\
12250 if item then\\\
12251 return item\\\
12252 end\\\
12253\\\
12254 -- try finding an item that has damage values\\\
12255 if type(key.damage) == 'number' then\\\
12256 item = TableDB.get(self, self:makeKey({ name = key.name, nbtHash = key.nbtHash }))\\\
12257 if item and item.maxDamage then\\\
12258 item = Util.shallowCopy(item)\\\
12259 item.damage = key.damage\\\
12260 if item.maxDamage > 0 and type(item.damage) == 'number' and item.damage > 0 then\\\
12261 item.displayName = string.format('%s (damage: %s)', item.displayName, item.damage)\\\
12262 end\\\
12263 return item\\\
12264 end\\\
12265 else\\\
12266 for k,item in pairs(self.data) do\\\
12267 if key.name == item.name and\\\
12268 key.nbtHash == key.nbtHash and\\\
12269 item.maxDamage > 0 then\\\
12270 item = Util.shallowCopy(item)\\\
12271 item.nbtHash = key.nbtHash\\\
12272 return item\\\
12273 end\\\
12274 end\\\
12275 end\\\
12276\\\
12277 if key.nbtHash then\\\
12278 item = self:get({ name = key.name, damage = key.damage })\\\
12279\\\
12280 if item and item.ignoreNBT then\\\
12281 item = Util.shallowCopy(item)\\\
12282 item.nbtHash = key.nbtHash\\\
12283 item.damage = key.damage\\\
12284 return item\\\
12285 end\\\
12286 end\\\
12287end\\\
12288\\\
12289local function formatTime(t)\\\
12290 local m = math.floor(t/60)\\\
12291 local s = t % 60\\\
12292 if s < 10 then\\\
12293 s = '0' .. s\\\
12294 end\\\
12295\\\
12296 return m .. ':' .. s\\\
12297end\\\
12298\\\
12299--[[\\\
12300 If the base item contains an NBT hash, then the NBT hash uniquely\\\
12301 identifies this item.\\\
12302]]--\\\
12303function itemDB:add(baseItem)\\\
12304 local nItem = {\\\
12305 name = baseItem.name,\\\
12306 damage = baseItem.damage,\\\
12307 nbtHash = baseItem.nbtHash,\\\
12308 }\\\
12309-- if detail.maxDamage > 0 then\\\
12310-- nItem.damage = '*'\\\
12311-- end\\\
12312\\\
12313 nItem.displayName = safeString(baseItem.displayName)\\\
12314 nItem.maxCount = baseItem.maxCount\\\
12315 nItem.maxDamage = baseItem.maxDamage\\\
12316\\\
12317 -- enchanted items\\\
12318 if baseItem.enchantments then\\\
12319 if nItem.name == 'minecraft:enchanted_book' then\\\
12320 nItem.displayName = 'Book: '\\\
12321 else\\\
12322 nItem.displayName = nItem.displayName .. ': '\\\
12323 end\\\
12324 for k, v in ipairs(baseItem.enchantments) do\\\
12325 if k > 1 then\\\
12326 nItem.displayName = nItem.displayName .. ', '\\\
12327 end\\\
12328 nItem.displayName = nItem.displayName .. v.fullName\\\
12329 end\\\
12330\\\
12331 -- turtles / computers / etc\\\
12332 elseif baseItem.computer then\\\
12333 -- a turtle's NBT is updated constantly\\\
12334 -- update the cache with the new NBT\\\
12335 if baseItem.computer.id then\\\
12336 Map.removeMatches(self.data, { name = nItem.name, displayName = nItem.displayName })\\\
12337 end\\\
12338 nItem.displayName = baseItem.computer.label or baseItem.displayName\\\
12339\\\
12340 -- disks\\\
12341 elseif baseItem.media then\\\
12342 -- don't ignore nbt... as disks can be labeled\\\
12343 if baseItem.media.recordTitle then\\\
12344 nItem.displayName = nItem.displayName .. ': ' .. baseItem.media.recordTitle\\\
12345 end\\\
12346\\\
12347 -- potions\\\
12348 elseif nItem.name == 'minecraft:potion' or nItem.name == 'minecraft:lingering_potion' then\\\
12349 if baseItem.effects then\\\
12350 local effect = baseItem.effects[1]\\\
12351 if effect.amplifier == 1 then\\\
12352 nItem.displayName = nItem.displayName .. ' II'\\\
12353 end\\\
12354 if effect.duration and effect.duration > 0 then\\\
12355 nItem.displayName = string.format('%s (%s)', nItem.displayName, formatTime(effect.duration))\\\
12356 end\\\
12357 end\\\
12358\\\
12359 else\\\
12360 for k,item in pairs(self.data) do\\\
12361 if nItem.name == item.name and\\\
12362 nItem.displayName == item.displayName then\\\
12363\\\
12364 if nItem.nbtHash ~= item.nbtHash and nItem.damage ~= item.damage then\\\
12365 nItem.damage = '*'\\\
12366 nItem.nbtHash = nil\\\
12367 nItem.ignoreNBT = true\\\
12368 self.data[k] = nil\\\
12369 break\\\
12370 elseif nItem.damage ~= item.damage then\\\
12371 nItem.damage = '*'\\\
12372 self.data[k] = nil\\\
12373 break\\\
12374 elseif nItem.nbtHash ~= item.nbtHash then\\\
12375 nItem.nbtHash = nil\\\
12376 nItem.ignoreNBT = true\\\
12377 self.data[k] = nil\\\
12378 break\\\
12379 end\\\
12380 end\\\
12381 end\\\
12382 end\\\
12383\\\
12384 TableDB.add(self, self:makeKey(nItem), nItem)\\\
12385 nItem = Util.shallowCopy(nItem)\\\
12386 nItem.damage = baseItem.damage\\\
12387 nItem.nbtHash = baseItem.nbtHash\\\
12388\\\
12389 return nItem\\\
12390end\\\
12391\\\
12392-- Accepts: \\\"minecraft:stick:0\\\" or { name = 'minecraft:stick', damage = 0 }\\\
12393function itemDB:getName(item)\\\
12394 if type(item) == 'string' then\\\
12395 item = self:splitKey(item)\\\
12396 end\\\
12397\\\
12398 local detail = self:get(item)\\\
12399 if detail then\\\
12400 return detail.displayName\\\
12401 end\\\
12402\\\
12403 -- fallback to nameDB\\\
12404 local strId = self:makeKey(item)\\\
12405 local name = nameDB.data[strId]\\\
12406 if not name and not item.damage then\\\
12407 name = nameDB.data[self:makeKey({ name = item.name, damage = 0, nbtHash = item.nbtHash })]\\\
12408 end\\\
12409 return name or strId\\\
12410end\\\
12411\\\
12412function itemDB:getMaxCount(item)\\\
12413 local detail = self:get(item)\\\
12414 return detail and detail.maxCount or 64\\\
12415end\\\
12416\\\
12417function itemDB:load()\\\
12418 TableDB.load(self)\\\
12419\\\
12420 for key,item in pairs(self.data) do\\\
12421 self:splitKey(key, item)\\\
12422 item.maxDamage = item.maxDamage or 0\\\
12423 item.maxCount = item.maxCount or 64\\\
12424 end\\\
12425end\\\
12426\\\
12427function itemDB:flush()\\\
12428 if self.dirty then\\\
12429\\\
12430 local t = { }\\\
12431 for k,v in pairs(self.data) do\\\
12432 v = Util.shallowCopy(v)\\\
12433 v.name = nil\\\
12434 v.damage = nil\\\
12435 v.nbtHash = nil\\\
12436v.count = nil -- wipe out previously saved counts - temporary\\\
12437 if v.maxDamage == 0 then\\\
12438 v.maxDamage = nil\\\
12439 end\\\
12440 if v.maxCount == 64 then\\\
12441 v.maxCount = nil\\\
12442 end\\\
12443 t[k] = v\\\
12444 end\\\
12445\\\
12446 Util.writeTable(self.fileName, t)\\\
12447 self.dirty = false\\\
12448 end\\\
12449end\\\
12450\\\
12451itemDB:load()\\\
12452\\\
12453return itemDB\",\
12454 [ \"common/etc/apps.db\" ] = \"{\\\
12455 [ \\\"c5497bca58468ae64aed6c0fd921109217988db3\\\" ] = {\\\
12456 title = \\\"Events\\\",\\\
12457 category = \\\"System\\\",\\\
12458 iconExt = \\\"\\\\0300\\\\031 \\\\159\\\\135\\\\030 \\\\0310\\\\156\\\\0301\\\\031 \\\\159\\\\030 \\\\0311\\\\144\\\\0300\\\\031 \\\\147\\\\139\\\\030 \\\\0310\\\\144\\\\010\\\\0300\\\\128\\\\128\\\\030 \\\\149\\\\0311\\\\157\\\\142\\\\0300\\\\031 \\\\149\\\\0310\\\\128\\\\128\\\\010\\\\030 \\\\130\\\\139\\\\141\\\\0311\\\\130\\\\131\\\\0310\\\\142\\\\135\\\\129\\\",\\\
12459 run = \\\"Events.lua\\\",\\\
12460 },\\\
12461 [ \\\"f1e14c30480042e8c71b5482d92ef161815b915e\\\" ] = {\\\
12462 title = \\\"DiskCopy\\\",\\\
12463 category = \\\"System\\\",\\\
12464 run = \\\"DiskCopy\\\",\\\
12465 iconExt = \\\"\\\\0305\\\\0318\\\\138\\\\0303\\\\159\\\\0305\\\\0317\\\\133\\\\030 \\\\0315\\\\148\\\\031 \\\\128\\\\0318\\\\151\\\\129\\\\010\\\\0300\\\\0315\\\\149\\\\030b\\\\0318\\\\138\\\\143\\\\0317\\\\133\\\\030 \\\\031b\\\\148\\\\0308\\\\031 \\\\140\\\\030 \\\\0318\\\\145\\\\010\\\\030 \\\\031 \\\\128\\\\0300\\\\031b\\\\149\\\\0310\\\\128\\\\128\\\\030 \\\\031b\\\\149\\\\0318\\\\157\\\\133\\\",\\\
12466 },\\\
12467 [ \\\"7ef35cac539f84722b0a988caee03b2df734c56a\\\" ] = {\\\
12468 title = \\\"AppStore\\\",\\\
12469 category = \\\"System\\\",\\\
12470 icon = \\\"\\\\030 \\\\0310=\\\\0300 \\\\030 XX\\\\0300\\\\031f \\\\030 \\\\010\\\\030 \\\\031f \\\\0300 \\\\030 \\\\010\\\\030 \\\\031f \\\\0310o \\\\031f \\\\0310o\\\\031f \\\",\\\
12471 iconExt = \\\"\\\\030 \\\\031e\\\\139\\\\0318\\\\151\\\\151\\\\151\\\\151\\\\151\\\\149\\\\010\\\\030 \\\\031 \\\\128\\\\0308\\\\136\\\\136\\\\136\\\\136\\\\030 \\\\0318\\\\135\\\\031 \\\\128\\\\010\\\\030 \\\\031 \\\\128\\\\0317\\\\130\\\\031 \\\\128\\\\0317\\\\130\\\\031 \\\\128\\\\128\\\\128\\\",\\\
12472 run = \\\"Appstore.lua\\\",\\\
12473 },\\\
12474 [ \\\"90ef98d4b6fd15466f0a1f212ec1db8d9ebe018c\\\" ] = {\\\
12475 title = \\\"Turtles\\\",\\\
12476 category = \\\"Apps\\\",\\\
12477 icon = \\\" \\\\0305 \\\\030c \\\\0305 \\\\030 \\\\010\\\\030d \\\\030c \\\\0305 \\\\030c \\\\0308 \\\\030d\\\\031f\\\\\\\"\\\\010 \\\\0308\\\\031f.\\\\030 \\\\031 \\\\0308\\\\031f.\\\\030 \\\\031 \\\",\\\
12478 iconExt = \\\"\\\\030 \\\\031 \\\\128\\\\0305\\\\135\\\\030c\\\\031c\\\\128\\\\128\\\\0305\\\\031 \\\\139\\\\0307\\\\149\\\\0308\\\\0317\\\\143\\\\0307\\\\128\\\\010\\\\030c\\\\031 \\\\145\\\\031c\\\\128\\\\030d\\\\132\\\\136\\\\030c\\\\128\\\\0307\\\\149\\\\0318\\\\143\\\\133\\\\010\\\\030 \\\\031 \\\\128\\\\0317\\\\143\\\\031 \\\\128\\\\128\\\\0317\\\\143\\\\031 \\\\128\\\\128\\\\128\\\",\\\
12479 run = \\\"Turtles.lua\\\",\\\
12480 },\\\
12481 [ \\\"66587a7a62a90fc91c4c88f1872bedaa52d23a35\\\" ] = {\\\
12482 title = \\\"Follow\\\",\\\
12483 category = \\\"Turtle\\\",\\\
12484 run = \\\"Follow.lua\\\",\\\
12485 iconExt = \\\"\\\\030 \\\\031 \\\\128\\\\128\\\\128\\\\128\\\\128\\\\128\\\\0314\\\\144\\\\031 \\\\128\\\\010\\\\030 \\\\031 \\\\128\\\\128\\\\128\\\\128\\\\0304\\\\139\\\\133\\\\0310\\\\130\\\\030 \\\\0311\\\\156\\\\010\\\\0307\\\\0318\\\\136\\\\030 \\\\0317\\\\149\\\\0307\\\\0318\\\\136\\\\030 \\\\0317\\\\149\\\\0304\\\\031 \\\\144\\\\0314\\\\128\\\\030 \\\\159\\\\031 \\\\128\\\",\\\
12486 },\\\
12487 [ \\\"df485c871329671f46570634d63216761441bcd6\\\" ] = {\\\
12488 title = \\\"Devices\\\",\\\
12489 category = \\\"System\\\",\\\
12490 icon = \\\"\\\\0304 \\\\030 \\\\010\\\\030f \\\\0304 \\\\0307 \\\\030 \\\\031 \\\\031f_\\\\010\\\\030f \\\\0304 \\\\0307 \\\\030 \\\\031f/\\\",\\\
12491 iconExt = \\\"\\\\030 \\\\031 \\\\128\\\\128\\\\128\\\\0308\\\\159\\\\143\\\\0300\\\\0317\\\\151\\\\0307\\\\0310\\\\140\\\\148\\\\010\\\\0314\\\\151\\\\131\\\\0304\\\\031f\\\\148\\\\030 \\\\0318\\\\138\\\\148\\\\0307\\\\0310\\\\138\\\\131\\\\129\\\\010\\\\0304\\\\031f\\\\138\\\\143\\\\133\\\\030 \\\\0318\\\\131\\\\129\\\\031 \\\\128\\\\128\\\\128\\\",\\\
12492 run = \\\"Devices.lua\\\",\\\
12493 },\\\
12494 [ \\\"114edfc04a1ab03541bdc80ce064f66a7cfcedbb\\\" ] = {\\\
12495 title = \\\"Recorder\\\",\\\
12496 category = \\\"Apps\\\",\\\
12497 icon = \\\"\\\\030 \\\\031f \\\\031b \\\\031foo \\\\010\\\\030 \\\\031f \\\\030e\\\\031b \\\\030 \\\\031f/\\\\010\\\\030 \\\\031b \\\\030e \\\\030 \\\\031f\\\\\\\\\\\",\\\
12498 iconExt = \\\"\\\\030 \\\\031 \\\\128\\\\030e\\\\143\\\\030 \\\\031e\\\\144\\\\031 \\\\128\\\\0304\\\\149\\\\0307\\\\0314\\\\131\\\\131\\\\030 \\\\149\\\\010\\\\030e\\\\031 \\\\129\\\\031e\\\\128\\\\128\\\\030 \\\\148\\\\0304\\\\031 \\\\149\\\\0307\\\\0318\\\\140\\\\140\\\\030 \\\\0314\\\\149\\\\010\\\\030 \\\\031e\\\\139\\\\030e\\\\128\\\\030 \\\\159\\\\129\\\\0314\\\\130\\\\131\\\\131\\\\129\\\",\\\
12499 run = \\\"recorder.lua\\\",\\\
12500 },\\\
12501 [ \\\"5c7e3aec1f9179ce3325a4f7a101dca65ac905f3\\\" ] = {\\\
12502 title = \\\"Swarm\\\",\\\
12503 category = \\\"Turtle\\\",\\\
12504 requires = \\\"neuralInterface\\\",\\\
12505 icon = \\\"\\\\030 \\\\0315\\\\\\\\\\\\030 \\\\031 \\\\010\\\\030 \\\\0304\\\\031f _ \\\\030 \\\\031c/\\\\0315\\\\\\\\\\\\010\\\\030 \\\\0304 \\\",\\\
12506 run = \\\"multiMiner.lua\\\",\\\
12507 },\\\
12508 [ \\\"783ecb3650eabd68f3caadc387abd23018132967\\\" ] = {\\\
12509 title = \\\"HexEdit\\\",\\\
12510 category = \\\"Apps\\\",\\\
12511 requires = \\\"advancedComputer\\\",\\\
12512 iconExt = \\\"\\\\030 \\\\031 \\\\128\\\\030d\\\\159\\\\030 \\\\031d\\\\140\\\\030d\\\\031 \\\\155\\\\030 \\\\0315\\\\140\\\\0305\\\\031 \\\\155\\\\030 \\\\128\\\\010\\\\030 \\\\031d\\\\136\\\\145\\\\0315\\\\136\\\\145\\\\031d\\\\153\\\\031 \\\\128\\\\0315\\\\153\\\\010\\\\030 \\\\031 \\\\128\\\\031d\\\\130\\\\140\\\\134\\\\0315\\\\140\\\\134\\\\031 \\\\128\\\",\\\
12513 run = \\\"fileui --exec=hexedit.lua --title=hexedit\\\",\\\
12514 },\\\
12515 [ \\\"fb1c39e9f4f3c2628ad173ab401a6e4e4baf783d\\\" ] = {\\\
12516 title = \\\"Sounds\\\",\\\
12517 category = \\\"System\\\",\\\
12518 run = \\\"SoundPlayer\\\",\\\
12519 iconExt = \\\"\\\\030 \\\\031 \\\\128\\\\0307\\\\159\\\\129\\\\030 \\\\0317\\\\149\\\\0310\\\\144\\\\0300\\\\031 \\\\155\\\\030 \\\\0310\\\\137\\\\144\\\\010\\\\0307\\\\0317\\\\128\\\\128\\\\128\\\\030 \\\\149\\\\0300\\\\031 \\\\149\\\\030 \\\\128\\\\0310\\\\149\\\\0300\\\\031 \\\\149\\\\010\\\\030 \\\\031 \\\\128\\\\0317\\\\130\\\\0307\\\\031 \\\\144\\\\030 \\\\0317\\\\149\\\\0310\\\\129\\\\134\\\\152\\\\129\\\",\\\
12520 },\\\
12521 [ \\\"464c4ffd019e1e9691dcf0537c797353ef2b1c1d4833d3d463e5b74ae4547344\\\" ] = {\\\
12522 title = \\\"Editor\\\",\\\
12523 category = \\\"Apps\\\",\\\
12524 run = \\\"edit\\\",\\\
12525 iconExt = \\\"7\\\\\\\
12526¨¨¨¨f0¨\\\\\\\
12527¨¨f0¨¨f\\\",\\\
12528 },\\\
12529 [ \\\"3f00927a719345edd4a8316599d3b328857987547f8884306861161ffa09647e\\\" ] = {\\\
12530 title = \\\"Write\\\",\\\
12531 category = \\\"Apps\\\",\\\
12532 run = \\\"write\\\",\\\
12533 },\\\
12534}\",\
12535 [ \"core/apis/inventoryAdapter.lua\" ] = \"local Adapter = { }\\\
12536\\\
12537function Adapter.wrap(args)\\\
12538 local adapters = {\\\
12539 'core.refinedAdapter',\\\
12540 'core.meAdapter18',\\\
12541 'core.chestAdapter18',\\\
12542\\\
12543 -- adapters for version 1.7\\\
12544 'core.meAdapter',\\\
12545 'core.chestAdapter',\\\
12546 }\\\
12547\\\
12548 for _,adapterType in ipairs(adapters) do\\\
12549 local adapter = require(adapterType)(args)\\\
12550\\\
12551 if adapter:isValid() then\\\
12552\\\
12553 -- figure out which direction to push/pull items from an inventory\\\
12554 -- based on the side the inventory is attached and which way the\\\
12555 -- turtle/computer is facing\\\
12556 if args and args.facing and adapter.side and not adapter.direction then\\\
12557 local horz = { top = 'down', bottom = 'up' }\\\
12558 adapter.direction = horz[adapter.side]\\\
12559\\\
12560 if not adapter.direction then\\\
12561 local sides = {\\\
12562 front = 0,\\\
12563 right = 1,\\\
12564 back = 2,\\\
12565 left = 3,\\\
12566 }\\\
12567 -- pretty sure computer/turtle have sides reversed\\\
12568 local cards = {\\\
12569 east = 0,\\\
12570 south = 1,\\\
12571 west = 2,\\\
12572 north = 3,\\\
12573 }\\\
12574 local icards = {\\\
12575 [ 0 ] = 'west',\\\
12576 [ 1 ] = 'north',\\\
12577 [ 2 ] = 'east',\\\
12578 [ 3 ] = 'south',\\\
12579 }\\\
12580 adapter.direction = icards[(cards[args.facing] + sides[adapter.side]) % 4]\\\
12581 end\\\
12582 end\\\
12583 return adapter\\\
12584 end\\\
12585 end\\\
12586end\\\
12587\\\
12588return Adapter\",\
12589 [ \"common/etc/scripts/goHome\" ] = \"local config = require('opus.config').load('gps')\\\
12590if config.home then\\\
12591 if turtle.enableGPS() then\\\
12592 return turtle.pathfind(config.home)\\\
12593 end\\\
12594end\",\
12595 [ \"core/apis/tableDB.lua\" ] = \"local class = require('opus.class')\\\
12596local Util = require('opus.util')\\\
12597\\\
12598local TableDB = class()\\\
12599function TableDB:init(args)\\\
12600 local defaults = {\\\
12601 fileName = '',\\\
12602 dirty = false,\\\
12603 data = { },\\\
12604 }\\\
12605 Util.merge(defaults, args)\\\
12606 Util.merge(self, defaults)\\\
12607end\\\
12608\\\
12609function TableDB:load()\\\
12610 local t = Util.readTable(self.fileName)\\\
12611 if t then\\\
12612 self.data = t.data or t\\\
12613 end\\\
12614end\\\
12615\\\
12616function TableDB:add(key, entry)\\\
12617 if type(key) == 'table' then\\\
12618 key = table.concat(key, ':')\\\
12619 end\\\
12620 self.data[key] = entry\\\
12621 self.dirty = true\\\
12622end\\\
12623\\\
12624function TableDB:get(key)\\\
12625 if type(key) == 'table' then\\\
12626 key = table.concat(key, ':')\\\
12627 end\\\
12628 return self.data[key]\\\
12629end\\\
12630\\\
12631function TableDB:remove(key)\\\
12632 self.data[key] = nil\\\
12633 self.dirty = true\\\
12634end\\\
12635\\\
12636function TableDB:flush()\\\
12637 if self.dirty then\\\
12638 Util.writeTable(self.fileName, self.data)\\\
12639 self.dirty = false\\\
12640 end\\\
12641end\\\
12642\\\
12643return TableDB\",\
12644 [ \"common/Devices.lua\" ] = \"local Ansi = require('opus.ansi')\\\
12645local Event = require('opus.event')\\\
12646local UI = require('opus.ui')\\\
12647local Util = require('opus.util')\\\
12648\\\
12649local device = _G.device\\\
12650\\\
12651--[[ -- PeripheralsPage -- ]] --\\\
12652local peripheralsPage = UI.Page {\\\
12653 grid = UI.ScrollingGrid {\\\
12654 ey = -2,\\\
12655 columns = {\\\
12656 --{ heading = 'Name', key = 'name' },\\\
12657 { heading = 'Type', key = 'type' },\\\
12658 { heading = 'Side', key = 'side' },\\\
12659 },\\\
12660 sortColumn = 'type',\\\
12661 autospace = true,\\\
12662 enable = function(self)\\\
12663 Util.clear(self.values)\\\
12664 for _,v in pairs(device) do\\\
12665 table.insert(self.values, {\\\
12666 type = v.type,\\\
12667 side = v.side,\\\
12668 name = v.name,\\\
12669 })\\\
12670 end\\\
12671 self:update()\\\
12672 self:adjustWidth()\\\
12673 UI.Grid.enable(self)\\\
12674 end,\\\
12675 },\\\
12676 statusBar = UI.StatusBar {\\\
12677 values = 'Select peripheral',\\\
12678 },\\\
12679 accelerators = {\\\
12680 [ 'control-q' ] = 'quit',\\\
12681 },\\\
12682 updatePeripherals = function(self)\\\
12683 if UI:getCurrentPage() == self then\\\
12684 self.grid:draw()\\\
12685 self:sync()\\\
12686 end\\\
12687 end,\\\
12688 eventHandler = function(self, event)\\\
12689 if event.type == 'quit' then\\\
12690 UI:quit()\\\
12691\\\
12692 elseif event.type == 'grid_select' then\\\
12693 UI:setPage('methods', event.selected)\\\
12694\\\
12695 end\\\
12696 return UI.Page.eventHandler(self, event)\\\
12697 end,\\\
12698}\\\
12699\\\
12700--[[ -- MethodsPage -- ]] --\\\
12701local methodsPage = UI.Page {\\\
12702 doc = UI.TextArea {\\\
12703 backgroundColor = 'black',\\\
12704 ey = -7,\\\
12705 marginLeft = 1, marginTop = 1,\\\
12706 },\\\
12707 grid = UI.ScrollingGrid {\\\
12708 y = -6, ey = -2,\\\
12709 columns = {\\\
12710 { heading = 'Name', key = 'name' }\\\
12711 },\\\
12712 sortColumn = 'name',\\\
12713 },\\\
12714 statusBar = UI.StatusBar {\\\
12715 status = 'q to return',\\\
12716 },\\\
12717 accelerators = {\\\
12718 [ 'control-q' ] = 'back',\\\
12719 backspace = 'back',\\\
12720 },\\\
12721 enable = function(self, p)\\\
12722 self.peripheral = p or self.peripheral\\\
12723\\\
12724 p = device[self.peripheral.name]\\\
12725 if p.getDocs then\\\
12726 -- plethora\\\
12727 self.grid.values = { }\\\
12728 for k,v in pairs(p.getDocs()) do\\\
12729 table.insert(self.grid.values, {\\\
12730 name = k,\\\
12731 doc = v,\\\
12732 })\\\
12733 end\\\
12734 elseif not p.getAdvancedMethodsData then\\\
12735 -- computercraft\\\
12736 self.grid.values = { }\\\
12737 for k,v in pairs(p) do\\\
12738 if type(v) == 'function' then\\\
12739 table.insert(self.grid.values, {\\\
12740 name = k,\\\
12741 noext = true,\\\
12742 })\\\
12743 end\\\
12744 end\\\
12745 else\\\
12746 -- open peripherals\\\
12747 self.grid.values = p.getAdvancedMethodsData()\\\
12748 for name,f in pairs(self.grid.values) do\\\
12749 f.name = name\\\
12750 end\\\
12751 end\\\
12752\\\
12753 self.grid:update()\\\
12754 self.grid:setIndex(1)\\\
12755\\\
12756 self.doc:setText(self:getDocumentation())\\\
12757\\\
12758 self.statusBar:setStatus(self.peripheral.type)\\\
12759 UI.Page.enable(self)\\\
12760\\\
12761 self:setFocus(self.grid)\\\
12762 end,\\\
12763}\\\
12764\\\
12765function methodsPage:eventHandler(event)\\\
12766 if event.type == 'back' then\\\
12767 UI:setPage(peripheralsPage)\\\
12768 return true\\\
12769 elseif event.type == 'grid_focus_row' then\\\
12770 self.doc:setText(self:getDocumentation())\\\
12771 end\\\
12772 return UI.Page.eventHandler(self, event)\\\
12773end\\\
12774\\\
12775function methodsPage:getDocumentation()\\\
12776 local method = self.grid:getSelected()\\\
12777\\\
12778 if not method or method.noext then -- computercraft docs\\\
12779 return 'No documentation'\\\
12780 end\\\
12781\\\
12782 if method.doc then -- plethora docs\\\
12783 return Ansi.yellow .. method.doc\\\
12784 end\\\
12785\\\
12786 -- open peripherals docs\\\
12787 local sb = { }\\\
12788 if method.description then\\\
12789 table.insert(sb, method.description .. '\\\\n\\\\n')\\\
12790 end\\\
12791\\\
12792 if method.returnTypes ~= '()' then\\\
12793 table.insert(sb, Ansi.yellow .. method.returnTypes .. ' ')\\\
12794 end\\\
12795 table.insert(sb, Ansi.blue .. method.name .. Ansi.reset .. '(')\\\
12796\\\
12797 for k,arg in ipairs(method.args) do\\\
12798 if arg.optional then\\\
12799 table.insert(sb, Ansi.orange .. string.format('[%s]', arg.name))\\\
12800 else\\\
12801 table.insert(sb, Ansi.green .. arg.name)\\\
12802 end\\\
12803 if k < #method.args then\\\
12804 table.insert(sb, ',')\\\
12805 end\\\
12806 end\\\
12807 table.insert(sb, Ansi.reset .. ')')\\\
12808\\\
12809 Util.filterInplace(method.args, function(a) return #a.description > 0 end)\\\
12810 if #method.args > 0 then\\\
12811 table.insert(sb, '\\\\n\\\\n')\\\
12812 for k,arg in ipairs(method.args) do\\\
12813 if arg.optional then\\\
12814 table.insert(sb, Ansi.orange)\\\
12815 else\\\
12816 table.insert(sb, Ansi.green)\\\
12817 end\\\
12818 table.insert(sb, arg.name .. Ansi.reset .. ': ' .. arg.description)\\\
12819 if k ~= #method.args then\\\
12820 table.insert(sb, '\\\\n\\\\n')\\\
12821 end\\\
12822 end\\\
12823 end\\\
12824 return table.concat(sb)\\\
12825end\\\
12826\\\
12827Event.on('peripheral', function()\\\
12828 peripheralsPage:updatePeripherals()\\\
12829end)\\\
12830\\\
12831Event.on('peripheral_detach', function()\\\
12832 peripheralsPage:updatePeripherals()\\\
12833end)\\\
12834\\\
12835UI:setPage(peripheralsPage)\\\
12836\\\
12837UI:setPages({\\\
12838 methods = methodsPage,\\\
12839})\\\
12840\\\
12841UI:start()\",\
12842 },\
12843}")
12844if fs.isReadOnly(outputPath) then
12845 error("Output path is read-only.")
12846elseif fs.getFreeSpace(outputPath) <= #archive then
12847 error("Insufficient space.")
12848end
12849fs.delete(shell.getRunningProgram()) -- saves space
12850for name, contents in pairs(archive.data) do
12851 stc(colors.lightGray)
12852 write("'" .. name .. "'...")
12853 if contents == true then -- indicates empty directory
12854 fs.makeDir(fs.combine(outputPath, name))
12855 else
12856 file = fs.open(fs.combine(outputPath, name), "w")
12857 if file then
12858 file.write(contents)
12859 file.close()
12860 end
12861 end
12862 if file then
12863 stc(colors.green)
12864 print("good")
12865 else
12866 stc(colors.red)
12867 print("fail")
12868 end
12869end
12870stc(colors.white)
12871write("Unpacked to '")
12872stc(colors.yellow)
12873write(outputPath .. "/")
12874stc(colors.white)
12875print("'.")