Files
ATM10_CC_Codes/me_pattern_debug.lua

226 lines
8.4 KiB
Lua

-- 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 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
if not bridgeName then error("Keine ME Bridge am Computer gefunden", 0) end
local function sortedKeys(value)
local keys = {}
for key in pairs(value or {}) do keys[#keys + 1] = key end
table.sort(keys, function(a, b) return tostring(a) < tostring(b) end)
return keys
end
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 "<cycle>" end
if depth >= 8 then return "<max-depth>" end
seen[value] = true
local parts = {}
for _, key in ipairs(sortedKeys(value)) do
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
if result.n == 2 then return true, result[2] end
local values = { n = result.n - 1 }
for index = 2, result.n do values[index - 1] = result[index] end
return true, values
end
local function listValues(value)
local result = {}
if type(value) ~= "table" then return result end
for key, entry in pairs(value) do
if key ~= "n" and type(entry) == "table" then result[#result + 1] = entry end
end
return result
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 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 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
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 "<not exposed>" 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)
local exactGroups, outputGroups = {}, {}
for index, pattern in ipairs(patterns) do
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
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
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"))
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 "<not set>"))
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("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.")