Advertisement
JereTheJuggler

Untitled

Mar 1st, 2019
154
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.32 KB | None | 0 0
  1. local promptColor = colors.yellow
  2. local textColor = colors.white
  3. local backgroundColor = colors.black
  4. local errorColor = colors.red
  5. local successColor = colors.green
  6. local titleColor = colors.cyan
  7. local buttonBackgroundColor = colors.red
  8. local buttonTextColor = colors.black
  9.  
  10. local recipes = {}
  11. local recipeConfigPath = shell.resolve("").."/autocrafting/recipes.config"
  12. -- structure={
  13. -- [itemIndexName]={
  14. -- name=(string),
  15. -- damageValues={
  16. -- (int)={
  17. -- displayName=(string),
  18. -- recipes={
  19. -- {
  20. -- recipe={
  21. -- [itemIndexName]={
  22. -- slots={(int),...},
  23. -- displayName=(string),
  24. -- name=(string),
  25. -- damage=(int)
  26. -- },
  27. -- ...
  28. -- },
  29. -- resultQty=(int)
  30. -- },
  31. -- ...
  32. -- }
  33. -- },
  34. -- ...
  35. -- }
  36. -- }
  37. -- }
  38.  
  39. local inventories = {}
  40. local outputInventory = nil
  41. local recipeInputInventory = nil
  42. local peripheralConfigPath = shell.resolve("").."/autocrafting/peripherals.config"
  43.  
  44. local turtle = nil
  45.  
  46. local indexedItems = {}
  47. -- An item's index name is defined as the item's internal name with all special characters removed
  48. --
  49. -- structure={
  50. -- [itemIndexName]={
  51. -- name=(string),
  52. -- damageValues={
  53. -- [int]={
  54. -- displayName=(string),
  55. -- locations={
  56. -- {
  57. -- perip=(string) the name of the inventory peripheral that this item can be gotten from,
  58. -- count=(int) the count of that item in the peripheral's inventory,
  59. -- slot=(int) the slot the item can be pulled from
  60. -- },
  61. -- ...
  62. -- }
  63. -- },
  64. -- ...
  65. -- }
  66. -- },
  67. -- ...
  68. -- }
  69.  
  70.  
  71. local orderQueue = {}
  72. -- structure={
  73. -- {
  74. -- result=(string) result display name,
  75. -- missingIngredients=(table) the ingredients required for crafting that were not present and do not have a recipe stored for them{
  76. -- [itemIndexName]={
  77. -- displayName=(string),
  78. -- qtyMissing=(int)
  79. -- },
  80. -- ...
  81. -- }
  82. -- },
  83. -- ...
  84. -- }
  85.  
  86. function getItemIndexName(itemName)
  87. return string.gsub(itemName,"([^%d%l%u])","")
  88. end
  89.  
  90. function resetRecipes()
  91. recipes = {}
  92. end
  93.  
  94. function saveRecipeConfig()
  95. local file = fs.open(recipeConfigPath,"w")
  96. file.writeLine(textutils.serialize(recipes))
  97. file.close()
  98. end
  99.  
  100. function prepareForSerialize(ob)
  101. local result = nil
  102. if type(ob) == "function" then
  103. result = "function"
  104. elseif type(ob) == "table" then
  105. result = {}
  106. for k,i in pairs(ob) do
  107. result[k] = prepareForSerialize(i)
  108. end
  109. else
  110. result = ob
  111. end
  112. return result
  113. end
  114.  
  115. function savePeripheralConfig()
  116. local file = fs.open(peripheralConfigPath,"w")
  117. local tempInventoryAddress = nil
  118. local outputInventoryAddress = nil
  119. local recipeInputInventoryAddress = nil
  120. local recipeInputInventorySlots = nil
  121. if tempInventory ~= nil then
  122. tempInventoryAddress = tempInventory.address
  123. end
  124. if outputInventory ~= nil then
  125. outputInventoryAddress = outputInventory.address
  126. end
  127. if recipeInputInventory ~= nil then
  128. recipeInputInventoryAddress = recipeInputInventory.address
  129. recipeInputInventorySlots = recipeInputInventory.craftingSlots
  130. end
  131. local data = {
  132. tempInventory=tempInventoryAddress,
  133. outputInventory=outputInventoryAddress,
  134. recipeInputInventory=recipeInputInventoryAddress,
  135. recipeInputInventorySlots=recipeInputInventorySlots
  136. }
  137. file.writeLine(textutils.serialize(data))
  138. file.close()
  139. end
  140.  
  141. function indexNetwork()
  142. local newIndex = {}
  143. for _,inv in pairs(inventories) do
  144. local invSize = peripheral.call(inv,"size")
  145. for slot = 1,invSize,1 do
  146. local data = peripheral.call(inv,"getItemMeta",slot)
  147. if data ~= nil then
  148. local indexName = getItemIndexName(data.name)
  149. if newIndex[indexName] == nil then
  150. newIndex[indexName] = {
  151. name=data.name,
  152. damageValues={}
  153. }
  154. end
  155. if newIndex[indexName].damageValues[data.damage] == nil then
  156. newIndex[indexName].damageValues[data.damage] = {
  157. displayName=data.displayName,
  158. locations={}
  159. }
  160. end
  161. table.insert(newIndex[indexName].damageValues[data.damage].locations,{
  162. address=inv,
  163. slot=slot
  164. })
  165. end
  166. end
  167. end
  168. indexedItems = newIndex
  169. end
  170.  
  171. function findTurtle()
  172. turtle = nil
  173. for _,p in pairs(peripheral.getNames()) do
  174. if peripheral.getType(p) == "turtle" then
  175. peripheral.call(p,"reboot")
  176. sleep(1)
  177. local perip = peripheral.wrap(p)
  178. if perip.getLabel() == "Ready For Crafting" then
  179. turtle = perip
  180. turtle.address = p
  181. return nil
  182. end
  183. end
  184. end
  185. end
  186.  
  187. function isPeripheralAnInventory(address)
  188. if not peripheral.isPresent(address) then
  189. return false
  190. end
  191. local perip = peripheral.wrap(address)
  192. if perip.pullItems and type(perip.pullItems) == "function" then
  193. return true
  194. end
  195. return false
  196. end
  197.  
  198. function setUpPeripherals()
  199. for _,p in pairs(peripheral.getNames()) do
  200. if peripheral.getType(p) == "modem" then
  201. rednet.open(p)
  202. end
  203. end
  204. local data = {}
  205. if fs.exists(peripheralConfigPath) then
  206. local file = fs.open(peripheralConfigPath,"r")
  207. data = textutils.unserialize(file.readAll())
  208. file.close()
  209. if data == nil then
  210. data = {}
  211. end
  212. end
  213. outputInventory = nil
  214. tempInventory = nil
  215. recipeInputInventory = nil
  216. inventories = {}
  217. if data.outputInventory ~= nil and isPeripheralAnInventory(data.outputInventory) then
  218. outputInventory = peripheral.wrap(data.outputInventory)
  219. outputInventory.address = data.outputInventory
  220. else
  221. if peripheral.isPresent("top") and isPeripheralAnInventory("top") then
  222. outputInventory = peripheral.wrap("top")
  223. outputInventory.address = "top"
  224. else
  225. term.clear()
  226. term.setCursorPos(1,1)
  227. term.setTextColor(titleColor)
  228. print("AutoCrafting.lua Configurator")
  229. while outputInventory == nil do
  230. term.setTextColor(promptColor)
  231. print("Please connect/reconnect an inventory to the network to serve as an output")
  232. local e,p = os.pullEvent("peripheral")
  233. if isPeripheralAnInventory(p) then
  234. outputInventory = peripheral.wrap(p)
  235. outputInventory.address = p
  236. else
  237. term.setTextColor(errorColor)
  238. print("Selected peripheral is not a valid inventory:")
  239. term.setTextColor(textColor)
  240. print("\""..p.."\"")
  241. print("")
  242. end
  243. end
  244. end
  245. end
  246. if data.tempInventory ~= nil and isPeripheralAnInventory(data.tempInventory) then
  247. tempInventory = peripheral.wrap(data.tempInventory)
  248. tempInventory.address = data.tempInventory
  249. else
  250. if peripheral.isPresent("bottom") and isPeripheralAnInventory("bottom") then
  251. tempInventory = peripheral.wrap("bottom")
  252. tempInventory.address = "bottom"
  253. else
  254. term.clear()
  255. term.setCursorPos(1,1)
  256. term.setTextColor(titleColor)
  257. print("AutoCrafting.lua Configurator")
  258. while tempInventory == nil do
  259. term.setTextColor(promptColor)
  260. print("Please connect/reconnect an inventory to the network to serve as a temporary inventory for storing intermediate products")
  261. local e,p = os.pullEvent("peripheral")
  262. if isPeripheralAnInventory(p) then
  263. tempInventory = peripheral.wrap(p)
  264. tempInventory.address = p
  265. else
  266. term.setTextColor(errorColor)
  267. print("Selected peripheral is not a valid inventory:")
  268. term.setTextColor(textColor)
  269. print("\""..p.."\"")
  270. print("")
  271. end
  272. end
  273. end
  274. end
  275. if data.recipeInputInventory ~= nil and isPeripheralAnInventory(data.recipeInputInventory) then
  276. recipeInputInventory = peripheral.wrap(data.recipeInputInventory)
  277. recipeInputInventory.address = data.recipeInputInventory
  278. else
  279. if peripheral.isPresent("left") and isPeripheralAnInventory("left") then
  280. recipeInputInventory = peripheral.wrap("left")
  281. recipeInputInventory.address = "left"
  282. else
  283. term.clear()
  284. term.setCursorPos(1,1)
  285. term.setTextColor(titleColor)
  286. print("AutoCrafting.lua Configurator")
  287. while recipeInputInventory == nil do
  288. term.setTextColor(promptColor)
  289. print("Please connect/reconnect an inventory to the network to serve as a recipe input")
  290. local e,p = os.pullEvent("peripheral")
  291. if isPeripheralAnInventory(p) then
  292. local perip = peripheral.wrap(p)
  293. if perip.size() >= 9 then
  294. recipeInputInventory = perip
  295. recipeInputInventory.address = p
  296. end
  297. end
  298. if recipeInputInventory == nil then
  299. term.setTextColor(errorColor)
  300. print("Selected peripheral is not a valid inventory:")
  301. term.setTextColor(textColor)
  302. print("\""..p.."\"")
  303. print("")
  304. end
  305. end
  306. end
  307. end
  308. if data.recipeInputInventorySlots ~= nil and type(data.recipeInputInventorySlots) == "table" and #data.recipeInputInventorySlots == 9 then
  309. recipeInputInventory.craftingSlots = {}
  310. for _,i in ipairs(data.recipeInputInventorySlots) do
  311. table.insert(recipeInputInventory.craftingSlots,i)
  312. end
  313. elseif recipeInputInventory.size() == 9 then
  314. recipeInputInventory.craftingSlots = {}
  315. for i=1,9,1 do
  316. table.insert(recipeInputInventory.craftingSlots,i)
  317. end
  318. else
  319. local slots = {}
  320. term.clear()
  321. term.setCursorPos(1,1)
  322. term.setTextColor(titleColor)
  323. print("AutoCrafting.lua Configurator")
  324. while #slots ~= 9 do
  325. slots = {}
  326. term.setTextColor(promptColor)
  327. print("Please put items in the 9 slots of the Recipe Input peripheral that you want to serve as the crafting grid")
  328. print("Press any key to continue")
  329. os.pullEvent("key")
  330. os.pullEvent("key_up")
  331. for i=1,recipeInputInventory.size(),1 do
  332. if recipeInputInventory.getItemMeta(i) ~= nil then
  333. table.insert(slots,i)
  334. end
  335. end
  336. if #slots ~= 9 then
  337. term.setTextColor(errorColor)
  338. if #slots < 9 then
  339. print("Too few slots were found with items in them")
  340. else
  341. print("Too many slots were found with items in them")
  342. end
  343. print("")
  344. end
  345. end
  346. recipeInputInventory.craftingSlots = slots
  347. end
  348.  
  349. for _,p in pairs(peripheral.getNames()) do
  350. if p ~= outputInventory.address and
  351. p ~= tempInventory.address and
  352. p ~= recipeInputInventory.address and
  353. isPeripheralAnInventory(p) then
  354. table.insert(inventories,p)
  355. end
  356. end
  357. savePeripheralConfig()
  358. end
  359.  
  360. if fs.exists(recipeConfigPath) then
  361. local file = fs.open(recipeConfigPath,"r")
  362. recipes = textutils.unserialize(file.readAll())
  363. file.close()
  364. if recipes == nil then
  365. resetRecipes()
  366. saveRecipeConfig()
  367. end
  368. else
  369. saveRecipeConfig()
  370. end
  371.  
  372. setUpPeripherals()
  373. while turtle == nil do
  374. findTurtle()
  375. if turtle == nil then
  376. sleep(1)
  377. end
  378. end
  379. indexNetwork()
  380.  
  381. local termWidth,termHeight = term.getSize()
  382.  
  383. local currentView = nil
  384. local windows = {}
  385.  
  386. local running = true
  387. local paused = false
  388.  
  389. function putItemInTurtle(sourceInventory,sourceSlot,quantity,turtleSlot)
  390. local slot = turtleSlot
  391. if slot >= 7 then
  392. slot = slot + 2
  393. elseif slot >= 4 then
  394. slot = slot + 1
  395. end
  396. if type(sourceInventory) == "table" then
  397. return sourceInventory.pushItems(turtle.address,sourceSlot,quantity,slot)
  398. else
  399. return peripheral.call(sourceInventory,"pushItems",turtle.address,sourceSlot,quantity,slot)
  400. end
  401. end
  402. function pullItemFromTurtle(toInventory,turtleSlot,quantity,toSlot)
  403. local slot = turtleSlot
  404. if slot >= 7 then
  405. slot = slot + 2
  406. elseif slot >= 4 then
  407. slot = slot + 1
  408. end
  409. if type(toInventory) == "table" then
  410. return toInventory.pullItems(turtle.address,slot,quantity,toSlot)
  411. else
  412. return peripheral.call(toInventory,"pullItems",turtle.address,slot,quantity,toSlot)
  413. end
  414. end
  415.  
  416. function addRecipe()
  417. paused = true
  418. rednet.receive("paused")
  419.  
  420. local recipe = {}
  421. term.setCursorPos(1,4)
  422. term.setTextColor(colors.white)
  423. term.setBackgroundColor(colors.black)
  424. for i=1,9,1 do
  425. local meta = recipeInputInventory.getItemMeta(recipeInputInventory.craftingSlots[i])
  426. if meta ~= nil then
  427. local indexName = getItemIndexName(meta.name)
  428. if recipe[indexName] == nil then
  429. recipe[indexName] = {
  430. slots={},
  431. displayName=meta.displayName,
  432. name=meta.name,
  433. damage=meta.damage
  434. }
  435. end
  436. table.insert(recipe[indexName].slots,i)
  437. putItemInTurtle(recipeInputInventory,recipeInputInventory.craftingSlots[i],1,i)
  438. print(meta.displayName)
  439. end
  440. end
  441. rednet.send(turtle.getID(),turtle.getID(),"craft")
  442. print("sent command")
  443. local returnValue = true
  444. while true do
  445. local senderId,message,protocol = rednet.receive()
  446. if protocol == "craft_done" then
  447. print("received message")
  448. if message ~= nil then
  449. print("crafting successful")
  450. local qty = recipeInputInventory.pullItems(turtle.address,16,64,recipeInputInventory.craftingSlots[5])
  451. local meta = recipeInputInventory.getItemMeta(recipeInputInventory.craftingSlots[5])
  452. local resultIndex = getItemIndexName(meta.name)
  453. if recipes[resultIndex] == nil then
  454. recipes[resultIndex] = {
  455. name=meta.name,
  456. damageValues={}
  457. }
  458. end
  459. if recipes[resultIndex].damageValues[meta.damage] == nil then
  460. recipes[resultIndex].damageValues[meta.damage] = {
  461. displayName=meta.displayName,
  462. recipes={}
  463. }
  464. end
  465. table.insert(recipes[resultIndex].damageValues[meta.damage].recipes,{
  466. resultQty=qty,
  467. recipe=recipe
  468. })
  469. saveRecipeConfig()
  470. returnValue = true
  471. else
  472. print("crafting failed")
  473. for i=1,9,1 do
  474. pullItemFromTurtle(recipeInputInventory,i,64,recipeInputInventory.craftingSlots[i])
  475. end
  476. returnValue = false
  477. end
  478. break
  479. end
  480. end
  481. paused = false
  482. rednet.send(os.getComputerID(),"","continue")
  483. return returnValue
  484. end
  485.  
  486. function switchView(view)
  487. if windows[view] ~= nil then
  488. if currentView ~= nil then
  489. windows[currentView].setVisible(false)
  490. end
  491. windows[view].setVisible(true)
  492. currentView = view
  493. end
  494. end
  495.  
  496. function createView(name,data)
  497. local win = window.create(term.current(),1,1,termWidth,termHeight,false)
  498. win.name = name
  499. win.extraRender = data.render
  500. win.extraOnload = data.onload
  501. win.buttons = {}
  502. win.panels = {}
  503.  
  504. win.onload = function(win)
  505. win.extraOnload(win)
  506. win.render(win)
  507. end
  508. win.render = function(win)
  509. win.extraRender(win)
  510. for _,p in pairs(win.panels) do
  511. p.render(win,p)
  512. end
  513. for _,b in pairs(win.buttons) do
  514. b.render(win,b)
  515. end
  516. end
  517.  
  518. for k,v in ipairs(data) do
  519. if k ~= "render" and k ~= "onload" then
  520. if win[k] == nil then
  521. win[k] = v
  522. end
  523. end
  524. end
  525.  
  526. for k,b in pairs(data.buttons) do
  527. local w = string.len(b.text)
  528. if b.width ~= nil then
  529. w = b.width
  530. end
  531. local button = window.create(win,b.x,b.y,w,1,true)
  532. button.text = b.text
  533. if b.render ~= nil then
  534. button.render = b.render
  535. else
  536. button.render = function(win,button)
  537. button.setCursorPos(1,1)
  538. button.setTextColor(buttonTextColor)
  539. button.setBackgroundColor(buttonBackgroundColor)
  540. button.write(button.text)
  541. end
  542. end
  543. button.onclick = b.onclick
  544. win.buttons[k] = button
  545. end
  546.  
  547. win.render(win)
  548.  
  549. windows[name] = win
  550. end
  551.  
  552. function createWindows()
  553. createView("main",{
  554. render=function(win)
  555. win.setBackgroundColor(backgroundColor)
  556. win.clear()
  557. win.setCursorPos(1,1)
  558. win.setBackgroundColor(colors.gray)
  559. win.setTextColor(colors.black)
  560. win.clearLine(1)
  561. win.write("AutoCrafting - Main Menu")
  562. end,
  563. buttons={
  564. closeButton={
  565. text="Close",
  566. x=termWidth-4,
  567. y=1,
  568. onclick=function(win) running = false end
  569. },
  570. addRecipeButton={
  571. text="Add Recipe",
  572. x=2,
  573. y=3,
  574. onclick=function(win) switchView("add_recipe") end
  575. }
  576. }
  577. })
  578. createView("add_recipe",{
  579. render = function(win)
  580. win.setBackgroundColor(backgroundColor)
  581. win.setTextColor(promptColor)
  582. win.clear()
  583. win.setCursorPos(2,3)
  584. print("Please use the Recipe Input device to specify the new recipe")
  585. win.setCursorPos(1,1)
  586. win.setBackgroundColor(colors.gray)
  587. win.setTextColor(colors.black)
  588. win.clearLine(1)
  589. win.write("AutoCrafting - Add New Recipe")
  590. end,
  591. buttons={
  592. cancelButton={
  593. text="Cancel",
  594. x=termWidth-5,
  595. y=1,
  596. onclick=function(win) switchView("main") end
  597. },
  598. submitButton={
  599. text="Submit",
  600. x=termWidth-6,
  601. y=termHeight-1,
  602. onclick=function(win)
  603. addRecipe()
  604. switchView("main")
  605. end
  606. }
  607. }
  608. })
  609. end
  610.  
  611. function main()
  612. createWindows()
  613. switchView("main")
  614. while running do
  615. local e,p1,p2,p3,p4,p5 = os.pullEvent()
  616. if e == "mouse_click" then
  617. local mouseButton,x,y = p1,p2,p3
  618. term.setCursorPos(1,4)
  619. term.setTextColor(colors.white)
  620. term.setBackgroundColor(colors.black)
  621. for _,b in pairs(windows[currentView].buttons) do
  622. local bx,by = b.getPosition()
  623. local bw,bh = b.getSize()
  624. if x >= bx and y >= by and x < bx+bw and y < by+bh then
  625. b.onclick()
  626. break
  627. end
  628. end
  629. end
  630. end
  631. rednet.send(turtle.getID(),"","terminate_autocraft")
  632. term.setCursorPos(1,1)
  633. term.setTextColor(colors.white)
  634. term.setBackgroundColor(colors.black)
  635. term.clear()
  636. end
  637.  
  638. function processQueue()
  639. while running do
  640. if #orderQueue > 0 then
  641. indexNetwork()
  642. local completedOrders = {}
  643. for _,i in pairs(orderQueue) do
  644. if paused then
  645. rednet.send(os.getComputerID(),"","paused")
  646. rednet.receive("continue")
  647. end
  648. end
  649. else
  650. if paused then
  651. rednet.send(os.getComputerID(),"","paused")
  652. rednet.receive("continue")
  653. end
  654. sleep(1)
  655. end
  656. end
  657. end
  658.  
  659. parallel.waitForAll(main,processQueue)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement