From 2ccf008f7fcca7672ddcfefd9244a6c075f75130 Mon Sep 17 00:00:00 2001 From: Dystroyer8 Date: Mon, 20 Jul 2026 18:23:41 +0200 Subject: [PATCH] Make ME pattern audit compact --- README.md | 6 +- me_pattern_debug.lua | 247 ++++++++++++++++++++++++++----------------- 2 files changed, 155 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index d8bc02f..bcb63f4 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,10 @@ greenhouse_inventory_debug ## AE2-Pattern und Duplikate erfassen Der rein lesende ME-Pattern-Auditor nutzt die Advanced-Peripherals-ME-Bridge und -schreibt alle vom Netzwerk angebotenen Pattern, Craftables und moegliche Duplikate -in `me_pattern_debug.txt`. Er startet keine Crafts und veraendert keine Pattern. +schreibt einen kompakten Pattern-Index, exakte Duplikate, Rezepte mit gleichem +Output sowie kleine Rohdaten-Samples nach `me_pattern_debug.txt`. Die kompakte +Ausgabe passt in den begrenzten Speicher eines CC-Computers. Er startet keine +Crafts und veraendert keine Pattern. ```lua delete me_pattern_debug.lua diff --git a/me_pattern_debug.lua b/me_pattern_debug.lua index db61690..bd101de 100644 --- a/me_pattern_debug.lua +++ b/me_pattern_debug.lua @@ -1,14 +1,17 @@ --- ATM10 AE2 pattern audit via Advanced Peripherals ME Bridge +-- ATM10 compact AE2 pattern audit via Advanced Peripherals ME Bridge -- Read-only: does not craft, export, import, cancel or modify patterns. +local VERSION = "1.1.0" local OUTPUT_FILE = "me_pattern_debug.txt" -local bridgeName +local SAMPLE_COUNT = 3 +local SAMPLE_LIMIT = 8000 +local LINE_LIMIT = 500 +local bridgeName local function hasType(name, wanted) if peripheral.hasType then return peripheral.hasType(name, wanted) end return peripheral.getType(name) == wanted end - for _, name in ipairs(peripheral.getNames()) do if hasType(name, "me_bridge") then bridgeName = name break end end @@ -21,40 +24,29 @@ local function sortedKeys(value) return keys end -local function serialize(value, depth, seen) +local function compact(value, depth, seen) depth, seen = depth or 0, seen or {} local kind = type(value) if kind == "string" then return string.format("%q", value) end if kind ~= "table" then return tostring(value) end if seen[value] then return "" end - if depth >= 12 then return "" end - seen[value] = true - local parts = { "{" } - for _, key in ipairs(sortedKeys(value)) do - parts[#parts + 1] = string.rep(" ", depth + 1) - .. "[" .. serialize(key, depth + 1, seen) .. "] = " - .. serialize(value[key], depth + 1, seen) .. "," - end - parts[#parts + 1] = string.rep(" ", depth) .. "}" - seen[value] = nil - return table.concat(parts, "\n") -end - -local function compact(value, seen) - local kind = type(value) - if kind == "string" then return string.format("%q", value) end - if kind ~= "table" then return tostring(value) end - seen = seen or {} - if seen[value] then return "" end + if depth >= 8 then return "" end seen[value] = true local parts = {} for _, key in ipairs(sortedKeys(value)) do - parts[#parts + 1] = "[" .. compact(key, seen) .. "]=" .. compact(value[key], seen) + parts[#parts + 1] = "[" .. compact(key, depth + 1, seen) .. "]=" + .. compact(value[key], depth + 1, seen) end seen[value] = nil return "{" .. table.concat(parts, ",") .. "}" end +local function truncate(value, limit) + value, limit = tostring(value), limit or LINE_LIMIT + if #value <= limit then return value end + return value:sub(1, limit) .. "...<" .. tostring(#value - limit) .. " chars>" +end + local function callRead(method, ...) local result = table.pack(pcall(peripheral.call, bridgeName, method, ...)) if not result[1] then return false, tostring(result[2]) end @@ -73,98 +65,161 @@ local function listValues(value) return result end -local function recipeSide(pattern, names) - for _, name in ipairs(names) do - if pattern[name] ~= nil then return pattern[name] end +local function first(value, keys) + if type(value) ~= "table" then return nil end + for _, key in ipairs(keys) do + if value[key] ~= nil then return value[key] end end end -local function fingerprint(pattern) - local inputs = recipeSide(pattern, { "inputs", "input", "ingredients" }) - local outputs = recipeSide(pattern, { "outputs", "output", "results", "result" }) - if inputs ~= nil or outputs ~= nil then - return "IN=" .. compact(inputs) .. "|OUT=" .. compact(outputs) - end - return compact(pattern) +local function itemName(value) + if type(value) == "string" then return value end + if type(value) ~= "table" then return nil end + local name = first(value, { "name", "id", "item", "itemId", "key" }) + if type(name) == "string" then return name end + if type(name) == "table" then return itemName(name) end + local what = first(value, { "what", "stack", "resource" }) + if what ~= nil and what ~= value then return itemName(what) end end -local lines = {} -local function emit(value) lines[#lines + 1] = tostring(value) end +local function itemToken(value) + local name = itemName(value) + if not name then return truncate(compact(value), 180) end + local count = type(value) == "table" and first(value, { "count", "amount", "quantity", "size" }) or nil + local token = name .. " x" .. tostring(tonumber(count) or 1) + if type(value) == "table" then + local components = first(value, { "components", "nbt", "tag" }) + if components ~= nil then token = token .. " " .. truncate(compact(components), 120) end + end + return token +end -emit("ATM10 AE2 PATTERN DEBUG") -emit("Computer ID: " .. tostring(os.getComputerID())) -emit("Computer label: " .. tostring(os.getComputerLabel() or "")) -emit("ME Bridge: " .. bridgeName) -local okOnline, online = callRead("isConnected") -emit("Connected: " .. (okOnline and tostring(online) or ("ERROR: " .. tostring(online)))) +local function sideEntries(side) + if side == nil then return {} end + if type(side) ~= "table" or itemName(side) then return { itemToken(side) } end + local entries = {} + for _, key in ipairs(sortedKeys(side)) do + if key ~= "n" then entries[#entries + 1] = tostring(key) .. ":" .. itemToken(side[key]) end + end + return entries +end + +local function sideText(side) + local entries = sideEntries(side) + return #entries > 0 and table.concat(entries, " + ") or "-" +end + +local INPUT_KEYS = { "inputs", "input", "ingredients" } +local OUTPUT_KEYS = { "outputs", "output", "results", "result" } +local function patternInputs(pattern) return first(pattern, INPUT_KEYS) end +local function patternOutputs(pattern) return first(pattern, OUTPUT_KEYS) end + +local function patternType(pattern) + local value = first(pattern, { "type", "patternType", "crafting", "processing", "isCrafting" }) + return value == nil and "unknown" or tostring(value) +end + +local function providerText(pattern) + local value = first(pattern, { + "provider", "providerName", "patternProvider", "machine", "location", "pos", "position" + }) + return value == nil and "" or truncate(compact(value), 180) +end + +local function exactFingerprint(pattern) + return patternType(pattern) .. "|IN=" .. table.concat(sideEntries(patternInputs(pattern)), "|") + .. "|OUT=" .. table.concat(sideEntries(patternOutputs(pattern)), "|") +end + +local function outputFingerprint(pattern) + return table.concat(sideEntries(patternOutputs(pattern)), "|") +end local okPatterns, rawPatterns = callRead("getPatterns") if not okPatterns then okPatterns, rawPatterns = callRead("getPatterns", {}) end if not okPatterns then error("getPatterns fehlgeschlagen: " .. tostring(rawPatterns), 0) end - local patterns = listValues(rawPatterns) -emit("Pattern records: " .. tostring(#patterns)) -local groups = {} + +local exactGroups, outputGroups = {}, {} for index, pattern in ipairs(patterns) do - local key = fingerprint(pattern) - groups[key] = groups[key] or {} - groups[key][#groups[key] + 1] = index -end -local duplicates = {} -for key, indexes in pairs(groups) do - if #indexes > 1 then duplicates[#duplicates + 1] = { key = key, indexes = indexes } end -end -table.sort(duplicates, function(a, b) - if #a.indexes == #b.indexes then return a.key < b.key end - return #a.indexes > #b.indexes -end) - -emit("Duplicate candidate groups: " .. tostring(#duplicates)) -emit("") -emit("DUPLICATE CANDIDATES") -emit(string.rep("=", 72)) -if #duplicates == 0 then - emit("No duplicates detected from the exposed pattern schema.") -else - for groupIndex, group in ipairs(duplicates) do - emit("") - emit("GROUP " .. groupIndex .. " | copies=" .. #group.indexes - .. " | pattern indexes=" .. table.concat(group.indexes, ", ")) - emit(group.key) + local exact, output = exactFingerprint(pattern), outputFingerprint(pattern) + exactGroups[exact] = exactGroups[exact] or {} + exactGroups[exact][#exactGroups[exact] + 1] = index + if output ~= "" then + outputGroups[output] = outputGroups[output] or {} + outputGroups[output][#outputGroups[output] + 1] = index end end -emit("") -emit("RAW GETPATTERNS RESULT") -emit(string.rep("=", 72)) -emit(serialize(rawPatterns)) -local okCraftable, craftable = callRead("getCraftableItems") -emit("") -emit("RAW GETCRAFTABLEITEMS RESULT") -emit(string.rep("=", 72)) -emit(okCraftable and serialize(craftable) or ("ERROR: " .. tostring(craftable))) - -emit("") -emit("CONNECTED PERIPHERALS") -emit(string.rep("=", 72)) -local peripheralNames = peripheral.getNames() -table.sort(peripheralNames) -for _, name in ipairs(peripheralNames) do - local types = table.pack(peripheral.getType(name)) - local typeNames = {} - for index = 1, types.n do - if types[index] ~= nil then typeNames[#typeNames + 1] = tostring(types[index]) end +local function duplicatesOf(groups) + local duplicates = {} + for signature, indexes in pairs(groups) do + if #indexes > 1 then duplicates[#duplicates + 1] = { signature = signature, indexes = indexes } end end - emit(name .. " | " .. table.concat(typeNames, ", ")) + table.sort(duplicates, function(a, b) + if #a.indexes == #b.indexes then return a.signature < b.signature end + return #a.indexes > #b.indexes + end) + return duplicates end +local exactDuplicates = duplicatesOf(exactGroups) +local outputDuplicates = duplicatesOf(outputGroups) +if fs.exists(OUTPUT_FILE) then fs.delete(OUTPUT_FILE) end local file = assert(fs.open(OUTPUT_FILE, "w")) -file.write(table.concat(lines, "\n")) -file.write("\n") +local function write(value) file.writeLine(tostring(value)) end + +write("ATM10 AE2 COMPACT PATTERN AUDIT V" .. VERSION) +write("Computer ID: " .. tostring(os.getComputerID())) +write("Computer label: " .. tostring(os.getComputerLabel() or "")) +write("ME Bridge: " .. bridgeName) +write("Pattern records: " .. tostring(#patterns)) +write("Exact duplicate groups: " .. tostring(#exactDuplicates)) +write("Same-output groups: " .. tostring(#outputDuplicates)) + +write("") +write("EXACT DUPLICATES") +write(string.rep("=", 72)) +if #exactDuplicates == 0 then write("None") end +for groupIndex, group in ipairs(exactDuplicates) do + write("GROUP " .. groupIndex .. " | copies=" .. #group.indexes + .. " | indexes=" .. table.concat(group.indexes, ",")) + write(truncate(group.signature)) + for _, index in ipairs(group.indexes) do write(" #" .. index .. " provider=" .. providerText(patterns[index])) end +end + +write("") +write("SAME OUTPUT (CHECK MANUALLY; MAY BE INTENTIONAL)") +write(string.rep("=", 72)) +if #outputDuplicates == 0 then write("None") end +for groupIndex, group in ipairs(outputDuplicates) do + write("GROUP " .. groupIndex .. " | recipes=" .. #group.indexes + .. " | indexes=" .. table.concat(group.indexes, ",") + .. " | output=" .. truncate(group.signature, 220)) +end + +write("") +write("COMPACT PATTERN INDEX") +write(string.rep("=", 72)) +for index, pattern in ipairs(patterns) do + write("#" .. index .. " | " .. patternType(pattern) + .. " | IN " .. truncate(sideText(patternInputs(pattern)), 220) + .. " | OUT " .. truncate(sideText(patternOutputs(pattern)), 220) + .. " | PROVIDER " .. providerText(pattern)) +end + +write("") +write("RAW SCHEMA SAMPLES") +write(string.rep("=", 72)) +for index = 1, math.min(SAMPLE_COUNT, #patterns) do + write("SAMPLE #" .. index) + write(truncate(compact(patterns[index]), SAMPLE_LIMIT)) +end file.close() -print("ME Pattern Audit abgeschlossen.") -print("Pattern-Datensaetze: " .. tostring(#patterns)) -print("Duplikat-Gruppen: " .. tostring(#duplicates)) +print("Kompakter ME Pattern Audit abgeschlossen.") +print("Pattern: " .. tostring(#patterns)) +print("Exakte Duplikat-Gruppen: " .. tostring(#exactDuplicates)) +print("Gleicher Output: " .. tostring(#outputDuplicates)) print("Erstellt: " .. OUTPUT_FILE) print("Bitte danach Codex 'fertig' schreiben.")