Advertisement
Kohjen

Mineolonies and AE2 test

Aug 25th, 2024 (edited)
265
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- Original author: Scott Adkins <[email protected]> (Zucanthor)
  2. --
  3. -- This program monitors work requests for the Minecolonies Warehouse and
  4. -- tries to fulfill requests from the Applied Energistics 2 network. If the
  5. -- AE2 network doesn't have enough items and a crafting pattern exists, a
  6. -- crafting job is scheduled to restock the items in order to fulfill the
  7. -- work request.  The script will continuously loop, monitoring for new
  8. -- requests and checking on crafting jobs to fulfill previous requests.
  9.  
  10. -- The following is required for setup:
  11. --   * 1 ComputerCraft Computer
  12. --   * 1 or more ComputerCraft Monitors (recommend 3x3 advanced monitors)
  13. --   * 1 Advanced Peripheral Colony Integrator
  14. --   * 1 Advanced Peripheral AE2 Bridge
  15. --   * 1 Chest or other storage container
  16. -- Attach an AE2 Cable from the AE2 network to the AE2 Bridge. Connect the
  17. -- storage container to the Minecolonies Warehouse Hut block directly or
  18. -- to an ender chest and then pipe ender chest contents out to the warehouse
  19. -- racks. Latter makes it easier to upgrade warehouse.
  20.  
  21. -- THINGS YOU CAN CUSTOMIZE IN THIS PROGRAM:
  22. -- Line 56: Specify the side storage container is at.
  23. -- Line 66: Name of log file for storing JSON data of all open requests.
  24. -- Lines 231+: Any items you find that should be manually provided.
  25. -- Line 373: Time in seconds between work order scans.
  26.  
  27. ----------------------------------------------------------------------------
  28. -- INITIALIZATION
  29. ----------------------------------------------------------------------------
  30.  
  31. -- Initialize Monitor
  32. -- A future update may allow for multiple monitors. This would allow one
  33. -- monitor to be used for logging and another to be used for work requests.
  34. local monitor = peripheral.find("monitor")
  35. if not monitor then error("Monitor not found.") end
  36. monitor.setTextScale(0.5)
  37. monitor.clear()
  38. monitor.setCursorPos(1, 1)
  39. monitor.setCursorBlink(false)
  40. print("Monitor initialized.")
  41.  
  42. -- Initialize ME Bridge
  43. local bridge = peripheral.find("meBridge")
  44. if not bridge then error("ME Bridge not found.") end
  45. print("ME Bridge initialized.")
  46.  
  47. -- Initialize Colony Integrator
  48. local colony = peripheral.find("colonyIntegrator")
  49. if not colony then error("Colony Integrator not found.") end
  50. if not colony.isInColony then error("Colony Integrator is not in a colony.") end
  51. print("Colony Integrator initialized.")
  52.  
  53. -- Point to location of chest or storage container
  54. -- A future update may autodetect where the storage container is and error
  55. -- out if no storage container is found.
  56. --local storage = "left"
  57. --print("Storage initialized.")
  58.  
  59. -- Name of log file to capture JSON data from the open requests.  The log can
  60. -- be too big to edit within CC, which may require a "pastebin put" if you want
  61. -- to look at it.  Logging could be improved to only capture Skipped items,
  62. -- which in turn will make log files smaller and edittable in CC directly.
  63. local logFile = "RSWarehouse.log"
  64.  
  65. ----------------------------------------------------------------------------
  66. -- FUNCTIONS
  67. ----------------------------------------------------------------------------
  68.  
  69. -- Prints to the screen one row after another, scrolling the screen when
  70. -- reaching the bottom. Acts as a normal display where text is printed in
  71. -- a standard way. Long lines are not wrapped and newlines are printed as
  72. -- spaces, both to be addressed in a future update.
  73. -- NOTE: No longer used in this program.
  74. function mPrintScrollable(mon, ...)
  75.     w, h = mon.getSize()
  76.     x, y = mon.getCursorPos()
  77.  
  78.     -- Blink the cursor like a normal display.
  79.     mon.setCursorBlink(true)
  80.  
  81.     -- For multiple strings, append them with a space between each.
  82.     for i = 2, #arg do t = t.." "..arg[i] end
  83.     mon.write(arg[1])
  84.     if y >= h then
  85.         mon.scroll(1)
  86.         mon.setCursorPos(1, y)
  87.     else
  88.         mon.setCursorPos(1, y+1)
  89.     end
  90. end
  91.  
  92. -- Prints strings left, centered, or right justified at a specific row and
  93. -- specific foreground/background color.
  94. function mPrintRowJustified(mon, y, pos, text, ...)
  95.     w, h = mon.getSize()
  96.     fg = mon.getTextColor()
  97.     bg = mon.getBackgroundColor()
  98.  
  99.     if pos == "left" then x = 1 end
  100.     if pos == "center" then x = math.floor((w - #text) / 2) end
  101.     if pos == "right" then x = w - #text end
  102.  
  103.     if #arg > 0 then mon.setTextColor(arg[1]) end
  104.     if #arg > 1 then mon.setBackgroundColor(arg[2]) end
  105.     mon.setCursorPos(x, y)
  106.     mon.write(text)
  107.     mon.setTextColor(fg)
  108.     mon.setBackgroundColor(bg)
  109. end
  110.  
  111. -- Utility function that returns true if the provided character is a digit.
  112. -- Yes, this is a hack and there are better ways to do this.  Clearly.
  113. function isdigit(c)
  114.     if c == "0" then return true end
  115.     if c == "1" then return true end
  116.     if c == "2" then return true end
  117.     if c == "3" then return true end
  118.     if c == "4" then return true end
  119.     if c == "5" then return true end
  120.     if c == "6" then return true end
  121.     if c == "7" then return true end
  122.     if c == "8" then return true end
  123.     if c == "9" then return true end
  124.     return false
  125. end
  126.  
  127. -- Utility function that displays current time and remaining time on timer.
  128. -- For time of day, yellow is day, orange is sunset/sunrise, and red is night.
  129. -- The countdown timer is orange over 15s, yellow under 15s, and red under 5s.
  130. -- At night, the countdown timer is red and shows PAUSED insted of a time.
  131. function displayTimer(mon, t)
  132.     now = os.time()
  133.  
  134.     cycle = "day"
  135.     cycle_color = colors.orange
  136.     if now >= 4 and now < 6 then
  137.         cycle = "sunrise"
  138.         cycle_color = colors.orange
  139.     elseif now >= 6 and now < 18 then
  140.         cycle = "day"
  141.         cycle_color = colors.yellow
  142.     elseif now >= 18 and now < 19.5 then
  143.         cycle = "sunset"
  144.         cycle_color = colors.orange
  145.     elseif now >= 19.5 or now < 5 then
  146.         cycle = "night"
  147.         cycle_color = colors.red
  148.     end
  149.  
  150.     timer_color = colors.orange
  151.     if t < 15 then timer_color = colors.yellow end
  152.     if t < 5 then timer_color = colors.red end
  153.  
  154.     mPrintRowJustified(mon, 1, "left", string.format("Time: %s [%s]    ", textutils.formatTime(now, false), cycle), cycle_color)
  155.     if cycle ~= "night" then mPrintRowJustified(mon, 1, "right", string.format("    Remaining: %ss", t), timer_color)
  156.     else mPrintRowJustified(mon, 1, "right", "    Remaining: PAUSED", colors.red) end
  157. end
  158.  
  159. -- Scan all open work requests from the Warehouse and attempt to satisfy those
  160. -- requests.  Display all activity on the monitor, including time of day and the
  161. -- countdown timer before next scan.  This function is not called at night to
  162. -- save on some ticks, as the colonists are in bed anyways.  Items in red mean
  163. -- work order can't be satisfied by Refined Storage (lack of pattern or lack of
  164. -- required crafting ingredients).  Yellow means order partially filled and a
  165. -- crafting job was scheduled for the rest.  Green means order fully filled.
  166. -- Blue means the Player needs to manually fill the work order.  This includes
  167. -- equipment (Tools of Class), NBT items like armor, weapons and tools, as well
  168. -- as generic requests ike Compostables, Fuel, Food, Flowers, etc.
  169. function scanWorkRequests(mon, rs, chest)
  170.     -- Before we do anything, prep the log file for this scan.
  171.     -- The log file is truncated each time this function is called.
  172.     file = fs.open(logFile, "w")
  173.     print("\nScan starting at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
  174.  
  175.     -- We want to keep three different lists so that they can be
  176.     -- displayed on the monitor in a more intelligent way.  The first
  177.     -- list is for the Builder requests.  The second list is for the
  178.     -- non-Builder requests.  The third list is for any armor, tools
  179.     -- and weapons requested by the colonists.
  180.     builder_list = {}
  181.     nonbuilder_list = {}
  182.     equipment_list = {}
  183.  
  184.     -- Scan RS for all items in its network. Ignore items with NBT data.
  185.     -- If a Builder needs any items with NBT data, this function will need
  186.     -- to be updated to not ignore those items.
  187.     items = bridge.listItems()
  188.     item_array = {}
  189.     for index, item in ipairs(items) do
  190.         if not item.nbt then
  191.             item_array[item.name] = item.amount
  192.         end
  193.     end
  194.  
  195.     -- Scan the Warehouse for all open work requests. For each item, try to
  196.     -- provide as much as possible from RS, then craft whatever is needed
  197.     -- after that. Green means item was provided entirely. Yellow means item
  198.     -- is being crafted. Red means item is missing crafting recipe.
  199.     workRequests = colony.getRequests()
  200.     -- file.write(textutils.serialize(workRequests))
  201.     for w in pairs(workRequests) do
  202.         name = workRequests[w].name
  203.         item = workRequests[w].items[1].name
  204.         target = workRequests[w].target
  205.         desc = workRequests[w].desc
  206.         needed = workRequests[w].count
  207.         provided = 0
  208.  
  209.         target_words = {}
  210.         target_length = 0
  211.         for word in target:gmatch("%S+") do
  212.             table.insert(target_words, word)
  213.             target_length = target_length + 1
  214.         end
  215.  
  216.         if target_length >= 3 then target_name = target_words[target_length-2] .. " " .. target_words[target_length]
  217.         else target_name = target end
  218.  
  219.         target_type = ""
  220.         target_count = 1
  221.         repeat
  222.             if target_type ~= "" then target_type = target_type .. " " end
  223.             target_type = target_type .. target_words[target_count]
  224.             target_count = target_count + 1
  225.         until target_count > target_length - 3
  226.  
  227.         useRS = 1
  228.         if string.find(desc, "Tool of class") then useRS = 0 end
  229.         if string.find(name, "Hoe") then useRS = 0 end
  230.         if string.find(name, "Shovel") then useRS = 0 end
  231.         if string.find(name, "Axe") then useRS = 0 end
  232.         if string.find(name, "Pickaxe") then useRS = 0 end
  233.         if string.find(name, "Bow") then useRS = 0 end
  234.         if string.find(name, "Sword") then useRS = 0 end
  235.         if string.find(name, "Shield") then useRS = 0 end
  236.         if string.find(name, "Helmet") then useRS = 0 end
  237.         if string.find(name, "Leather Cap") then useRS = 0 end
  238.         if string.find(name, "Chestplate") then useRS = 0 end
  239.         if string.find(name, "Tunic") then useRS = 0 end
  240.         if string.find(name, "Pants") then useRS = 0 end
  241.         if string.find(name, "Leggings") then useRS = 0 end
  242.         if string.find(name, "Boots") then useRS = 0 end
  243.         if name == "Rallying Banner" then useRS = 0 end --bugged in alpha versions
  244.         if name == "Crafter" then useRS = 0 end
  245.         if name == "Compostable" then useRS = 0 end
  246.         if name == "Fertilizer" then useRS = 0 end
  247.         if name == "Flowers" then useRS = 0 end
  248.         if name == "Food" then useRS = 0 end
  249.         if name == "Fuel" then useRS = 0 end
  250.         if name == "Smeltable Ore" then useRS = 0 end
  251.         if name == "Stack List" then useRS = 0 end
  252.  
  253.         color = colors.blue
  254.         if useRS == 1 then
  255.             if item_array[item] then
  256.                 print(item)
  257.                 print(needed)
  258.                 provided = bridge.exportItem({name=item, count=needed}, "west")
  259.             end
  260.  
  261.             color = colors.green
  262.             if provided < needed then
  263.                 if bridge.isItemCrafting( { name = item } ) then
  264.                     color = colors.yellow
  265.                     print("[Crafting]", item)
  266.                 else
  267.                     if bridge.craftItem({name=item, count=needed}) then
  268.                         color = colors.yellow
  269.                         print("[Scheduled]", needed, "x", item)
  270.                     else
  271.                         color = colors.red
  272.                         print("[Failed]", item)
  273.                     end
  274.                 end
  275.             end
  276.         else
  277.             nameString = name .. " [" .. target .. "]"
  278.             print("[Skipped]", nameString)
  279.         end
  280.  
  281.         if string.find(desc, "of class") then
  282.             level = "Any Level"
  283.             if string.find(desc, "with maximal level:Leather") then level = "Leather" end
  284.             if string.find(desc, "with maximal level:Gold") then level = "Gold" end
  285.             if string.find(desc, "with maximal level:Chain") then level = "Chain" end
  286.             if string.find(desc, "with maximal level:Wood or Gold") then level = "Wood or Gold" end
  287.             if string.find(desc, "with maximal level:Stone") then level = "Stone" end
  288.             if string.find(desc, "with maximal level:Iron") then level = "Iron" end
  289.             if string.find(desc, "with maximal level:Diamond") then level = "Diamond" end
  290.             new_name = level .. " " .. name
  291.             if level == "Any Level" then new_name = name .. " of any level" end
  292.             new_target = target_type .. " " .. target_name
  293.             equipment = { name=new_name, target=new_target, needed=needed, provided=provided, color=color}
  294.             table.insert(equipment_list, equipment)
  295.         elseif string.find(target, "Builder") then
  296.             builder = { name=name, item=item, target=target_name, needed=needed, provided=provided, color=color }
  297.             table.insert(builder_list, builder)
  298.         else
  299.             new_target = target_type .. " " .. target_name
  300.             if target_length < 3 then
  301.                 new_target = target
  302.             end
  303.             nonbuilder = { name=name, target=new_target, needed=needed, provided=provided, color=color }
  304.             table.insert(nonbuilder_list, nonbuilder)
  305.         end
  306.     end
  307.  
  308.     -- Show the various lists on the attached monitor.
  309.     row = 3
  310.     mon.clear()
  311.  
  312.     header_shown = 0
  313.     for e in pairs(equipment_list) do
  314.         equipment = equipment_list[e]
  315.         if header_shown == 0 then
  316.             mPrintRowJustified(mon, row, "center", "Equipment")
  317.             header_shown = 1
  318.             row = row + 1
  319.         end
  320.         text = string.format("%d %s", equipment.needed, equipment.name)
  321.         mPrintRowJustified(mon, row, "left", text, equipment.color)
  322.         mPrintRowJustified(mon, row, "right", " " .. equipment.target, equipment.color)
  323.         row = row + 1
  324.     end
  325.  
  326.     header_shown = 0
  327.     for b in pairs(builder_list) do
  328.         builder = builder_list[b]
  329.         if header_shown == 0 then
  330.             if row > 1 then row = row + 1 end
  331.             mPrintRowJustified(mon, row, "center", "Builder Requests")
  332.             header_shown = 1
  333.             row = row + 1
  334.         end
  335.         text = string.format("%d/%s", builder.provided, builder.name)
  336.         mPrintRowJustified(mon, row, "left", text, builder.color)
  337.         mPrintRowJustified(mon, row, "right", " " .. builder.target, builder.color)
  338.         row = row + 1
  339.     end
  340.  
  341.     header_shown = 0
  342.     for n in pairs(nonbuilder_list) do
  343.         nonbuilder = nonbuilder_list[n]
  344.         if header_shown == 0 then
  345.             if row > 1 then row = row + 1 end
  346.             mPrintRowJustified(mon, row, "center", "Nonbuilder Requests")
  347.             header_shown = 1
  348.             row = row + 1
  349.         end
  350.         text = string.format("%d %s", nonbuilder.needed, nonbuilder.name)
  351.         if isdigit(nonbuilder.name:sub(1,1)) then
  352.             text = string.format("%d/%s", nonbuilder.provided, nonbuilder.name)
  353.         end
  354.         mPrintRowJustified(mon, row, "left", text, nonbuilder.color)
  355.         mPrintRowJustified(mon, row, "right", " " .. nonbuilder.target, nonbuilder.color)
  356.         row = row + 1
  357.     end
  358.  
  359.     if row == 3 then mPrintRowJustified(mon, row, "center", "No Open Requests") end
  360.     print("Scan completed at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
  361.     file.close()
  362. end
  363.  
  364. ----------------------------------------------------------------------------
  365. -- MAIN
  366. ----------------------------------------------------------------------------
  367.  
  368. -- Scan for requests periodically. This will catch any updates that were
  369. -- triggered from the previous scan. Right-clicking on the monitor will
  370. -- trigger an immediate scan and reset the timer. Unfortunately, there is
  371. -- no way to capture left-clicks on the monitor.
  372. local time_between_runs = 30
  373. local current_run = time_between_runs
  374. scanWorkRequests(monitor, bridge, storage)
  375. displayTimer(monitor, current_run)
  376. local TIMER = os.startTimer(1)
  377.  
  378. while true do
  379.     local e = {os.pullEvent()}
  380.     if e[1] == "timer" and e[2] == TIMER then
  381.         now = os.time()
  382.         if now >= 5 and now < 19.5 then
  383.             current_run = current_run - 1
  384.             if current_run <= 0 then
  385.                 scanWorkRequests(monitor, bridge, storage)
  386.                 current_run = time_between_runs
  387.             end
  388.         end
  389.         displayTimer(monitor, current_run)
  390.         TIMER = os.startTimer(1)
  391.     elseif e[1] == "monitor_touch" then
  392.         os.cancelTimer(TIMER)
  393.         scanWorkRequests(monitor, bridge, storage)
  394.         current_run = time_between_runs
  395.         displayTimer(monitor, current_run)
  396.         TIMER = os.startTimer(1)
  397.     end
  398. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement