From bd1766e448b149f7245c3d2db18af7ecc984f7ca Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Wed, 6 Aug 2014 22:54:17 -0400 Subject: Improve the LuaController Changes: * Stops code after a certain number of instructions. * Allows functions, due to instruction counting. * Allows loops and goto with non-JIT Lua (LuaJIT doesn't count looping as an instruction, allowing infinite loops), due to instruction counting. * Removes string matching functions as they can be slow. * Adds some safe functions. * Limits the amount of printing that can be done (to prevent console flooding). * Code cleanup. * More... --- mesecons/legacy.lua | 30 +- mesecons_luacontroller/init.lua | 761 +++++++++++++++++++++----------------- mesecons_microcontroller/init.lua | 4 +- 3 files changed, 432 insertions(+), 363 deletions(-) diff --git a/mesecons/legacy.lua b/mesecons/legacy.lua index c4334cf..119fa24 100644 --- a/mesecons/legacy.lua +++ b/mesecons/legacy.lua @@ -1,18 +1,13 @@ -minetest.swap_node = minetest.swap_node or function(pos, node) - local data = minetest.get_meta(pos):to_table() - minetest.add_node(pos, node) - minetest.get_meta(pos):from_table(data) -end - -local rules = {} -rules.a = {x = -1, y = 0, z = 0, name="A"} -rules.b = {x = 0, y = 0, z = 1, name="B"} -rules.c = {x = 1, y = 0, z = 0, name="C"} -rules.d = {x = 0, y = 0, z = -1, name="D"} +local rules = { + a = {x = -1, y = 0, z = 0, name="A"}, + b = {x = 0, y = 0, z = 1, name="B"}, + c = {x = 1, y = 0, z = 0, name="C"}, + d = {x = 0, y = 0, z = -1, name="D"}, +} -function legacy_update_ports(pos) +function mesecon.legacy_update_ports(pos) local meta = minetest.get_meta(pos) - L = { + local ports = { a = mesecon:is_power_on(mesecon:addPosRule(pos, rules.a), mesecon:invertRule(rules.a)) and mesecon:rules_link(mesecon:addPosRule(pos, rules.a), pos), @@ -26,7 +21,12 @@ function legacy_update_ports(pos) mesecon:invertRule(rules.d)) and mesecon:rules_link(mesecon:addPosRule(pos, rules.d), pos), } - local n = (L.a and 1 or 0) + (L.b and 2 or 0) + (L.c and 4 or 0) + (L.d and 8 or 0) + 1 + local n = + (ports.a and 1 or 0) + + (ports.b and 2 or 0) + + (ports.c and 4 or 0) + + (ports.d and 8 or 0) + 1 meta:set_int("real_portstates", n) - return L + return ports end + diff --git a/mesecons_luacontroller/init.lua b/mesecons_luacontroller/init.lua index f4869b5..f50db1a 100644 --- a/mesecons_luacontroller/init.lua +++ b/mesecons_luacontroller/init.lua @@ -1,11 +1,21 @@ +-- ______ +-- | +-- | +-- | __ ___ _ __ _ _ +-- | | | | | |\ | | |_| | | | | |_ |_| +-- |___| |______ |__| | \| | | \ |__| |_ |_ |_ |\ +-- | +-- | +-- + -- Reference --- ports = get_real_portstates(pos): gets if inputs are powered from outside --- newport = merge_portstates(state1, state2): just does result = state1 or state2 for every port --- action_setports(pos, rule, state): activates/deactivates the mesecons according to the portstates (helper for action) --- action(pos, ports): Applies new portstates to a luacontroller at pos --- lc_update(pos): updates the controller at pos by executing the code --- reset_meta (pos, code, errmsg): performs a software-reset, installs new code and prints error messages --- reset (pos): performs a hardware reset, turns off all ports +-- ports = get_real_port_states(pos): gets if inputs are powered from outside +-- newport = merge_port_states(state1, state2): just does result = state1 or state2 for every port +-- set_port(pos, rule, state): activates/deactivates the mesecons according to the port states +-- set_port_states(pos, ports): Applies new port states to a LuaController at pos +-- run(pos): runs the code in the controller at pos +-- reset_meta(pos, code, errmsg): performs a software-reset, installs new code and prints error messages +-- resetn(pos): performs a hardware reset, turns off all ports -- -- The Sandbox -- The whole code of the controller runs in a sandbox, @@ -20,70 +30,85 @@ local BASENAME = "mesecons_luacontroller:luacontroller" -local rules = {} -rules.a = {x = -1, y = 0, z = 0, name="A"} -rules.b = {x = 0, y = 0, z = 1, name="B"} -rules.c = {x = 1, y = 0, z = 0, name="C"} -rules.d = {x = 0, y = 0, z = -1, name="D"} +local rules = { + a = {x = -1, y = 0, z = 0, name="A"}, + b = {x = 0, y = 0, z = 1, name="B"}, + c = {x = 1, y = 0, z = 0, name="C"}, + d = {x = 0, y = 0, z = -1, name="D"}, +} + ------------------ -- Action stuff -- ------------------ --- These helpers are required to set the portstates of the luacontroller +-- These helpers are required to set the port states of the luacontroller -function lc_update_real_portstates(pos, rulename, newstate) +local function update_real_port_states(pos, rule_name, new_state) local meta = minetest.get_meta(pos) - if rulename == nil then + if rule_name == nil then meta:set_int("real_portstates", 1) return end local n = meta:get_int("real_portstates") - 1 if n < 0 then - legacy_update_ports(pos) + mesecon.legacy_update_ports(pos) n = meta:get_int("real_portstates") - 1 end + -- Create list of bytes in n local L = {} for i = 1, 4 do - L[i] = n%2 - n = math.floor(n/2) + L[i] = n % 2 + n = math.floor(n / 2) end - if rulename.x == nil then - for _, rname in ipairs(rulename) do - local port = ({4, 1, nil, 3, 2})[rname.x+2*rname.z+3] + -- (0,-1) (-1,0) (1,0) (0,1) + local pos_to_side = { 4, 1, nil, 3, 2 } + if rule_name.x == nil then + for _, rname in ipairs(rule_name) do + local port = pos_to_side[rname.x + (2 * rname.z) + 3] L[port] = (newstate == "on") and 1 or 0 end else - local port = ({4, 1, nil, 3, 2})[rulename.x+2*rulename.z+3] - L[port] = (newstate == "on") and 1 or 0 + local port = pos_to_side[rule_name.x + (2 * rule_name.z) + 3] + L[port] = (new_state == "on") and 1 or 0 end - meta:set_int("real_portstates", 1 + L[1] + 2*L[2] + 4*L[3] + 8*L[4]) + meta:set_int("real_portstates", + 1 + + 1 * L[1] + + 2 * L[2] + + 4 * L[3] + + 8 * L[4]) end -local get_real_portstates = function(pos) -- determine if ports are powered (by itself or from outside) + +local port_names = {"a", "b", "c", "d"} + +local function get_real_port_states(pos) + -- Determine if ports are powered (by itself or from outside) local meta = minetest.get_meta(pos) local L = {} local n = meta:get_int("real_portstates") - 1 if n < 0 then - return legacy_update_ports(pos) + return mesecon.legacy_update_ports(pos) end - for _, index in ipairs({"a", "b", "c", "d"}) do - L[index] = ((n%2) == 1) - n = math.floor(n/2) + for _, name in ipairs(port_names) do + L[name] = ((n % 2) == 1) + n = math.floor(n / 2) end return L end -local merge_portstates = function (ports, vports) - local npo = {a=false, b=false, c=false, d=false} - npo.a = vports.a or ports.a - npo.b = vports.b or ports.b - npo.c = vports.c or ports.c - npo.d = vports.d or ports.d - return npo + +local function merge_port_states(ports, vports) + return { + a = ports.a or vports.a, + b = ports.b or vports.b, + c = ports.c or vports.c, + d = ports.d or vports.d, + } end -local generate_name = function (ports) - local overwrite = overwrite or {} + +local function generate_name(ports) local d = ports.d and 1 or 0 local c = ports.c and 1 or 0 local b = ports.b and 1 or 0 @@ -91,7 +116,8 @@ local generate_name = function (ports) return BASENAME..d..c..b..a end -local setport = function (pos, rule, state) + +local function set_port(pos, rule, state) if state then mesecon:receptor_on(pos, {rule}) else @@ -99,93 +125,114 @@ local setport = function (pos, rule, state) end end -local action = function (pos, ports) + +local function clean_port_states(ports) + ports.a = ports.a and true or false + ports.b = ports.b and true or false + ports.c = ports.c and true or false + ports.d = ports.d and true or false +end + + +local function set_port_states(pos, ports) local node = minetest.get_node(pos) local name = node.name + clean_port_states(ports) local vports = minetest.registered_nodes[name].virtual_portstates - local newname = generate_name(ports) + local new_name = generate_name(ports) - if name ~= newname and vports then - local rules_on = {} - local rules_off = {} + if name ~= new_name and vports then + minetest.swap_node(pos, {name = new_name, param2 = node.param2}) - minetest.swap_node(pos, {name = newname, param2 = node.param2}) - - if ports.a ~= vports.a then setport(pos, rules.a, ports.a) end - if ports.b ~= vports.b then setport(pos, rules.b, ports.b) end - if ports.c ~= vports.c then setport(pos, rules.c, ports.c) end - if ports.d ~= vports.d then setport(pos, rules.d, ports.d) end + if ports.a ~= vports.a then set_port(pos, rules.a, ports.a) end + if ports.b ~= vports.b then set_port(pos, rules.b, ports.b) end + if ports.c ~= vports.c then set_port(pos, rules.c, ports.c) end + if ports.d ~= vports.d then set_port(pos, rules.d, ports.d) end end end --------------------- --- Overheat stuff -- --------------------- -local overheat_off = function(pos) +----------------- +-- Overheating -- +----------------- + +local function overheat_off(pos) mesecon:receptor_off(pos, mesecon.rules.flat) end -------------------- --- Parsing stuff -- -------------------- -local code_prohibited = function(code) - -- Clean code - local prohibited = {"while", "for", "repeat", "until", "function", "goto"} - for _, p in ipairs(prohibited) do - if string.find(code, p) then - return "Prohibited command: "..p - end +local function overheat(pos, meta) + if mesecon.do_overheat(pos) then -- If too hot + local node = minetest.get_node(pos) + node.name = BASENAME.."_burnt" + minetest.swap_node(pos, node) + -- Wait for pending operations + minetest.after(0.2, overheat_off, pos) + return true end end -local safe_print = function(param) - print(dump(param)) -end -deep_copy = function(original, visited) --deep copy that removes functions - visited = visited or {} - if visited[original] ~= nil then --already visited this node - return visited[original] +------------------------- +-- Parsing and running -- +------------------------- + +-- Limit printing to prevent flooding +local print_count = 0 + +local function safe_print(param) + local to_print = dump(param, "") + print_count = print_count + 1 + (#to_print / 64) + for c in to_print:gmatch("\n") do + print_count = print_count + 1 end - if type(original) == 'table' then --nested table - local copy = {} - visited[original] = copy - for key, value in next, original, nil do - copy[deep_copy(key, visited)] = deep_copy(value, visited) - end - setmetatable(copy, deep_copy(getmetatable(original), visited)) - return copy - elseif type(original) == 'function' then --ignore functions - return nil - else --by-value type - return original + if print_count > 8 then + error("Too much printing!") end + print(to_print) end -local safe_serialize = function(value) - return minetest.serialize(deep_copy(value)) +minetest.register_globalstep(function(dtime) + print_count = print_count - dtime +end) + + +local function remove_functions(x) + local tp = type(x) + if tp == "table" then + for key, value in pairs(x) do + local key_t, val_t = type(key), type(value) + if key_t == "function" or val_t == "function" then + x[key] = nil + else + if key_t == "table" then + remove_functions(key) + end + if val_t == "table" then + remove_functions(value) + end + end + end + elseif tp == "function" then + return nil + end + return x end -mesecon.queue:add_function("lc_interrupt", function (pos, iid, luac_id) - -- There is no luacontroller anymore / it has been reprogrammed / replaced - if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end - lc_update(pos, {type="interrupt", iid = iid}) -end) -local getinterrupt = function(pos) - local interrupt = function (time, iid) -- iid = interrupt id +local function get_interrupt(pos) + -- iid = interrupt id + local function interrupt(time, iid) if type(time) ~= "number" then return end - luac_id = minetest.get_meta(pos):get_int("luac_id") - mesecon.queue:add_action(pos, "lc_interrupt", {iid, luac_id}, time, iid, 1) + local luac_id = minetest.get_meta(pos):get_int("luac_id") + mesecon.queue:add_action(pos, "LuaController interrupt", {iid, luac_id}, time, iid, 1) end return interrupt end -local getdigiline_send = function(pos) + +local function get_digiline_send(pos) if not digiline then return end - -- Send messages on next serverstep return function(channel, msg) minetest.after(0, function() digiline:receptor_send(pos, digiline.rules.default, channel, msg) @@ -193,145 +240,180 @@ local getdigiline_send = function(pos) end end -local create_environment = function(pos, mem, event) + +local safe_globals = { + "assert", "error", "ipairs", "next", "pairs", "pcall", "select", + "tonumber", "tostring", "type", "unpack", "_VERSION", "xpcall", +} +local function create_environment(pos, mem, event) -- Gather variables for the environment local vports = minetest.registered_nodes[minetest.get_node(pos).name].virtual_portstates - vports = {a = vports.a, b = vports.b, c = vports.c, d = vports.d} - local rports = get_real_portstates(pos) - - return { - print = safe_print, - pin = merge_portstates(vports, rports), - port = vports, - interrupt = getinterrupt(pos), - digiline_send = getdigiline_send(pos), - mem = mem, - tostring = tostring, - tonumber = tonumber, - heat = minetest.get_meta(pos):get_int("heat"), - heat_max = OVERHEAT_MAX, - string = { - byte = string.byte, - char = string.char, - find = string.find, - format = string.format, - gmatch = string.gmatch, - gsub = string.gsub, - len = string.len, - lower = string.lower, - upper = string.upper, - match = string.match, - rep = string.rep, - reverse = string.reverse, - sub = string.sub, - }, - math = { - abs = math.abs, - acos = math.acos, - asin = math.asin, - atan = math.atan, - atan2 = math.atan2, - ceil = math.ceil, - cos = math.cos, - cosh = math.cosh, - deg = math.deg, - exp = math.exp, - floor = math.floor, - fmod = math.fmod, - frexp = math.frexp, - huge = math.huge, - ldexp = math.ldexp, - log = math.log, - log10 = math.log10, - max = math.max, - min = math.min, - modf = math.modf, - pi = math.pi, - pow = math.pow, - rad = math.rad, - random = math.random, - sin = math.sin, - sinh = math.sinh, - sqrt = math.sqrt, - tan = math.tan, - tanh = math.tanh, - }, - table = { - insert = table.insert, - maxn = table.maxn, - remove = table.remove, - sort = table.sort - }, - event = event, + local vports_copy = {} + for k, v in pairs(vports) do vports_copy[k] = v end + local rports = get_real_port_states(pos) + + -- Create new library tables on each call to prevent one LuaController + -- from breaking a library and messing up other LuaControllers. + local env = { + pin = merge_port_states(vports, rports), + port = vports_copy, + event = event, + mem = mem, + heat = minetest.get_meta(pos):get_int("heat"), + heat_max = OVERHEAT_MAX, + print = safe_print, + interrupt = get_interrupt(pos), + digiline_send = get_digiline_send(pos), + string = { + byte = string.byte, + char = string.char, + format = string.format, + gsub = string.gsub, + len = string.len, + lower = string.lower, + upper = string.upper, + rep = string.rep, + reverse = string.reverse, + sub = string.sub, + }, + math = { + abs = math.abs, + acos = math.acos, + asin = math.asin, + atan = math.atan, + atan2 = math.atan2, + ceil = math.ceil, + cos = math.cos, + cosh = math.cosh, + deg = math.deg, + exp = math.exp, + floor = math.floor, + fmod = math.fmod, + frexp = math.frexp, + huge = math.huge, + ldexp = math.ldexp, + log = math.log, + log10 = math.log10, + max = math.max, + min = math.min, + modf = math.modf, + pi = math.pi, + pow = math.pow, + rad = math.rad, + random = math.random, + sin = math.sin, + sinh = math.sinh, + sqrt = math.sqrt, + tan = math.tan, + tanh = math.tanh, + }, + table = { + concat = table.concat, + insert = table.insert, + maxn = table.maxn, + remove = table.remove, + sort = table.sort, + }, + os = { + clock = os.clock, + difftime = os.difftime, + time = os.time, + }, } + env._G = env + + for _, name in pairs(safe_globals) do + env[name] = _G[name] + end + + return env end -local create_sandbox = function (code, env) - -- Create Sandbox + +local function timeout() + debug.sethook() -- Clear hook + error("Code timed out!") +end + + +local function code_prohibited(code) + -- LuaJIT doesn't increment the instruction counter when running + -- loops, so we have to sanitize inputs if we're using LuaJIT. + if not jit then + return false + end + local prohibited = {"while", "for", "do", "repeat", "until", "goto"} + code = " "..code.." " + for _, p in ipairs(prohibited) do + if string.find(code, "[^%w_]"..p.."[^%w_]") then + return "Prohibited command: "..p + end + end +end + + +local function create_sandbox(code, env) if code:byte(1) == 27 then - return _, "You Hacker You! Don't use binary code!" + return nil, "Binary code prohibited." end - f, msg = loadstring(code) - if not f then return _, msg end + local f, msg = loadstring(code) + if not f then return nil, msg end setfenv(f, env) - return f -end -local lc_overheat = function (pos, meta) - if mesecon.do_overheat(pos) then -- if too hot - local node = minetest.get_node(pos) - minetest.swap_node(pos, {name = BASENAME.."_burnt", param2 = node.param2}) - minetest.after(0.2, overheat_off, pos) -- wait for pending operations - return true + return function(...) + debug.sethook(timeout, "", 10000) + local ok, ret = pcall(f, ...) + debug.sethook() -- Clear hook + if not ok then error(ret) end + return ret end end -local load_memory = function(meta) + +local function load_memory(meta) return minetest.deserialize(meta:get_string("lc_memory")) or {} end -local save_memory = function(meta, mem) - meta:set_string("lc_memory", safe_serialize(mem)) -end -local ports_invalid = function (var) - if type(var) == "table" then - return false - end - return "The ports you set are invalid" +local function save_memory(meta, mem) + meta:set_string("lc_memory", + minetest.serialize( + remove_functions(mem) + ) + ) end ----------------------- --- Parsing function -- ----------------------- -lc_update = function (pos, event) +local function run(pos, event) local meta = minetest.get_meta(pos) - if lc_overheat(pos) then return end + if overheat(pos) then return end - -- load code & mem from memory + -- Load code & mem from meta local mem = load_memory(meta) local code = meta:get_string("code") - -- make sure code is ok and create environment - local prohibited = code_prohibited(code) - if prohibited then return prohibited end + local err = code_prohibited(code) + if err then return err end + + -- Create environment local env = create_environment(pos, mem, event) - -- create the sandbox and execute code - local chunk, msg = create_sandbox (code, env) - if not chunk then return msg end + -- Create the sandbox and execute code + local f, msg = create_sandbox(code, env) + if not f then return msg end local success, msg = pcall(f) if not success then return msg end - if ports_invalid(env.port) then return ports_invalid(env.port) end + if type(env.port) ~= "table" then + return "Ports set are invalid." + end - save_memory(meta, mem) + save_memory(meta, env.mem) -- Actually set the ports - action(pos, env.port) + set_port_states(pos, env.port) end -local reset_meta = function(pos, code, errmsg) +local function reset_meta(pos, code, errmsg) local meta = minetest.get_meta(pos) meta:set_string("code", code) code = minetest.formspec_escape(code or "") @@ -343,173 +425,164 @@ local reset_meta = function(pos, code, errmsg) "image_button_exit[9.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]".. "label[0.1,5;"..errmsg.."]") meta:set_int("heat", 0) - meta:set_int("luac_id", math.random(1, 1000000)) + meta:set_int("luac_id", math.random(1, 65535)) end -local reset = function (pos) - action(pos, {a=false, b=false, c=false, d=false}) +local function reset(pos) + set_port_states(pos, {a=false, b=false, c=false, d=false}) end --- ______ --- | --- | --- | __ ___ _ __ _ _ --- | | | | | |\ | | |_| | | | | |_ |_| --- |___| |______ |__| | \| | | \ |__| |_ |_ |_ |\ --- | --- | --- + +mesecon.queue:add_function("LuaController interrupt", function(pos, iid, luac_id) + -- There is no LuaController anymore / it has been reprogrammed / replaced + if minetest.get_meta(pos):get_int("luac_id") ~= luac_id then + return + end + run(pos, {type="interrupt", iid = iid}) +end) + ----------------------- -- Node Registration -- ----------------------- -local output_rules={} -local input_rules={} +local output_rules = {} +local input_rules = {} -local nodebox = { - type = "fixed", - fixed = { - { -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- bottom slab - { -5/16, -7/16, -5/16, 5/16, -6/16, 5/16 }, -- circuit board - { -3/16, -6/16, -3/16, 3/16, -5/16, 3/16 }, -- IC - } +local node_box = { + type = "fixed", + fixed = { + {-8/16, -8/16, -8/16, 8/16, -7/16, 8/16}, -- Bottom slab + {-5/16, -7/16, -5/16, 5/16, -6/16, 5/16}, -- Circuit board + {-3/16, -6/16, -3/16, 3/16, -5/16, 3/16}, -- IC } +} -local selectionbox = { - type = "fixed", - fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 }, - } +local selection_box = { + type = "fixed", + fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 }, +} local digiline = { receptor = {}, effector = { - action = function (pos, node, channel, msg) - lc_update (pos, {type = "digiline", channel = channel, msg = msg}) + action = function(pos, node, channel, msg) + run(pos, {type = "digiline", channel = channel, msg = msg}) end } } +local function on_receive_fields(pos, form_name, fields) + if not fields.program then + return + end + reset(pos) + reset_meta(pos, fields.code) + local err = run(pos, {type="program"}) + if err then + print(err) + reset_meta(pos, fields.code, err) + end +end -for a = 0, 1 do -- 0 = off; 1 = on +for a = 0, 1 do -- 0 = off 1 = on for b = 0, 1 do for c = 0, 1 do for d = 0, 1 do + local cid = tostring(d)..tostring(c)..tostring(b)..tostring(a) + local node_name = BASENAME..cid + local top = "jeija_luacontroller_top.png" + if a == 1 then + top = top.."^jeija_luacontroller_LED_A.png" + end + if b == 1 then + top = top.."^jeija_luacontroller_LED_B.png" + end + if c == 1 then + top = top.."^jeija_luacontroller_LED_C.png" + end + if d == 1 then + top = top.."^jeija_luacontroller_LED_D.png" + end -local cid = tostring(d)..tostring(c)..tostring(b)..tostring(a) -local nodename = BASENAME..cid -local top = "jeija_luacontroller_top.png" -if a == 1 then - top = top.."^jeija_luacontroller_LED_A.png" -end -if b == 1 then - top = top.."^jeija_luacontroller_LED_B.png" -end -if c == 1 then - top = top.."^jeija_luacontroller_LED_C.png" -end -if d == 1 then - top = top.."^jeija_luacontroller_LED_D.png" -end - -if a + b + c + d ~= 0 then - groups = {dig_immediate=2, not_in_creative_inventory=1, overheat = 1} -else - groups = {dig_immediate=2, overheat = 1} -end + local groups + if a + b + c + d ~= 0 then + groups = {dig_immediate=2, not_in_creative_inventory=1, overheat = 1} + else + groups = {dig_immediate=2, overheat = 1} + end -output_rules[cid] = {} -input_rules[cid] = {} -if (a == 1) then table.insert(output_rules[cid], rules.a) end -if (b == 1) then table.insert(output_rules[cid], rules.b) end -if (c == 1) then table.insert(output_rules[cid], rules.c) end -if (d == 1) then table.insert(output_rules[cid], rules.d) end - -if (a == 0) then table.insert(input_rules[cid], rules.a) end -if (b == 0) then table.insert(input_rules[cid], rules.b) end -if (c == 0) then table.insert(input_rules[cid], rules.c) end -if (d == 0) then table.insert(input_rules[cid], rules.d) end - -local mesecons = { - effector = - { - rules = input_rules[cid], - action_change = function (pos, _, rulename, newstate) - lc_update_real_portstates(pos, rulename, newstate) - lc_update(pos, {type=newstate, pin=rulename}) - end, - }, - receptor = - { - state = mesecon.state.on, - rules = output_rules[cid] + output_rules[cid] = {} + input_rules[cid] = {} + if a == 1 then table.insert(output_rules[cid], rules.a) end + if b == 1 then table.insert(output_rules[cid], rules.b) end + if c == 1 then table.insert(output_rules[cid], rules.c) end + if d == 1 then table.insert(output_rules[cid], rules.d) end + + if a == 0 then table.insert( input_rules[cid], rules.a) end + if b == 0 then table.insert( input_rules[cid], rules.b) end + if c == 0 then table.insert( input_rules[cid], rules.c) end + if d == 0 then table.insert( input_rules[cid], rules.d) end + + local mesecons = { + effector = { + rules = input_rules[cid], + action_change = function (pos, _, rule_name, new_state) + update_real_port_states(pos, rule_name, new_state) + run(pos, {type=new_state, pin=rule_name}) + end, + }, + receptor = { + state = mesecon.state.on, + rules = output_rules[cid] + } } -} -minetest.register_node(nodename, { - description = "Luacontroller", - drawtype = "nodebox", - tiles = { - top, - "jeija_microcontroller_bottom.png", - "jeija_microcontroller_sides.png", - "jeija_microcontroller_sides.png", - "jeija_microcontroller_sides.png", - "jeija_microcontroller_sides.png" + minetest.register_node(node_name, { + description = "LuaController", + drawtype = "nodebox", + tiles = { + top, + "jeija_microcontroller_bottom.png", + "jeija_microcontroller_sides.png", + "jeija_microcontroller_sides.png", + "jeija_microcontroller_sides.png", + "jeija_microcontroller_sides.png" }, - - inventory_image = top, - paramtype = "light", - groups = groups, - drop = BASENAME.."0000", - sunlight_propagates = true, - selection_box = selectionbox, - node_box = nodebox, - on_construct = reset_meta, - on_receive_fields = function(pos, formname, fields) - if not fields.program then - return - end - reset(pos) - reset_meta(pos, fields.code) - local err = lc_update(pos, {type="program"}) - if err then - print(err) - reset_meta(pos, fields.code, err) - end - end, - on_timer = handle_timer, - sounds = default.node_sound_stone_defaults(), - mesecons = mesecons, - digiline = digiline, - virtual_portstates = { a = a == 1, -- virtual portstates are - b = b == 1, -- the ports the the - c = c == 1, -- controller powers itself - d = d == 1},-- so those that light up - after_dig_node = function (pos, node) - mesecon:receptor_off(pos, output_rules) - end, - is_luacontroller = true, -}) + inventory_image = top, + paramtype = "light", + groups = groups, + drop = BASENAME.."0000", + sunlight_propagates = true, + selection_box = selection_box, + node_box = node_box, + on_construct = reset_meta, + on_receive_fields = on_receive_fields, + on_timer = handle_timer, + sounds = default.node_sound_stone_defaults(), + mesecons = mesecons, + digiline = digiline, + -- Virtual portstates are the ports that + -- the node shows as powered up (light up). + virtual_portstates = { + a = a == 1, + b = b == 1, + c = c == 1, + d = d == 1, + }, + after_dig_node = function (pos, node) + mesecon:receptor_off(pos, output_rules) + end, + is_luacontroller = true, + }) end end end end ------------------------------ --- overheated luacontroller -- +-- Overheated LuaController -- ------------------------------ -local mesecons_burnt = { - effector = - { - rules = mesecon.rules.flat, - action_change = function (pos, _, rulename, newstate) - -- only update portstates when changes are triggered - lc_update_real_portstates(pos, rulename, newstate) - end - } -} - minetest.register_node(BASENAME .. "_burnt", { drawtype = "nodebox", tiles = { @@ -528,21 +601,17 @@ minetest.register_node(BASENAME .. "_burnt", { selection_box = selectionbox, node_box = nodebox, on_construct = reset_meta, - on_receive_fields = function(pos, formname, fields) - if fields.quit then - return - end - reset(pos) - reset_meta(pos, fields.code) - local err = lc_update(pos, {type="program"}) - if err then - print(err) - reset_meta(pos, fields.code, err) - end - end, + on_receive_fields = on_receive_fields, sounds = default.node_sound_stone_defaults(), virtual_portstates = {a = false, b = false, c = false, d = false}, - mesecons = mesecons_burnt, + mesecons = { + effector = { + rules = mesecon.rules.flat, + action_change = function(pos, _, rule_name, new_state) + update_real_portstates(pos, rule_name, new_state) + end, + }, + }, }) ------------------------ diff --git a/mesecons_microcontroller/init.lua b/mesecons_microcontroller/init.lua index 8c9f3b8..2cea23c 100644 --- a/mesecons_microcontroller/init.lua +++ b/mesecons_microcontroller/init.lua @@ -638,7 +638,7 @@ function yc_update_real_portstates(pos, node, rulename, newstate) end local n = meta:get_int("real_portstates") - 1 if n < 0 then - legacy_update_ports(pos) + mesecon.legacy_update_ports(pos) n = meta:get_int("real_portstates") - 1 end local L = {} @@ -663,7 +663,7 @@ function yc_get_real_portstates(pos) -- determine if ports are powered (by itsel local L = {} local n = meta:get_int("real_portstates") - 1 if n < 0 then - return legacy_update_ports(pos) + return mesecon.legacy_update_ports(pos) end for _, index in ipairs({"a", "b", "c", "d"}) do L[index] = ((n%2) == 1) -- cgit v1.2.3