-- -- sa_probe.lua -- -- Ground-truth probe for Self-Aware detection. Run on the CHAMBER RTU computer. -- For every sim chamber that has a data model, it dumps the FULL getItemDetail -- table (every field CC exposes), the chamber's methods, energy, and re-reads -- the model 8s later to show exactly what (if anything) changes over time. -- -- sa_probe (writes sa_probe.txt + prints a summary) -- -- We want to know: does a self-aware model's nbt hash stay STABLE, or keep -- changing? And is there any readable field (tags, displayName, damage, a hidden -- method) that reveals the tier directly? -- local CHAMBER = "hostilenetworks:sim_chamber" local MODEL = "hostilenetworks:data_model" local out = fs.open("sa_probe.txt", "w") local function w(s) out.write(tostring(s) .. "\n"); print(s) end -- pretty-print any lua value (tables recursively, capped depth) local function dump(v, indent, depth) indent = indent or "" depth = depth or 0 if type(v) ~= "table" or depth > 4 then w(indent .. tostring(v)) return end local keys = {} for k in pairs(v) do keys[#keys+1] = k end table.sort(keys, function (a, b) return tostring(a) < tostring(b) end) for _, k in ipairs(keys) do local val = v[k] if type(val) == "table" then w(indent .. tostring(k) .. ":") dump(val, indent .. " ", depth + 1) else w(indent .. tostring(k) .. " = " .. tostring(val)) end end end local function modelSlot(p) local ok, list = pcall(p.list) if not ok or not list then return nil end for s, item in pairs(list) do if item.name and item.name:find(MODEL) then return s, item end end return nil end -- find chambers with a model local targets = {} for _, n in ipairs(peripheral.getNames()) do if peripheral.getType(n) == CHAMBER then local p = peripheral.wrap(n) local slot = modelSlot(p) if slot then targets[#targets+1] = { name = n, p = p, slot = slot } end end end w("=== sa_probe " .. os.date("%H:%M:%S") .. " ===") w("chambers with a model: " .. #targets) if #targets == 0 then w("(load a model into a chamber first)"); out.close(); return end -- methods of the first chamber (same block type for all) w("") w("--- chamber getMethods() ---") local meths = peripheral.getMethods(targets[1].name) or {} table.sort(meths) w(table.concat(meths, ", ")) -- first read local first = {} for _, t in ipairs(targets) do w("") w("=== " .. t.name .. " slot " .. t.slot .. " (read 1) ===") local ok, det = pcall(t.p.getItemDetail, t.slot) if ok and type(det) == "table" then dump(det) else w("getItemDetail failed: " .. tostring(det)) end local e = 0; pcall(function () e = t.p.getEnergy() end) w("energy = " .. e) first[t.name] = (ok and type(det) == "table") and det.nbt or nil end w("") w("... waiting 8s to see what changes ...") sleep(8) -- second read, diff the nbt hash w("") w("=== READ 2 (nbt-hash diff) ===") for _, t in ipairs(targets) do local ok, det = pcall(t.p.getItemDetail, t.slot) local nbt2 = (ok and type(det) == "table") and det.nbt or nil local changed = (first[t.name] ~= nbt2) w(t.name .. ": nbt " .. (changed and "CHANGED (still simulating)" or "STABLE (idle/done)") .. " [" .. tostring(first[t.name]) .. " -> " .. tostring(nbt2) .. "]") end w("") w("Read the READ 1 dump above: if any field names the tier (tags/displayName/") w("damage/a 'self_aware' string) we can detect it directly. Otherwise the nbt") w("CHANGED/STABLE result is what the heuristic relies on.") out.close() print("") print("wrote sa_probe.txt")