1 Commits

Author SHA1 Message Date
SmallJoker
84cf0c4ff2 mesecons: Unify duplicated code
The new comments should help understanding what is going on
to ease maintenance in the long-run.

Co-authored-by: Jude Melton-Houghton <jwmhjwmh@gmail.com>
2025-11-10 19:50:54 +01:00
2 changed files with 53 additions and 117 deletions

View File

@@ -90,6 +90,16 @@ function mesecon.get_any_rules(node)
mesecon.get_any_outputrules(node))
end
local function get_rules_from_ndef(ndef, node)
local rules = ndef and ndef.rules
if type(rules) == 'function' then
return rules(node)
elseif rules then
return rules
end
return mesecon.rules.default
end
-- Receptors
-- Nodes that can power mesecons
function mesecon.is_receptor_on(nodename)
@@ -117,17 +127,7 @@ function mesecon.is_receptor(nodename)
end
function mesecon.receptor_get_rules(node)
local receptor = mesecon.get_receptor(node.name)
if receptor then
local rules = receptor.rules
if type(rules) == 'function' then
return rules(node)
elseif rules then
return rules
end
end
return mesecon.rules.default
return get_rules_from_ndef(mesecon.get_receptor(node.name), node)
end
-- Effectors
@@ -157,16 +157,7 @@ function mesecon.is_effector(nodename)
end
function mesecon.effector_get_rules(node)
local effector = mesecon.get_effector(node.name)
if effector then
local rules = effector.rules
if type(rules) == 'function' then
return rules(node)
elseif rules then
return rules
end
end
return mesecon.rules.default
return get_rules_from_ndef(mesecon.get_effector(node.name), node)
end
-- #######################
@@ -330,16 +321,7 @@ function mesecon.get_conductor_off(node_on, rulename)
end
function mesecon.conductor_get_rules(node)
local conductor = mesecon.get_conductor(node.name)
if conductor then
local rules = conductor.rules
if type(rules) == 'function' then
return rules(node)
elseif rules then
return rules
end
end
return mesecon.rules.default
return get_rules_from_ndef(mesecon.get_conductor(node.name), node)
end
-- some more general high-level stuff
@@ -545,46 +527,42 @@ function mesecon.turnoff(pos, link)
return true
end
-- @param getter_function Function that returns nested rules (vectors)
-- @return The matching rule or `nil`
local function find_rule_in_rules(pos, rule, getter_function)
local r_pos = vector.add(pos, rule)
local r_node = mesecon.get_node_force(r_pos)
local r_rules = getter_function(r_node)
if not r_rules then
return
end
local dir = vector.subtract(pos, r_pos)
for _, r_rule in ipairs(mesecon.flattenrules(r_rules)) do
if vector.equals(dir, r_rule) then
-- Other matches are duplicates
return r_rule
end
end
end
-- Get all linking inputrules of inputnode (effector or conductor) that is connected to
-- outputnode (receptor or conductor) at position `output` and has an output in direction `rule`
function mesecon.rules_link_rule_all(output, rule)
local input = vector.add(output, rule)
local inputnode = mesecon.get_node_force(input)
local inputrules = mesecon.get_any_inputrules(inputnode)
if not inputrules then
return {}
end
local rules = {}
for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do
-- Check if input accepts from output
if vector.equals(vector.add(input, inputrule), output) then
table.insert(rules, inputrule)
end
end
return rules
local match = find_rule_in_rules(output, rule, mesecon.get_any_inputrules)
return { match }
end
-- Get all linking outputnodes of outputnode (receptor or conductor) that is connected to
-- inputnode (effector or conductor) at position `input` and has an input in direction `rule`
function mesecon.rules_link_rule_all_inverted(input, rule)
local output = vector.add(input, rule)
local outputnode = mesecon.get_node_force(output)
local outputrules = mesecon.get_any_outputrules(outputnode)
if not outputrules then
return {}
end
local rules = {}
for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do
if vector.equals(vector.add(output, outputrule), input) then
table.insert(rules, mesecon.invertRule(outputrule))
end
end
return rules
local match = find_rule_in_rules(input, rule, mesecon.get_any_outputrules)
return { match and mesecon.invertRule(match) }
end
-- @param pos Node position (a vector) to check
-- @param rule Optional. Specific rule (i.e. node side) to check.
-- @return Returns a list of vectors that power `pos`, or `false` if unpowered.
function mesecon.is_powered(pos, rule)
local node = mesecon.get_node_force(pos)
local rules = mesecon.get_any_inputrules(node)
@@ -593,26 +571,15 @@ function mesecon.is_powered(pos, rule)
-- List of nodes that send out power to pos
local sourcepos = {}
if not rule then
for _, rule in ipairs(mesecon.flattenrules(rules)) do
local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
for _, rname in ipairs(rulenames) do
local np = vector.add(pos, rname)
local nn = mesecon.get_node_force(np)
if (mesecon.is_conductor_on(nn, mesecon.invertRule(rname))
or mesecon.is_receptor_on(nn.name)) then
table.insert(sourcepos, np)
end
end
end
else
local rules_flat = rule and { rule } or mesecon.flattenrules(rules)
for _, rule in ipairs(rules_flat) do
local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
for _, rname in ipairs(rulenames) do
local np = vector.add(pos, rname)
local nn = mesecon.get_node_force(np)
if (mesecon.is_conductor_on (nn, mesecon.invertRule(rname))
or mesecon.is_receptor_on (nn.name)) then
if (mesecon.is_conductor_on(nn, mesecon.invertRule(rname))
or mesecon.is_receptor_on(nn.name)) then
table.insert(sourcepos, np)
end
end

View File

@@ -12,12 +12,12 @@
-- 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, ignore_overheat): Applies new port states to a Luacontroller at pos
-- set_port_states(pos, ports): Applies new port states to a Luacontroller at pos
-- run_inner(pos, code, event): runs code on the controller at pos and event
-- reset_formspec(pos, code, errmsg): installs new code and prints error messages, without resetting LCID
-- reset_meta(pos, code, errmsg): performs a software-reset, installs new code and prints error message
-- run(pos, event): a wrapper for run_inner which gets code & handles errors via reset_meta
-- reset(pos): performs a hardware reset, turns off all ports
-- resetn(pos): performs a hardware reset, turns off all ports
--
-- The Sandbox
-- The whole code of the controller runs in a sandbox,
@@ -129,15 +129,10 @@ local function clean_port_states(ports)
ports.d = ports.d and true or false
end
local is_controller_burnt
local function set_port_states(pos, ports, ignore_overheat)
local function set_port_states(pos, ports)
local node = minetest.get_node(pos)
local name = node.name
if not ignore_overheat and is_controller_burnt(name) then
return -- Avoid swapping back to a non-burnt node
end
clean_port_states(ports)
local vports = minetest.registered_nodes[name].virtual_portstates
local new_name = generate_name(ports)
@@ -173,16 +168,6 @@ end
-----------------
-- Overheating --
-----------------
-- Overheating factor of deferred tasks (e.g. digilines).
-- Higher values result in faster overheating.
-- See also: settings 'overheat_max' and 'cooldown_time'
local TASK_HEAT_FACTOR = 0.8
is_controller_burnt = function(node_name)
return node_name == (BASENAME .. "_burnt")
end
local function burn_controller(pos)
local node = minetest.get_node(pos)
node.name = BASENAME.."_burnt"
@@ -193,10 +178,6 @@ local function burn_controller(pos)
end
local function overheat(pos)
if is_controller_burnt(core.get_node(pos).name) then
-- Avoid spamming "Node overheats" log messages.
return true
end
if mesecon.do_overheat(pos) then -- If too hot
burn_controller(pos)
return true
@@ -469,10 +450,6 @@ local function get_digiline_send(pos, itbl, send_warning)
local chan_maxlen = mesecon.setting("luacontroller_digiline_channel_maxlen", 256)
local maxlen = mesecon.setting("luacontroller_digiline_maxlen", 50000)
return function(channel, msg)
if is_controller_burnt(pos) then
return false
end
-- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y
-- or via anything that could.
-- Make sure channel is string, number or boolean
@@ -700,13 +677,6 @@ local function run_inner(pos, code, event)
-- Save memory. This may burn the luacontroller if a memory overflow occurs.
save_memory(pos, meta, env.mem)
-- Action queues can escape the sandbox, thus give a harsh penalty.
for i = 1, math.floor(#itbl * TASK_HEAT_FACTOR + 0.5) do
if overheat(pos) then
break
end
end
-- Execute deferred tasks
for _, v in ipairs(itbl) do
local failure = v()
@@ -714,15 +684,13 @@ local function run_inner(pos, code, event)
return false, failure
end
end
return true, warning
end
local function reset_formspec(meta, code, errmsg)
code = code or ""
meta:set_string("code", code)
meta:mark_as_private("code")
code = minetest.formspec_escape(code)
code = minetest.formspec_escape(code or "")
errmsg = minetest.formspec_escape(tostring(errmsg or ""))
meta:set_string("formspec", "size[12,10]"
.."style_type[label,textarea;font=mono]"
@@ -754,11 +722,11 @@ local function run(pos, event)
end
local function reset(pos)
set_port_states(pos, {a=false, b=false, c=false, d=false}, true)
set_port_states(pos, {a=false, b=false, c=false, d=false})
end
local function node_timer(pos)
if is_controller_burnt(core.get_node(pos).name) then
if minetest.registered_nodes[minetest.get_node(pos).name].is_burnt then
return false
end
run(pos, {type="interrupt"})
@@ -772,7 +740,7 @@ end
mesecon.queue:add_function("lc_interrupt", function (pos, luac_id, iid)
-- There is no luacontroller anymore / it has been reprogrammed / replaced / burnt
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end
if is_controller_burnt(core.get_node(pos).name) then return end
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end
run(pos, {type="interrupt", iid = iid})
end)
@@ -780,7 +748,7 @@ mesecon.queue:add_function("lc_digiline_relay", function (pos, channel, luac_id,
if not digiline then return end
-- This check is only really necessary because in case of server crash, old actions can be thrown into the future
if (minetest.get_meta(pos):get_int("luac_id") ~= luac_id) then return end
if is_controller_burnt(core.get_node(pos).name) then return end
if (minetest.registered_nodes[minetest.get_node(pos).name].is_burnt) then return end
-- The actual work
digiline:receptor_send(pos, digiline.rules.default, channel, msg)
end)
@@ -961,6 +929,7 @@ minetest.register_node(BASENAME .. "_burnt", {
"jeija_microcontroller_sides.png"
},
inventory_image = "jeija_luacontroller_burnt_top.png",
is_burnt = true,
paramtype = "light",
is_ground_content = false,
groups = {dig_immediate=2, not_in_creative_inventory=1},