Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Backpack Inventory Processor v1.21
- -- Configuration
- local trash = peripheral.wrap("up")
- local chest = peripheral.wrap("back")
- local modem = peripheral.wrap("top") -- Modem on top as per your setup
- local monitor = peripheral.find("monitor") -- Find the monitor through the modem
- local backpack_side = "front"
- -- Constants
- local VERSION = "1.21"
- local BACKPACKS_PER_CYCLE = 9 -- Process exactly 9 backpacks per cycle
- local STUCK_LIMIT = 10
- local SLEEP_INTERVAL = 0.25
- -- Global tables to track item quantities and frequencies (removed local so they're actually global for save functions)
- item_quantities = {} -- {item_name = total_quantity}
- item_frequencies = {} -- {item_name = number_of_stacks}
- -- wtp imports
- local filefunctions = dofile("filefunctions.lua") -- https://pastebin.com/52b6wYBq
- -- Append line of text to file at 'path'
- local function fileAppend(path, text)
- local file = io.open(path, "a")
- file:write(text .. "\n")
- file:close()
- end
- -- Append a log entry with timestamp 'time' and text 'text' to the file at 'path'
- local function addLog(text)
- fileAppend("log.txt", "[" .. os.time() .. "]"..text)
- end
- -- Initialize monitor with a specific text scale
- local function initMonitor(scale)
- if not monitor then
- error("Monitor not found. Ensure it's connected via the modem.")
- end
- monitor.clear()
- monitor.setTextScale(scale)
- monitor.setCursorPos(1, 1)
- return monitor.getSize()
- end
- -- Display items on monitor in two columns with progress bars at the bottom
- local function displayManifest(manifest, mon_width, mon_height, backpacks_processed, current_slot, backpack_size)
- local mon_width, mon_height = initMonitor(1.4) -- Scale 1.4 for processing
- monitor.clear()
- monitor.setCursorPos(1, 1)
- -- Add version header
- monitor.write("Version " .. VERSION)
- monitor.setCursorPos(1, 2)
- monitor.write("Backpack Contents")
- monitor.setCursorPos(1, 3)
- local mid_point = math.ceil(mon_width / 2) -- Split the width for two columns
- local max_item_length = mid_point - 1 -- Dynamically set max_item_length based on column width
- local row = 3 -- Start below the headers
- local col = 1
- local item_display_height = mon_height - 2 -- Reserve last 2 lines for progress bars
- for _, item in ipairs(manifest) do
- if row > item_display_height then
- col = mid_point -- Second column starts at mid_point
- row = 3 -- Reset row below the headers
- monitor.setCursorPos(col, row)
- end
- -- Truncate item name to leave room for quantity (": XX" plus buffer)
- local display_name = item.name
- local name_length = max_item_length - 5 -- Reserve 5 characters for ": XX" and a buffer
- if #display_name > name_length then
- display_name = string.sub(display_name, 1, name_length - 3) .. "..."
- end
- monitor.write(display_name .. ": " .. item.count)
- row = row + 1
- monitor.setCursorPos(col, row)
- end
- -- Draw backpack progress bar (Line mon_height - 1)
- local backpack_percent = (backpacks_processed / BACKPACKS_PER_CYCLE) * 100
- local filled_backpack = math.floor(backpack_percent * mon_width / 100)
- monitor.setCursorPos(1, mon_height - 1)
- monitor.setBackgroundColour(colors.green)
- for i = 1, filled_backpack do
- monitor.write(" ")
- end
- monitor.setBackgroundColour(colors.red)
- for i = filled_backpack + 1, mon_width do
- monitor.write(" ")
- end
- monitor.setBackgroundColour(colors.black)
- monitor.setTextColour(colors.white)
- monitor.setCursorPos(1, mon_height - 1)
- monitor.write("Backpacks: " .. math.floor(backpack_percent) .. "%")
- -- Draw inventory progress bar (Line mon_height)
- local inventory_percent = backpack_size > 0 and (current_slot / backpack_size) * 100 or 0
- local filled_inventory = math.floor(inventory_percent * mon_width / 100)
- monitor.setCursorPos(1, mon_height)
- monitor.setBackgroundColour(colors.green)
- for i = 1, filled_inventory do
- monitor.write(" ")
- end
- monitor.setBackgroundColour(colors.red)
- for i = filled_inventory + 1, mon_width do
- monitor.write(" ")
- end
- monitor.setBackgroundColour(colors.black)
- monitor.setTextColour(colors.white)
- monitor.setCursorPos(1, mon_height)
- monitor.write("Inventory: " .. math.floor(inventory_percent) .. "%")
- end
- -- Check for backpack and handle stuck detection
- local function checkForBackpack()
- local stuck = 0
- while true do
- local has_block, data = turtle.inspect()
- if has_block then
- return true
- end
- print("Waiting for backpack...")
- sleep(SLEEP_INTERVAL)
- stuck = stuck + 1
- if stuck >= STUCK_LIMIT then
- print("No more backpacks to process.")
- return false
- end
- end
- end
- -- Process backpack contents and track items imported
- local function processBackpack(backpack, chest, manifest, mon_width, mon_height, backpacks_processed)
- local size = backpack.size()
- local items_imported = 0 -- Track items imported in this backpack
- for i = 1, size do
- local slot = backpack.getItemDetail(i)
- if slot then
- addLog("Slot " .. i .. " contains " .. slot.name .. " (" .. slot.count .. ")")
- table.insert(manifest, {name = slot.name, count = slot.count})
- -- Update item quantities and frequencies
- item_quantities[slot.name] = (item_quantities[slot.name] or 0) + slot.count
- item_frequencies[slot.name] = (item_frequencies[slot.name] or 0) + 1
- displayManifest(manifest, mon_width, mon_height, backpacks_processed, i, size)
- local items = backpack.pushItems(peripheral.getName(chest), i, 64)
- if items ~= slot.count then
- addLog(items .. " of " .. slot.count .. " transferred, chest full? Failing.")
- return false, items_imported
- end
- items_imported = items_imported + 1 -- Count each stack as an "item"
- end
- end
- return true, items_imported
- end
- -- Empty turtle inventory
- local function emptyTurtleInventory()
- local success = true
- local mon_width, mon_height = initMonitor(1.4) -- Scale 1.4 for dumping
- monitor.clear()
- monitor.setCursorPos(1, 1)
- monitor.write("Version " .. VERSION)
- monitor.setCursorPos(1, 2)
- monitor.write("Dumping empty backpacks...")
- turtle.turnRight()
- local ok, err = pcall(function()
- for slot = 1, BACKPACKS_PER_CYCLE do
- turtle.select(slot)
- if not turtle.drop() then
- addLog("Drop failed, likely had <" .. BACKPACKS_PER_CYCLE .. " backpacks.")
- success = false
- return
- end
- end
- end)
- turtle.turnLeft() -- Always turn back left
- if not ok then
- addLog("Error during dump: " .. tostring(err))
- success = false
- end
- return success
- end
- -- Helper function to sort items for display
- local function sortItemsForDisplay(item_table, sort_by_value, ascending)
- local sorted_items = {}
- for name, value in pairs(item_table) do
- table.insert(sorted_items, {name = name, value = value})
- end
- table.sort(sorted_items, function(a, b)
- if a.value == b.value then
- return a.name < b.name -- Alphabetical order for ties
- end
- if ascending then
- return a.value < b.value
- else
- return a.value > b.value
- end
- end)
- return sorted_items
- end
- -- Display waiting screen with top looted and rarest items
- local function displayWaitingScreen(mon_width, mon_height, total_backpacks_processed, total_items_imported)
- monitor.clear()
- monitor.setCursorPos(1, 1)
- monitor.write("Version " .. VERSION)
- monitor.setCursorPos(1, 2)
- monitor.write("Waiting for backpacks...")
- monitor.setCursorPos(1, 3)
- monitor.write("Backpacks Processed: " .. total_backpacks_processed)
- monitor.setCursorPos(1, 4)
- monitor.write("Items Imported: " .. total_items_imported)
- -- Get top 10 looted items (by quantity)
- -- I believe this passes a global reference that sort uses internally as a local
- local top_looted = sortItemsForDisplay(item_quantities, true, false)
- local mid_point = math.ceil(mon_width / 2)
- local max_item_length = mid_point - 5 -- Reserve space for ": XXXXX" (up to 5 digits)
- -- Display top 10 looted items in two columns
- monitor.setCursorPos(1, 7)
- monitor.write("Top 10 Looted Items")
- local row = 8 -- Start items on line 8
- local col = 1
- for i = 1, math.min(10, #top_looted) do
- if i == 6 then
- col = mid_point
- row = 8 -- Reset row for second column
- end
- local item = top_looted[i]
- local display_name = item.name
- if #display_name > max_item_length then
- display_name = string.sub(display_name, 1, max_item_length - 3) .. "..."
- end
- monitor.setCursorPos(col, row)
- monitor.write(display_name .. ": " .. item.value)
- row = row + 1
- end
- -- Display 5 rarest items below with a blank line
- local rarest_items = sortItemsForDisplay(item_frequencies, true, true)
- monitor.setCursorPos(1, 14) -- Moved to line 14 with a blank line on 13
- monitor.write("5 Rarest Items Seen")
- row = 15 -- Start items on line 15
- col = 1
- for i = 1, math.min(5, #rarest_items) do
- local item = rarest_items[i]
- local display_name = item.name
- if #display_name > max_item_length then
- display_name = string.sub(display_name, 1, max_item_length - 3) .. "..."
- end
- monitor.setCursorPos(col, row)
- monitor.write(display_name .. ": " .. item.value)
- row = row + 1
- end
- end
- -- Main function
- local function main()
- local mon_width, mon_height = initMonitor(1.4) -- Use 1.4 scale for waiting screen
- local total_backpacks_processed = 0 -- Track total backpacks processed
- local total_items_imported = 0 -- Track total items imported
- while true do
- -- Wait for redstone signal to indicate 9 backpacks (signal strength 15)
- displayWaitingScreen(mon_width, mon_height, total_backpacks_processed, total_items_imported)
- while redstone.getAnalogInput("bottom") ~= 15 do
- sleep(1) -- Check every second to avoid excessive CPU usage
- monitor.setCursorPos(1, 5)
- monitor.write("Signal: " .. redstone.getAnalogInput("bottom"))
- sleep(1) -- Update every second
- displayWaitingScreen(mon_width, mon_height, total_backpacks_processed, total_items_imported)
- end
- -- Process exactly 9 backpacks
- local backpacks_processed = 0 -- Track backpacks processed in this cycle
- local cycle_items_imported = 0 -- Track items imported in this cycle
- for slot = 1, BACKPACKS_PER_CYCLE do
- local manifest = {} -- Reset manifest for each backpack
- turtle.select(slot)
- redstone.setOutput("left", true)
- if not checkForBackpack() then
- redstone.setOutput("left", false) -- Reset redstone on abort
- addLog("Fewer than 9 backpacks available, proceeding with " .. backpacks_processed .. " backpacks.")
- break
- end
- redstone.setOutput("left", false)
- backpacks_processed = backpacks_processed + 1
- local backpack = peripheral.wrap(backpack_side)
- local success, items_imported = processBackpack(backpack, chest, manifest, mon_width, mon_height, backpacks_processed)
- if not success then
- redstone.setOutput("left", false) -- Ensure redstone is off
- addLog("Chest full, proceeding to dump backpacks.")
- break
- end
- cycle_items_imported = cycle_items_imported + items_imported
- turtle.dig()
- sleep(1) -- Brief pause to show the manifest
- end
- -- Dump backpacks if any were processed
- if backpacks_processed > 0 then
- if not emptyTurtleInventory() then
- redstone.setOutput("left", false) -- Ensure redstone is off
- addLog("Dump failed, continuing to wait for next cycle.")
- -- Continue to waiting state instead of exiting
- end
- total_backpacks_processed = total_backpacks_processed + backpacks_processed
- total_items_imported = total_items_imported + cycle_items_imported
- end
- -- Return to waiting state
- mon_width, mon_height = initMonitor(1.4) -- Reset to 1.4 scale for waiting
- monitor.clear()
- monitor.setCursorPos(1, 1)
- monitor.write("Version " .. VERSION)
- monitor.setCursorPos(1, 2)
- monitor.write("Ready for next set of backpacks...")
- monitor.setCursorPos(1, 3)
- monitor.write("Backpacks Processed: " .. total_backpacks_processed)
- monitor.setCursorPos(1, 4)
- monitor.write("Items Imported: " .. total_items_imported)
- sleep(2) -- Brief pause before returning to waiting state
- filefunctions.saveStats("stats.dat") -- trying _G. out ,item_quantities,item_frequencies)
- print("Logging stats...")
- for k,v in pairs(item_quantities) do
- addLog("Qty", k, v)
- end
- for k,v in pairs(item_frequencies) do
- addLog("Freq", k, v)
- end
- addLog("Stats saved.")
- end
- end
- -- initialize log
- if fs.exists("log.txt") then
- else
- file = io.open("logs.txt", "w")
- file:write("")
- file:close()
- end
- -- load previous values (if any)
- -- item_quantities,item_frequencies=
- filefunctions.loadStats("stats.dat") -- _G. should be global so trying to revert back to not passing the tables
- -- for whatever reason my non local item_* variables seem to act local in scope and seems to need the following to work
- item_quantities=_G.item_quantities
- item_frequencies=_G.item_frequencies
- print("checking stats...")
- for k,v in pairs(item_quantities) do
- print("Qty", k, v)
- end
- for k,v in pairs(item_frequencies) do
- print("Freq", k, v)
- end
- -- Run the program
- main()
- -- Ensure redstone is off on program exit
- redstone.setOutput("left", false)
- -- Mobs drop backpacks now. Basic setup is AE2 export bus->ender chest, that 'combo' ender chest exports to a placer.
- -- The redstone signal on the left is letting a placer place the backpack (back-filled from an item
- -- pipe from the ender chest full of backpacks from the AE2) it's carried along the top of a block via redstone wire
- -- the chest referenced in code below is where the turtle dumps the inventory inside the backpacks
- -- finally after 16 backpacks it turns to the right and drops them all in a different ender chest that then drops them
- -- into a matter condenser for singularities.
- -- I've introduced a comparator and a couple ARS redstone relays linked one in front of the comparator, and one below the turtle.
- -- Alternative implementation by DesatKorun at https://pastebin.com/gkDnN8Fm
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement