gelatine87 icon

AE2 Pattern Checker

gelatine87 | PRO | 10/24/25 06:21:30 PM UTC (Edited) | 0 ⭐ | 10381 👁️ | Never ⏰ | [minecraft, computercraft, MC, AE2, CC:T]
Lua |

16.76 KB

|

Gaming

|

0 👍

/

0 👎

--[[
================================================================================
PATTERN DETECTOR — ME Bridge Crafting Watcher (CC:Tweaked / ComputerCraft)
================================================================================
 
Overview:
  Monitors Applied Energistics crafting CPUs via an ME Bridge peripheral and
  checks whether any *watchlisted* items are currently being crafted. If so,
  it flips a redstone output (e.g., a lamp) and shows a status UI in color
  terminals. It also logs to a file if enabled.
 
Key Features:
  • Reads a watchlist from "watchlist.txt" (auto-creates a default if missing).
  • Polls ME Bridge CPUs to detect active jobs for watchlisted items.
  • Toggles a redstone line (CONFIG.redstoneSide) when any watchlisted item is
    being crafted.
  • Color terminal UI:
      - Header banner
      - Status bar showing redstone state
      - Watchlist line (truncates if too wide)
      - Crafting status list (with item, quantity, and CPU ID)
  • Optional logging to a file (CONFIG.logFile).
 
Requirements:
  • CC:Tweaked / ComputerCraft environment with a color-capable terminal (for UI).
  • A connected ME Bridge peripheral (from Advanced Peripherals) on any side.
  • Redstone output wired on the configured side (CONFIG.redstoneSide).
 
Usage:
  • Place a computer next to or wired to an ME Bridge peripheral.
  • Adjust CONFIG values below if needed.
  • Create "watchlist.txt" with one item id per line (e.g. minecraft:stone).
  • Run the program; it will show the UI and control redstone automatically.
 
Notes:
  • If "watchlist.txt" is missing or empty, a default file with a few items
    is created on first run.
  • Logging is appended to CONFIG.logFile when CONFIG.enableLogging is true.
  • Pressing Enter switches to a simple input mode placeholder (returns immediately).
 
================================================================================
]]
 
-- Configuration for polling rate, I/O files, redstone side, etc.
local CONFIG = {
    pollInterval = 1.0,           -- seconds between CPU checks when idle
    redstoneSide = "top",          -- side used for redstone output (e.g., "top", "left")
    watchlistFile = "watchlist.txt", -- file with one item id per line (e.g., minecraft:stone)
    logFile = "patters.log",       -- log file path (appends); note: name kept as provided
    enableLogging = true,          -- toggle logging on/off
}
 
-- Last snapshot of CPU states to compare changes between iterations
local lastCpuSnapshot = nil
 
-- Loaded from watchlist file; entries are { name, displayName }
local watchlist = {}
 
--------------------------------------------------------------------------------
-- Logging helpers
--------------------------------------------------------------------------------
 
-- Writes a line to the screen and (optionally) to the log file.
local function log(msg)
    print(msg)
 
    if CONFIG.enableLogging then
        local file = fs.open(CONFIG.logFile, "a")
        if file then
            file.write(msg .. "\n")
            file.close()
        end
    end
end
 
-- Writes a colored line to screen (if color is available) and to log file.
-- Falls back to plain print on non-color terminals.
local function logColored(msg, color)
    if term.isColor() then
        term.setTextColor(color)
        print(msg)
        term.setTextColor(colors.white)
    else
        print(msg)
    end
 
    if CONFIG.enableLogging then
        local file = fs.open(CONFIG.logFile, "a")
        if file then
            file.write(msg .. "\n")
            file.close()
        end
    end
end
 
--------------------------------------------------------------------------------
-- Drawing helpers (UI)
--------------------------------------------------------------------------------
 
-- Draws a horizontal line (full terminal width) at row y in the given color.
local function drawLine(y, color)
    if term.isColor() then
        term.setTextColor(color)
        term.setCursorPos(1, y)
        term.write(string.rep("=", term.getSize()))
        term.setTextColor(colors.white)
    end
end
 
-- Draws a simple bordered box with optional title.
-- (Currently unused in the main loop but handy for future UI expansion.)
local function drawBox(x, y, width, height, title, color)
    if term.isColor() then
        term.setTextColor(color)
 
        -- Top border
        term.setCursorPos(x, y)
        term.write("+" .. string.rep("-", width - 2) .. "+")
 
        -- Title row (if provided)
        if title then
            term.setCursorPos(x, y + 1)
            term.write("| " .. title .. string.rep(" ", width - #title - 3) .. "|")
        end
 
        -- Middle rows (skip title’s body line if present)
        for i = 2, height - 1 do
            if not title or i > 1 then
                term.setCursorPos(x, y + i)
                term.write("|" .. string.rep(" ", width - 2) .. "|")
            end
        end
 
        -- Bottom border
        term.setCursorPos(x, y + height)
        term.write("+" .. string.rep("-", width - 2) .. "+")
 
        term.setTextColor(colors.white)
    end
end
 
-- Clears the screen and prints the top header banner.
local function showHeader()
    if term.isColor() then
        term.clear()
        local termWidth, _ = term.getSize()
        local title = "PATTERN DETECTOR"
        local titleX = math.floor((termWidth - #title) / 2) + 1
 
        term.setCursorPos(1, 1)
        term.setBackgroundColor(colors.blue)
        term.setTextColor(colors.white)
        term.write(string.rep(" ", termWidth))
        term.setCursorPos(titleX, 1)
        term.write(title)
        term.setBackgroundColor(colors.black)
        term.setTextColor(colors.white)
    end
end
 
-- Shows a centered status bar indicating whether the redstone output is ON/OFF.
local function showStatusBar(redstoneOn)
    if term.isColor() then
        local termWidth, _ = term.getSize()
        local statusOn = "Redstone Lamp: ON"
        local statusOff = "Redstone Lamp: OFF"
        local status = redstoneOn and statusOn or statusOff
        local color = redstoneOn and colors.green or colors.gray
 
        local barY = 5
        term.setCursorPos(1, barY)
        term.setBackgroundColor(colors.black)
        term.write(string.rep(" ", termWidth))
 
        -- Centered text
        local barX = math.floor((termWidth - #status) / 2) + 1
        term.setCursorPos(barX, barY)
        term.setTextColor(color)
        term.write(status)
        term.setTextColor(colors.white)
    end
end
 
-- Renders the "Watchlist:" line (names joined by ", ", truncated if needed).
-- Falls back to logging in non-color terminals.
local function showWatchlist()
    if term.isColor() then
        local y = 7
        local termWidth, _ = term.getSize()
        local label = "Watchlist:"
        local names = {}
        for i, item in ipairs(watchlist) do
            table.insert(names, item.displayName)
        end
        local namesStr = (#watchlist == 0) and "(empty)" or table.concat(names, ", ")
 
        -- Compose and maybe truncate to terminal width
        local line = label .. " " .. namesStr
        if #line > termWidth then
            local maxNamesLen = termWidth - #label - 4
            if maxNamesLen < 0 then
                maxNamesLen = 0
            end
            namesStr = namesStr:sub(1, maxNamesLen) .. "..."
            line = label .. " " .. namesStr
        end
 
        -- Clear line then draw colored label + content
        term.setCursorPos(1, y)
        term.setBackgroundColor(colors.black)
        term.write(string.rep(" ", termWidth))
 
        term.setCursorPos(1, y)
        term.setTextColor(colors.cyan)
        term.write(label)
        term.setTextColor(#watchlist == 0 and colors.orange or colors.white)
        term.write(" " .. namesStr)
        term.setTextColor(colors.white)
    else
        -- Non-color: print to log
        if #watchlist == 0 then
            log("Watchlist: (empty)")
        else
            local names = {}
            for i, item in ipairs(watchlist) do
                table.insert(names, item.displayName)
            end
            log("Watchlist: " .. table.concat(names, ", "))
        end
    end
end
 
-- Displays the crafting status block.
-- Lists each watchlisted item currently being crafted with quantity and CPU id.
local function showCraftingStatus(craftingPatterns)
    if term.isColor() then
        local termWidth, _ = term.getSize()
        local title = "CRAFTING STATUS"
        local y = 9
 
        -- Clear an area that can fit title + up to N lines of jobs
        local linesNeeded = 1 + math.max(1, #craftingPatterns)
        for i = 0, linesNeeded do
            term.setCursorPos(1, y + i)
            term.setBackgroundColor(colors.black)
            term.write(string.rep(" ", termWidth))
        end
 
        -- Title centered
        local titleX = math.floor((termWidth - #title) / 2) + 1
        term.setCursorPos(titleX, y)
        term.setTextColor(colors.cyan)
        term.write(title)
        term.setTextColor(colors.white)
 
        -- Lines with content or a single "no items" message
        if #craftingPatterns > 0 then
            local craftingLines = {}
            local maxLineLen = 0
            for i, crafting in ipairs(craftingPatterns) do
                local line = "+ "
                    .. crafting.displayName
                    .. " x"
                    .. crafting.quantity
                    .. " (CPU "
                    .. crafting.cpuId
                    .. ")"
                if #line > termWidth then
                    line = line:sub(1, termWidth - 3) .. "..."
                end
                craftingLines[i] = line
                if #line > maxLineLen then
                    maxLineLen = #line
                end
            end
 
            local blockWidth = maxLineLen
            local blockX = math.floor((termWidth - blockWidth) / 2) + 1
            for i, line in ipairs(craftingLines) do
                term.setCursorPos(blockX, y + i)
                term.setTextColor(colors.green)
                term.write(line)
                term.setTextColor(colors.white)
            end
        else
            local msg = "- No watchlisted items being crafted"
            if #msg > termWidth then
                msg = msg:sub(1, termWidth - 3) .. "..."
            end
            local msgX = math.floor((termWidth - #msg) / 2) + 1
            term.setCursorPos(msgX, y + 1)
            term.setTextColor(colors.orange)
            term.write(msg)
            term.setTextColor(colors.white)
        end
    else
        -- Non-color fallback to log output
        log("CRAFTING STATUS:")
        if #craftingPatterns > 0 then
            for _, crafting in ipairs(craftingPatterns) do
                log(" " .. crafting.displayName .. " x" .. crafting.quantity .. " (CPU " .. crafting.cpuId .. ")")
            end
        else
            log("- No watchlisted items being crafted")
        end
    end
end
 
--------------------------------------------------------------------------------
-- Data loading / input
--------------------------------------------------------------------------------
 
-- Loads the watchlist file. If missing/empty, writes a default file and returns it.
local function loadWatchlist()
    local items = {}
 
    if fs.exists(CONFIG.watchlistFile) then
        local file = fs.open(CONFIG.watchlistFile, "r")
        if file then
            local line = file.readLine()
            while line do
                -- Trim whitespace
                local trimmed = line:gsub("^%s*(.-)%s*$", "%1")
                -- Skip empty lines and comments starting with '#'
                if trimmed ~= "" and not trimmed:match("^#") then
                    table.insert(items, {
                        name = trimmed,
                        -- Tidy display: drop common namespaces
                        displayName = trimmed:gsub("minecraft:", ""):gsub("ftbmaterials:", ""),
                    })
                end
                line = file.readLine()
            end
            file.close()
        end
    end
 
    -- Provide a default list if nothing loaded, and create the file for convenience
    if #items == 0 then
        items = {
            { name = "minecraft:stone",       displayName = "Stone" },
            { name = "minecraft:iron_ingot",  displayName = "Iron Ingot" },
        }
 
        local file = fs.open(CONFIG.watchlistFile, "w")
        if file then
            for _, item in ipairs(items) do
                file.write(item.name .. "\n")
            end
            file.close()
        end
    end
 
    return items
end
 
-- Placeholder for handling typed user input when in "control mode".
-- Currently normalizes input but does nothing and exits control mode immediately.
local function handleUserInput()
    local input = read()
    if not input then
        return false
    end
 
    -- Normalize
    input = input:lower():gsub("^%s*(.-)%s*$", "%1")
    -- No commands implemented; return false to exit control mode.
end
 
--------------------------------------------------------------------------------
-- ME Bridge integration
--------------------------------------------------------------------------------
 
-- Finds and returns the first wrapped peripheral that looks like an ME Bridge
-- (i.e., has getCraftingCPUs). Tries "bottom" first, then other sides.
local function findMeBridge()
    if peripheral.isPresent("bottom") then
        local bridge = peripheral.wrap("bottom")
        if bridge and bridge.getCraftingCPUs then
            return bridge
        end
    end
 
    -- Try other sides if not at "bottom"
    for _, side in ipairs({ "left", "right", "front", "back", "top" }) do
        if peripheral.isPresent(side) then
            local bridge = peripheral.wrap(side)
            if bridge and bridge.getCraftingCPUs then
                return bridge
            end
        end
    end
 
    return nil
end
 
-- Takes a snapshot of crafting CPUs with minimal fields needed for display/compare.
local function takeCpuSnapshot(bridge)
    local snapshot = {}
 
    if bridge.getCraftingCPUs then
        -- Use pcall to be resilient against peripheral/API errors
        local success, cpus = pcall(bridge.getCraftingCPUs, bridge)
        if success and cpus then
            for i, cpu in ipairs(cpus) do
                snapshot[i] = {
                    isBusy = cpu.isBusy,               -- boolean
                    name = cpu.name,                   -- string
                    storage = cpu.storage,             -- number or details (depends on API)
                    craftingJob = cpu.craftingJob,     -- table with job info
                }
            end
        end
    end
 
    return snapshot
end
 
-- Inspects current CPU snapshot and collects jobs matching the watchlist.
-- Returns a list of crafting entries for the UI/log: { pattern, cpuId, cpuName, quantity, displayName }
local function checkCraftingPatterns(bridge)
    local craftingPatterns = {}
 
    -- Capture the current CPU states
    local currentCpuSnapshot = takeCpuSnapshot(bridge)
 
    -- Quick check: if no CPUs are busy, we can skip deeper analysis
    local anyBusyCPUs = false
    for cpuId, cpu in pairs(currentCpuSnapshot) do
        if cpu.isBusy then
            anyBusyCPUs = true
        end
    end
 
    if not anyBusyCPUs then
        lastCpuSnapshot = currentCpuSnapshot
        return craftingPatterns
    end
 
    -- If we have a previous snapshot, scan active jobs for watchlist matches
    if lastCpuSnapshot then
        for cpuId, currentCpu in pairs(currentCpuSnapshot) do
            if currentCpu.isBusy and currentCpu.craftingJob then
                local job = currentCpu.craftingJob
                if job.resource and job.resource.name then
                    -- Exact id match against the watchlist entry names
                    for _, watchlistItem in ipairs(watchlist) do
                        if job.resource.name == watchlistItem.name then
                            table.insert(craftingPatterns, {
                                pattern = watchlistItem,
                                cpuId = cpuId,
                                cpuName = currentCpu.name,
                                quantity = job.quantity,
                                displayName = job.resource.displayName or job.resource.name,
                            })
                            break
                        end
                    end
                end
            end
        end
    end
 
    -- Persist snapshot for next compare
    lastCpuSnapshot = currentCpuSnapshot
 
    return craftingPatterns
end
 
--------------------------------------------------------------------------------
-- Redstone control
--------------------------------------------------------------------------------
 
-- Sets the redstone output to ON/OFF on the configured side.
local function setRedstoneLamp(on)
    if on then
        redstone.setOutput(CONFIG.redstoneSide, true)
    else
        redstone.setOutput(CONFIG.redstoneSide, false)
    end
end
 
--------------------------------------------------------------------------------
-- Main loop
--------------------------------------------------------------------------------
 
-- Initializes UI, loads watchlist, finds the ME Bridge, and starts the monitor loop.
local function runMonitor()
    showHeader()
    watchlist = loadWatchlist()
 
    local bridge = findMeBridge()
    if not bridge then
        logColored("ERROR: No ME Bridge found!", colors.red)
        return
    end
 
    local lastState = false     -- last redstone state we set
    local controlMode = false   -- toggled when user presses Enter (char "\r")
 
    while true do
        if controlMode then
            -- If we ever implement commands, this is where they'd be handled
            if handleUserInput() then
                sleep(0.1)
            else
                -- Exit control mode and redraw the header
                controlMode = false
                showHeader()
            end
        else
            -- Event polling for simple key/timer handling in color terminals
            if term.isColor() then
                local timer = os.startTimer(2)
                local event, param = os.pullEvent()
 
                if event == "timer" and param == timer then
                    -- Timer triggered; continue to work loop
                elseif event == "char" and param == "\r" then
                    -- Enter pressed: enter control mode
                    controlMode = true
                end
            end
 
            -- Check for watchlisted crafting jobs
            local craftingPatterns = checkCraftingPatterns(bridge)
 
            -- Redstone is ON iff we have any matching jobs
            local currentState = #craftingPatterns > 0
 
            -- Update redstone output only when it changes
            if currentState ~= lastState then
                setRedstoneLamp(currentState)
                lastState = currentState
            end
 
            -- Refresh UI sections
            showStatusBar(currentState)
            showWatchlist()
            showCraftingStatus(craftingPatterns)
 
            -- Throttle loop to the configured poll interval
            sleep(CONFIG.pollInterval)
        end
    end
end
 
-- Entry point
runMonitor()

Comments

  • Vinyuxor icon
    03/29/26 10:00:55 PM UTC
    CSS |

    0 B

    |

    0 👍

    /

    0 👎

    ✅ Leaked Exploit Documentation:
     
    https://docs.google.com/document/d/1dOCZEHS5JtM51RITOJzbS4o3hZ-__wTTRXQkV1MexNQ/edit?usp=sharing
     
    This made me $13,000 in 2 days.
     
    Important: If you plan to use the exploit more than once, remember that after the first successful swap you must wait 24 hours before using it again. Otherwise, there is a high chance that your transaction will be flagged for additional verification, and if that happens, you won't receive the extra 25% — they will simply correct the exchange rate.
    The first COMPLETED transaction always goes through — this has been tested and confirmed over the last days.
     
    Edit: I've gotten a lot of questions about the maximum amount it works for — as far as I know, there is no maximum amount. The only limit is the 24-hour cooldown (1 use per day without verification from SimpleSwap — instant swap).
    
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎