Advertisement
Zaroph

Minecolonies RF AP CC Script AUG23

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