Last active
February 6, 2025 19:45
-
-
Save Homer199090/03a5ac19f0a97381e1b3e3b5e295d3f9 to your computer and use it in GitHub Desktop.
Lager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- RSWarehouse.lua | |
-- Author: Scott Adkins <adkinss@gmail.com> (Zucanthor) | |
-- Published: 2021-09-21 | |
-- | |
-- This program monitors work requests for the Minecolonies Warehouse and | |
-- tries to fulfill requests from the Refined Storage network. If the | |
-- RS network doesn't have enough items and a crafting pattern exists, a | |
-- crafting job is scheduled to restock the items in order to fulfill the | |
-- work request. The script will continuously loop, monitoring for new | |
-- requests and checking on crafting jobs to fulfill previous requests. | |
-- The following is required for setup: | |
-- * 1 ComputerCraft Computer | |
-- * 1 or more ComputerCraft Monitors (recommend 3x3 monitors) | |
-- * 1 Advanced Peripheral Colony Integrator | |
-- * 1 Advanced Peripheral RS Bridge | |
-- * 1 Chest or other storage container | |
-- Attach an RS Cable from the RS network to the RS Bridge. Connect the | |
-- storage container to the Minecolonies Warehouse Hut block. One idea is | |
-- to set up a second RS network attached to the Warehouse Hut using an | |
-- External Storage connector and then attach an Importer for that network | |
-- to the storage container. | |
-- THINGS YOU CAN CUSTOMIZE IN THIS PROGRAM: | |
-- Line 59: Specify the side storage container is at. | |
-- Line 66: Name of log file for storing JSON data of all open requests. | |
-- Lines 231+: Any items you find that should be manually provided. | |
-- Line 373: Time in seconds between work order scans. | |
---------------------------------------------------------------------------- | |
-- INITIALIZATION | |
---------------------------------------------------------------------------- | |
-- Initialize Monitor | |
-- A future update may allow for multiple monitors. This would allow one | |
-- monitor to be used for logging and another to be used for work requests. | |
local monitor = peripheral.find("monitor") | |
if not monitor then error("Monitor not found.") end | |
monitor.setTextScale(0.5) | |
monitor.clear() | |
monitor.setCursorPos(1, 1) | |
monitor.setCursorBlink(false) | |
print("Monitor initialized.") | |
-- Initialize RS Bridge | |
local bridge = peripheral.find("rsBridge") | |
if not bridge then error("RS Bridge not found.") end | |
print("RS Bridge initialized.") | |
-- Point to location of chest or storage container | |
-- A future update may autodetect where the storage container is and error | |
-- out if no storage container is found. | |
local storage = "left" | |
print("Storage initialized.") | |
-- Name of log file to capture JSON data from the open requests. The log can | |
-- be too big to edit within CC, which may require a "pastebin put" if you want | |
-- to look at it. Logging could be improved to only capture Skipped items, | |
-- which in turn will make log files smaller and edittable in CC directly. | |
local logFile = "RSWarehouse.log" | |
---------------------------------------------------------------------------- | |
-- FUNCTIONS | |
---------------------------------------------------------------------------- | |
-- Prints to the screen one row after another, scrolling the screen when | |
-- reaching the bottom. Acts as a normal display where text is printed in | |
-- a standard way. Long lines are not wrapped and newlines are printed as | |
-- spaces, both to be addressed in a future update. | |
-- NOTE: No longer used in this program. | |
function mPrintScrollable(mon, ...) | |
w, h = mon.getSize() | |
x, y = mon.getCursorPos() | |
-- Blink the cursor like a normal display. | |
mon.setCursorBlink(true) | |
-- For multiple strings, append them with a space between each. | |
for i = 2, #arg do t = t.." "..arg[i] end | |
mon.write(arg[1]) | |
if y >= h then | |
mon.scroll(1) | |
mon.setCursorPos(1, y) | |
else | |
mon.setCursorPos(1, y+1) | |
end | |
end | |
-- Prints strings left, centered, or right justified at a specific row and | |
-- specific foreground/background color. | |
function mPrintRowJustified(mon, y, pos, text, ...) | |
w, h = mon.getSize() | |
fg = mon.getTextColor() | |
bg = mon.getBackgroundColor() | |
if pos == "left" then x = 1 end | |
if pos == "center" then x = math.floor((w - #text) / 2) end | |
if pos == "right" then x = w - #text end | |
if #arg > 0 then mon.setTextColor(arg[1]) end | |
if #arg > 1 then mon.setBackgroundColor(arg[2]) end | |
mon.setCursorPos(x, y) | |
mon.write(text) | |
mon.setTextColor(fg) | |
mon.setBackgroundColor(bg) | |
end | |
-- Utility function that returns true if the provided character is a digit. | |
-- Yes, this is a hack and there are better ways to do this. Clearly. | |
function isdigit(c) | |
if c == "0" then return true end | |
if c == "1" then return true end | |
if c == "2" then return true end | |
if c == "3" then return true end | |
if c == "4" then return true end | |
if c == "5" then return true end | |
if c == "6" then return true end | |
if c == "7" then return true end | |
if c == "8" then return true end | |
if c == "9" then return true end | |
return false | |
end | |
-- Utility function that displays current time and remaining time on timer. | |
-- For time of day, yellow is day, orange is sunset/sunrise, and red is night. | |
-- The countdown timer is orange over 15s, yellow under 15s, and red under 5s. | |
-- At night, the countdown timer is red and shows PAUSED insted of a time. | |
function displayTimer(mon, t) | |
now = os.time() | |
cycle = "day" | |
cycle_color = colors.orange | |
if now >= 4 and now < 6 then | |
cycle = "sunrise" | |
cycle_color = colors.orange | |
elseif now >= 6 and now < 18 then | |
cycle = "day" | |
cycle_color = colors.yellow | |
elseif now >= 18 and now < 19.5 then | |
cycle = "sunset" | |
cycle_color = colors.orange | |
elseif now >= 19.5 or now < 5 then | |
cycle = "night" | |
cycle_color = colors.red | |
end | |
timer_color = colors.orange | |
if t < 15 then timer_color = colors.yellow end | |
if t < 5 then timer_color = colors.red end | |
mPrintRowJustified(mon, 1, "left", string.format("Time: %s [%s] ", textutils.formatTime(now, false), cycle), cycle_color) | |
if cycle ~= "night" then mPrintRowJustified(mon, 1, "right", string.format(" Remaining: %ss", t), timer_color) | |
else mPrintRowJustified(mon, 1, "right", " Remaining: PAUSED", colors.red) end | |
end | |
-- Scan all open work requests from the Warehouse and attempt to satisfy those | |
-- requests. Display all activity on the monitor, including time of day and the | |
-- countdown timer before next scan. This function is not called at night to | |
-- save on some ticks, as the colonists are in bed anyways. Items in red mean | |
-- work order can't be satisfied by Refined Storage (lack of pattern or lack of | |
-- required crafting ingredients). Yellow means order partially filled and a | |
-- crafting job was scheduled for the rest. Green means order fully filled. | |
-- Blue means the Player needs to manually fill the work order. This includes | |
-- equipment (Tools of Class), NBT items like armor, weapons and tools, as well | |
-- as generic requests ike Compostables, Fuel, Food, Flowers, etc. | |
function scanWorkRequests(mon, rs, chest) | |
-- Before we do anything, prep the log file for this scan. | |
-- The log file is truncated each time this function is called. | |
file = fs.open(logFile, "w") | |
print("\nScan starting at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").") | |
-- We want to keep three different lists so that they can be | |
-- displayed on the monitor in a more intelligent way. The first | |
-- list is for the Builder requests. The second list is for the | |
-- non-Builder requests. The third list is for any armor, tools | |
-- and weapons requested by the colonists. | |
builder_list = {} | |
nonbuilder_list = {} | |
equipment_list = {} | |
-- Scan RS for all items in its network. Ignore items with NBT data. | |
-- If a Builder needs any items with NBT data, this function will need | |
-- to be updated to not ignore those items. | |
items = rs.listItems() | |
item_array = {} | |
for index, item in ipairs(items) do | |
if not item.nbt then | |
item_array[item.name] = item.amount | |
end | |
end | |
-- Scan the Warehouse for all open work requests. For each item, try to | |
-- provide as much as possible from RS, then craft whatever is needed | |
-- after that. Green means item was provided entirely. Yellow means item | |
-- is being crafted. Red means item is missing crafting recipe. | |
-- Show the various lists on the attached monitor. | |
row = 3 | |
mon.clear() | |
header_shown = 0 | |
for e in pairs(equipment_list) do | |
equipment = equipment_list[e] | |
if header_shown == 0 then | |
mPrintRowJustified(mon, row, "center", "Equipment") | |
header_shown = 1 | |
row = row + 1 | |
end | |
text = string.format("%d %s", equipment.needed, equipment.name) | |
mPrintRowJustified(mon, row, "left", text, equipment.color) | |
mPrintRowJustified(mon, row, "right", " " .. equipment.target, equipment.color) | |
row = row + 1 | |
end | |
header_shown = 0 | |
for b in pairs(builder_list) do | |
builder = builder_list[b] | |
if header_shown == 0 then | |
if row > 1 then row = row + 1 end | |
mPrintRowJustified(mon, row, "center", "Builder Requests") | |
header_shown = 1 | |
row = row + 1 | |
end | |
text = string.format("%d/%s", builder.provided, builder.name) | |
mPrintRowJustified(mon, row, "left", text, builder.color) | |
mPrintRowJustified(mon, row, "right", " " .. builder.target, builder.color) | |
row = row + 1 | |
end | |
header_shown = 0 | |
for n in pairs(nonbuilder_list) do | |
nonbuilder = nonbuilder_list[n] | |
if header_shown == 0 then | |
if row > 1 then row = row + 1 end | |
mPrintRowJustified(mon, row, "center", "Nonbuilder Requests") | |
header_shown = 1 | |
row = row + 1 | |
end | |
text = string.format("%d %s", nonbuilder.needed, nonbuilder.name) | |
if isdigit(nonbuilder.name:sub(1,1)) then | |
text = string.format("%d/%s", nonbuilder.provided, nonbuilder.name) | |
end | |
mPrintRowJustified(mon, row, "left", text, nonbuilder.color) | |
mPrintRowJustified(mon, row, "right", " " .. nonbuilder.target, nonbuilder.color) | |
row = row + 1 | |
end | |
if row == 3 then mPrintRowJustified(mon, row, "center", "No Open Requests") end | |
print("Scan completed at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").") | |
file.close() | |
end | |
---------------------------------------------------------------------------- | |
-- MAIN | |
---------------------------------------------------------------------------- | |
-- Scan for requests periodically. This will catch any updates that were | |
-- triggered from the previous scan. Right-clicking on the monitor will | |
-- trigger an immediate scan and reset the timer. Unfortunately, there is | |
-- no way to capture left-clicks on the monitor. | |
local time_between_runs = 30 | |
local current_run = time_between_runs | |
scanWorkRequests(monitor, bridge, storage) | |
displayTimer(monitor, current_run) | |
local TIMER = os.startTimer(1) | |
while true do | |
local e = {os.pullEvent()} | |
if e[1] == "timer" and e[2] == TIMER then | |
now = os.time() | |
if now >= 5 and now < 19.5 then | |
current_run = current_run - 1 | |
if current_run <= 0 then | |
scanWorkRequests(monitor, bridge, storage) | |
current_run = time_between_runs | |
end | |
end | |
displayTimer(monitor, current_run) | |
TIMER = os.startTimer(1) | |
elseif e[1] == "monitor_touch" then | |
os.cancelTimer(TIMER) | |
scanWorkRequests(monitor, bridge, storage) | |
current_run = time_between_runs | |
displayTimer(monitor, current_run) | |
TIMER = os.startTimer(1) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment