Skip to content

Instantly share code, notes, and snippets.

@Homer199090
Last active February 6, 2025 19:45
Show Gist options
  • Save Homer199090/03a5ac19f0a97381e1b3e3b5e295d3f9 to your computer and use it in GitHub Desktop.
Save Homer199090/03a5ac19f0a97381e1b3e3b5e295d3f9 to your computer and use it in GitHub Desktop.
Lager
-- 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