summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesecons/actionqueue.lua120
-rw-r--r--mesecons/init.lua86
-rw-r--r--mesecons/internal.lua293
-rw-r--r--mesecons/legacy.lua29
-rw-r--r--mesecons/services.lua11
-rw-r--r--mesecons/settings.lua2
-rw-r--r--mesecons/util.lua12
-rw-r--r--mesecons_delayer/init.lua14
-rw-r--r--mesecons_detector/init.lua3
-rw-r--r--mesecons_gates/init.lua3
-rw-r--r--mesecons_luacontroller/init.lua131
-rw-r--r--mesecons_microcontroller/init.lua61
12 files changed, 472 insertions, 293 deletions
diff --git a/mesecons/actionqueue.lua b/mesecons/actionqueue.lua
new file mode 100644
index 0000000..f9d197a
--- /dev/null
+++ b/mesecons/actionqueue.lua
@@ -0,0 +1,120 @@
+mesecon.queue.actions={} -- contains all ActionQueue actions
+
+function mesecon.queue:add_function(name, func)
+ mesecon.queue.funcs[name] = func
+end
+
+-- If add_action with twice the same overwritecheck and same position are called, the first one is overwritten
+-- use overwritecheck nil to never overwrite, but just add the event to the queue
+-- priority specifies the order actions are executed within one globalstep, highest first
+-- should be between 0 and 1
+function mesecon.queue:add_action(pos, func, params, time, overwritecheck, priority)
+ -- Create Action Table:
+ time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution
+ priority = priority or 1
+ local action = { pos=mesecon:tablecopy(pos),
+ func=func,
+ params=mesecon:tablecopy(params),
+ time=time,
+ owcheck=(overwritecheck and mesecon:tablecopy(overwritecheck)) or nil,
+ priority=priority}
+
+ -- if not using the queue, (MESECONS_GLOBALSTEP off), just execute the function an we're done
+ if not MESECONS_GLOBALSTEP and action.time == 0 then
+ mesecon.queue:execute(action)
+ return
+ end
+
+ local toremove = nil
+ -- Otherwise, add the action to the queue
+ if overwritecheck then -- check if old action has to be overwritten / removed:
+ for i, ac in ipairs(mesecon.queue.actions) do
+ if(mesecon:cmpPos(pos, ac.pos)
+ and mesecon:cmpAny(overwritecheck, ac.owcheck)) then
+ toremove = i
+ break
+ end
+ end
+ end
+
+ if (toremove ~= nil) then
+ table.remove(mesecon.queue.actions, toremove)
+ end
+
+ table.insert(mesecon.queue.actions, action)
+end
+
+-- execute the stored functions on a globalstep
+-- if however, the pos of a function is not loaded (get_node_or_nil == nil), do NOT execute the function
+-- this makes sure that resuming mesecons circuits when restarting minetest works fine
+-- However, even that does not work in some cases, that's why we delay the time the globalsteps
+-- start to be execute by 5 seconds
+local get_highest_priority = function (actions)
+ local highestp = -1, highesti
+ for i, ac in ipairs(actions) do
+ if ac.priority > highestp then
+ highestp = ac.priority
+ highesti = i
+ end
+ end
+
+ return highesti
+end
+
+local m_time = 0
+minetest.register_globalstep(function (dtime)
+ m_time = m_time + dtime
+ if (m_time < MESECONS_RESUMETIME) then return end -- don't even try if server has not been running for XY seconds
+ local actions = mesecon:tablecopy(mesecon.queue.actions)
+ local actions_now={}
+
+ mesecon.queue.actions = {}
+
+ -- sort actions into two categories:
+ -- those toexecute now (actions_now) and those to execute later (mesecon.queue.actions)
+ for i, ac in ipairs(actions) do
+ if ac.time > 0 then
+ ac.time = ac.time - dtime -- executed later
+ table.insert(mesecon.queue.actions, ac)
+ else
+ table.insert(actions_now, ac)
+ end
+ end
+
+ while(#actions_now > 0) do -- execute highest priorities first, until all are executed
+ local hp = get_highest_priority(actions_now)
+ mesecon.queue:execute(actions_now[hp])
+ table.remove(actions_now, hp)
+ end
+end)
+
+function mesecon.queue:execute(action)
+ mesecon.queue.funcs[action.func](action.pos, unpack(action.params))
+end
+
+
+-- Store and read the ActionQueue to / from a file
+-- so that upcoming actions are remembered when the game
+-- is restarted
+
+local wpath = minetest.get_worldpath()
+local function file2table(filename)
+ local f = io.open(filename, "r")
+ if f==nil then return {} end
+ local t = f:read("*all")
+ f:close()
+ if t=="" or t==nil then return {} end
+ return minetest.deserialize(t)
+end
+
+local function table2file(filename, table)
+ local f = io.open(filename, "w")
+ f:write(minetest.serialize(table))
+ f:close()
+end
+
+mesecon.queue.actions = file2table(wpath.."/mesecon_actionqueue")
+
+minetest.register_on_shutdown(function()
+ mesecon.queue.actions = table2file(wpath.."/mesecon_actionqueue", mesecon.queue.actions)
+end)
diff --git a/mesecons/init.lua b/mesecons/init.lua
index 640af4d..b5cf68b 100644
--- a/mesecons/init.lua
+++ b/mesecons/init.lua
@@ -42,37 +42,8 @@
-- PUBLIC VARIABLES
mesecon={} -- contains all functions and all global variables
-mesecon.actions_on={} -- Saves registered function callbacks for mesecon on | DEPRECATED
-mesecon.actions_off={} -- Saves registered function callbacks for mesecon off | DEPRECATED
-mesecon.actions_change={} -- Saves registered function callbacks for mesecon change | DEPRECATED
-mesecon.receptors={} -- saves all information about receptors | DEPRECATED
-mesecon.effectors={} -- saves all information about effectors | DEPRECATED
-mesecon.conductors={} -- saves all information about conductors | DEPRECATED
-
-
-local wpath = minetest.get_worldpath()
-local function read_file(fn)
- local f = io.open(fn, "r")
- if f==nil then return {} end
- local t = f:read("*all")
- f:close()
- if t=="" or t==nil then return {} end
- return minetest.deserialize(t)
-end
-
-local function write_file(fn, tbl)
- local f = io.open(fn, "w")
- f:write(minetest.serialize(tbl))
- f:close()
-end
-
-mesecon.to_update = read_file(wpath.."/mesecon_to_update")
-mesecon.r_to_update = read_file(wpath.."/mesecon_r_to_update")
-
-minetest.register_on_shutdown(function()
- write_file(wpath.."/mesecon_to_update",mesecon.to_update)
- write_file(wpath.."/mesecon_r_to_update",mesecon.r_to_update)
-end)
+mesecon.queue={} -- contains the ActionQueue
+mesecon.queue.funcs={} -- contains all ActionQueue functions
-- Settings
dofile(minetest.get_modpath("mesecons").."/settings.lua")
@@ -86,6 +57,10 @@ dofile(minetest.get_modpath("mesecons").."/presets.lua");
-- mostly things that make the source look cleaner
dofile(minetest.get_modpath("mesecons").."/util.lua");
+-- The ActionQueue
+-- Saves all the actions that have to be execute in the future
+dofile(minetest.get_modpath("mesecons").."/actionqueue.lua");
+
-- Internal stuff
-- This is the most important file
-- it handles signal transmission and basically everything else
@@ -101,9 +76,22 @@ dofile(minetest.get_modpath("mesecons").."/legacy.lua");
-- API
-- these are the only functions you need to remember
-function mesecon:receptor_on_i(pos, rules)
+mesecon.queue:add_function("receptor_on", function (pos, rules)
rules = rules or mesecon.rules.default
+ -- if area (any of the rule targets) is not loaded, keep trying and call this again later
+ if MESECONS_GLOBALSTEP then -- trying to enable resuming with globalstep disabled would cause an endless loop
+ for _, rule in ipairs(mesecon:flattenrules(rules)) do
+ local np = mesecon:addPosRule(pos, rule)
+ -- if area is not loaded, keep trying
+ if minetest.get_node_or_nil(np) == nil then
+ mesecon.queue:add_action(pos, "receptor_on", {rules}, nil, rules)
+ return
+ end
+ end
+ end
+
+ -- execute action
for _, rule in ipairs(mesecon:flattenrules(rules)) do
local np = mesecon:addPosRule(pos, rule)
local rulenames = mesecon:rules_link_rule_all(pos, rule)
@@ -111,19 +99,26 @@ function mesecon:receptor_on_i(pos, rules)
mesecon:turnon(np, rulename)
end
end
-end
+end)
function mesecon:receptor_on(pos, rules)
- if MESECONS_GLOBALSTEP then
- rules = rules or mesecon.rules.default
- mesecon.r_to_update[#mesecon.r_to_update+1]={pos=pos, rules=rules, action="on"}
- else
- mesecon:receptor_on_i(pos, rules)
- end
+ mesecon.queue:add_action(pos, "receptor_on", {rules}, nil, rules)
end
-function mesecon:receptor_off_i(pos, rules)
+mesecon.queue:add_function("receptor_off", function (pos, rules)
rules = rules or mesecon.rules.default
+
+ -- if area (any of the rule targets) is not loaded, keep trying and call this again later
+ if MESECONS_GLOBALSTEP then
+ for _, rule in ipairs(mesecon:flattenrules(rules)) do
+ local np = mesecon:addPosRule(pos, rule)
+ if minetest.get_node_or_nil(np) == nil then
+ mesecon.queue:add_action(pos, "receptor_off", {rules}, nil, rules)
+ return
+ end
+ end
+ end
+
for _, rule in ipairs(mesecon:flattenrules(rules)) do
local np = mesecon:addPosRule(pos, rule)
local rulenames = mesecon:rules_link_rule_all(pos, rule)
@@ -131,19 +126,14 @@ function mesecon:receptor_off_i(pos, rules)
if not mesecon:connected_to_receptor(np, mesecon:invertRule(rule)) then
mesecon:turnoff(np, rulename)
else
- mesecon:changesignal(np, minetest.get_node(np), rulename, mesecon.state.off)
+ mesecon:changesignal(np, minetest.get_node(np), rulename, mesecon.state.off, 2)
end
end
end
-end
+end)
function mesecon:receptor_off(pos, rules)
- if MESECONS_GLOBALSTEP then
- rules = rules or mesecon.rules.default
- mesecon.r_to_update[#mesecon.r_to_update+1]={pos=pos, rules=rules, action="off"}
- else
- mesecon:receptor_off_i(pos, rules)
- end
+ mesecon.queue:add_action(pos, "receptor_off", {rules}, nil, rules)
end
diff --git a/mesecons/internal.lua b/mesecons/internal.lua
index cb77f5d..06fbdff 100644
--- a/mesecons/internal.lua
+++ b/mesecons/internal.lua
@@ -22,9 +22,9 @@
-- mesecon:effector_get_rules(node) --> Returns the input rules of the effector (mesecon.rules.default if none specified)
-- SIGNALS
--- mesecon:activate(pos, node) --> Activates the effector node at the specific pos (calls nodedef.mesecons.effector.action_on)
--- mesecon:deactivate(pos, node) --> Deactivates the effector node at the specific pos (calls nodedef.mesecons.effector.action_off)
--- mesecon:changesignal(pos, node, rulename, newstate) --> Changes the effector node at the specific pos (calls nodedef.mesecons.effector.action_change)
+-- mesecon:activate(pos, node, recdepth) --> Activates the effector node at the specific pos (calls nodedef.mesecons.effector.action_on), higher recdepths are executed later
+-- mesecon:deactivate(pos, node, recdepth) --> Deactivates the effector node at the specific pos (calls nodedef.mesecons.effector.action_off), "
+-- mesecon:changesignal(pos, node, rulename, newstate) --> Changes the effector node at the specific pos (calls nodedef.mesecons.effector.action_change), "
-- RULES
-- mesecon:add_rules(name, rules) | deprecated? --> Saves rules table by name
@@ -41,8 +41,8 @@
-- HIGH-LEVEL Internals
-- mesecon:is_power_on(pos) --> Returns true if pos emits power in any way
-- mesecon:is_power_off(pos) --> Returns true if pos does not emit power in any way
--- mesecon:turnon(pos, rulename) --> Returns true whatever there is at pos. Calls itself for connected nodes (if pos is a conductor) --> recursive, the rulename is the name of the input rule that caused calling turnon
--- mesecon:turnoff(pos, rulename) --> Turns off whatever there is at pos. Calls itself for connected nodes (if pos is a conductor) --> recursive, the rulename is the name of the input rule that caused calling turnoff
+-- mesecon:turnon(pos, rulename) --> Returns true whatever there is at pos. Calls itself for connected nodes (if pos is a conductor) --> recursive, the rulename is the name of the input rule that caused calling turnon; Uses third parameter recdepth internally to determine how far away the current node is from the initial pos as it uses recursion
+-- mesecon:turnoff(pos, rulename) --> Turns off whatever there is at pos. Calls itself for connected nodes (if pos is a conductor) --> recursive, the rulename is the name of the input rule that caused calling turnoff; Uses third parameter recdepth internally to determine how far away the current node is from the initial pos as it uses recursion
-- mesecon:connected_to_receptor(pos) --> Returns true if pos is connected to a receptor directly or via conductors; calls itself if pos is a conductor --> recursive
-- mesecon:rules_link(output, input, dug_outputrules) --> Returns true if outputposition + outputrules = inputposition and inputposition + inputrules = outputposition (if the two positions connect)
-- mesecon:rules_link_anydir(outp., inp., d_outpr.) --> Same as rules mesecon:rules_link but also returns true if output and input are swapped
@@ -177,121 +177,76 @@ function mesecon:effector_get_rules(node)
return mesecon.rules.default
end
---Signals
+-- #######################
+-- # Signals (effectors) #
+-- #######################
-function mesecon:activate(pos, node, rulename)
- if MESECONS_GLOBALSTEP then
- if rulename == nil then
- for _,rule in ipairs(mesecon:effector_get_rules(node)) do
- mesecon:activate(pos, node, rule)
- end
- return
- end
- add_action(pos, "on", rulename)
- else
- local effector = mesecon:get_effector(node.name)
- if effector and effector.action_on then
- effector.action_on (pos, node, rulename)
- end
- end
-end
+-- Activation:
+mesecon.queue:add_function("activate", function (pos, rulename)
+ node = minetest.get_node(pos)
+ effector = mesecon:get_effector(node.name)
-function mesecon:deactivate(pos, node, rulename)
- if MESECONS_GLOBALSTEP then
- if rulename == nil then
- for _,rule in ipairs(mesecon:effector_get_rules(node)) do
- mesecon:deactivate(pos, node, rule)
- end
- return
- end
- add_action(pos, "off", rulename)
- else
- local effector = mesecon:get_effector(node.name)
- if effector and effector.action_off then
- effector.action_off (pos, node, rulename)
- end
+ if effector and effector.action_on then
+ effector.action_on(pos, node, rulename)
end
-end
+end)
-function mesecon:changesignal(pos, node, rulename, newstate)
-
- newstate = newstate or "on"
- --rulename = rulename or mesecon.rules.default
- if MESECONS_GLOBALSTEP then
- if rulename == nil then
- for _,rule in ipairs(mesecon:effector_get_rules(node)) do
- mesecon:changesignal(pos, node, rule, newstate)
- end
- return
- end
- add_action(pos, "c"..newstate, rulename)
- else
- local effector = mesecon:get_effector(node.name)
- if effector and effector.action_change then
- effector.action_change (pos, node, rulename, newstate)
+function mesecon:activate(pos, node, rulename, recdepth)
+ if rulename == nil then
+ for _,rule in ipairs(mesecon:effector_get_rules(node)) do
+ mesecon:activate(pos, node, rule, recdepth + 1)
end
+ return
end
+ mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename, 1 / recdepth)
end
-function execute_actions(dtime)
- local nactions = mesecon.to_update
- mesecon.to_update = {}
- for _,i in ipairs(nactions) do
- node = minetest.get_node(i.pos)
- if node.name=="ignore" then
- add_action(i.pos, i.action, i.rname)
- else
- effector = mesecon:get_effector(node.name)
- if i.action == "on" then
- if effector and effector.action_on then
- effector.action_on(i.pos, node, i.rname)
- end
- elseif i.action == "off" then
- if effector and effector.action_off then
- effector.action_off(i.pos, node, i.rname)
- end
- elseif i.action == "con" then
- if effector and effector.action_change then
- effector.action_change(i.pos, node, i.rname, "on")
- end
- elseif i.action == "coff" then
- if effector and effector.action_change then
- effector.action_change(i.pos, node, i.rname, "off")
- end
- end
- end
+
+-- Deactivation
+mesecon.queue:add_function("deactivate", function (pos, rulename)
+ node = minetest.get_node(pos)
+ effector = mesecon:get_effector(node.name)
+
+ if effector and effector.action_off then
+ effector.action_off(pos, node, rulename)
end
- local nactions = mesecon.r_to_update
- mesecon.r_to_update = {}
- for _,i in ipairs(nactions) do
- if i.action == "on" then
- mesecon:receptor_on_i(i.pos, i.rules)
- else
- mesecon:receptor_off_i(i.pos,i.rules)
+end)
+
+function mesecon:deactivate(pos, node, rulename, recdepth)
+ if rulename == nil then
+ for _,rule in ipairs(mesecon:effector_get_rules(node)) do
+ mesecon:deactivate(pos, node, rule, recdepth + 1)
end
+ return
end
+ mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename, 1 / recdepth)
end
-minetest.register_globalstep(execute_actions)
-function add_action(pos, action, rname)
- for _,i in ipairs(mesecon.to_update) do
- if i.pos.x == pos.x and i.pos.y == pos.y and i.pos.z == pos.z and i.rname.x == rname.x and i.rname.y == rname.y and i.rname.z == rname.z then
- if (i.action == "on" and action == "on") or (i.action == "off" and action == "off") then
- --nothing
- elseif i.action == "coff" and action == "on" then i.action = "on"
- elseif i.action == "con" and action == "off" then i.action = "off"
- else
- if action == "on" or action == "con" then i.action = "con" end
- if action == "off" or action == "coff" then i.action = "coff" end
- end
- break
+-- Change
+mesecon.queue:add_function("change", function (pos, rulename, changetype)
+ node = minetest.get_node(pos)
+ effector = mesecon:get_effector(node.name)
+
+ if effector and effector.action_change then
+ effector.action_change(pos, node, rulename, changetype)
+ end
+end)
+
+function mesecon:changesignal(pos, node, rulename, newstate, recdepth)
+ if rulename == nil then
+ for _,rule in ipairs(mesecon:effector_get_rules(node)) do
+ mesecon:changesignal(pos, node, rule, newstate, recdepth + 1)
end
+ return
end
- mesecon.to_update[#mesecon.to_update+1] = {pos = pos, action = action, rname = rname}
+
+ mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, rulename, 1 / recdepth)
end
---Rules
+-- #########
+-- # Rules # "Database" for rulenames
+-- #########
function mesecon:add_rules(name, rules)
mesecon.rules[name] = rules
@@ -410,8 +365,15 @@ function mesecon:is_power_off(pos, rulename)
return false
end
-function mesecon:turnon(pos, rulename)
+function mesecon:turnon(pos, rulename, recdepth)
+ recdepth = recdepth or 2
local node = minetest.get_node(pos)
+
+ if(node.name == "ignore") then
+ -- try turning on later again
+ mesecon.queue:add_action(
+ pos, "turnon", {rulename, recdepth + 1}, nil, true)
+ end
if mesecon:is_conductor_off(node, rulename) then
local rules = mesecon:conductor_get_rules(node)
@@ -419,7 +381,7 @@ function mesecon:turnon(pos, rulename)
if not rulename then
for _, rule in ipairs(mesecon:flattenrules(rules)) do
if mesecon:connected_to_receptor(pos, rule) then
- mesecon:turnon(pos, rule)
+ mesecon:turnon(pos, rule, recdepth + 1)
end
end
return
@@ -429,54 +391,75 @@ function mesecon:turnon(pos, rulename)
for _, rule in ipairs(mesecon:rule2meta(rulename, rules)) do
local np = mesecon:addPosRule(pos, rule)
- local rulenames = mesecon:rules_link_rule_all(pos, rule)
+ if(minetest.get_node(np).name == "ignore") then
+ -- try turning on later again
+ mesecon.queue:add_action(
+ np, "turnon", {rulename, recdepth + 1}, nil, true)
+ else
+ local rulenames = mesecon:rules_link_rule_all(pos, rule)
- for _, rulename in ipairs(rulenames) do
- mesecon:turnon(np, rulename)
+ for _, rulename in ipairs(rulenames) do
+ mesecon:turnon(np, rulename, recdepth + 1)
+ end
end
end
elseif mesecon:is_effector(node.name) then
- mesecon:changesignal(pos, node, rulename, mesecon.state.on)
+ mesecon:changesignal(pos, node, rulename, mesecon.state.on, recdepth)
if mesecon:is_effector_off(node.name) then
- mesecon:activate(pos, node, rulename)
+ mesecon:activate(pos, node, rulename, recdepth)
end
end
end
-function mesecon:turnoff(pos, rulename)
+mesecon.queue:add_function("turnon", function (pos, rulename, recdepth)
+ if (MESECONS_GLOBALSTEP) then -- do not resume if we don't use globalstep - that would cause an endless loop
+ mesecon:turnon(pos, rulename, recdepth)
+ end
+end)
+
+function mesecon:turnoff(pos, rulename, recdepth)
+ recdepth = recdepth or 2
local node = minetest.get_node(pos)
+ if(node.name == "ignore") then
+ -- try turning on later again
+ mesecon.queue:add_action(
+ pos, "turnoff", {rulename, recdepth + 1}, nil, true)
+ end
+
if mesecon:is_conductor_on(node, rulename) then
local rules = mesecon:conductor_get_rules(node)
- --[[
- if not rulename then
- for _, rule in ipairs(mesecon:flattenrules(rules)) do
- if mesecon:is_powered(pos, rule) then
- mesecon:turnoff(pos, rule)
- end
- end
- return
- end
- --]]
minetest.swap_node(pos, {name = mesecon:get_conductor_off(node, rulename), param2 = node.param2})
for _, rule in ipairs(mesecon:rule2meta(rulename, rules)) do
local np = mesecon:addPosRule(pos, rule)
- local rulenames = mesecon:rules_link_rule_all(pos, rule)
+ if(minetest.get_node(np).name == "ignore") then
+ -- try turning on later again
+ mesecon.queue:add_action(
+ np, "turnoff", {rulename, recdepth + 1}, nil, true)
+ else
+ local rulenames = mesecon:rules_link_rule_all(pos, rule)
- for _, rulename in ipairs(rulenames) do
- mesecon:turnoff(np, rulename)
+ for _, rulename in ipairs(rulenames) do
+ mesecon:turnoff(np, rulename, recdepth + 1)
+ end
end
end
elseif mesecon:is_effector(node.name) then
- mesecon:changesignal(pos, node, rulename, mesecon.state.off)
+ mesecon:changesignal(pos, node, rulename, mesecon.state.off, recdepth)
if mesecon:is_effector_on(node.name)
and not mesecon:is_powered(pos) then
- mesecon:deactivate(pos, node, rulename)
+ mesecon:deactivate(pos, node, rulename, recdepth + 1)
end
end
end
+mesecon.queue:add_function("turnoff", function (pos, rulename, recdepth)
+ if (MESECONS_GLOBALSTEP) then -- do not resume if we don't use globalstep - that would cause an endless loop
+ mesecon:turnoff(pos, rulename, recdepth)
+ end
+end)
+
function mesecon:connected_to_receptor(pos, rulename)
local node = minetest.get_node(pos)
@@ -486,9 +469,10 @@ function mesecon:connected_to_receptor(pos, rulename)
if not rules then return false end
for _, rule in ipairs(mesecon:rule2meta(rulename, rules)) do
- local np = mesecon:addPosRule(pos, rule)
- if mesecon:rules_link(np, pos) then
- if mesecon:find_receptor_on(np, {}, mesecon:invertRule(rule)) then
+ local rulenames = mesecon:rules_link_rule_all_inverted(pos, rule)
+ for _, rname in ipairs(rulenames) do
+ local np = mesecon:addPosRule(pos, rname)
+ if mesecon:find_receptor_on(np, {}, mesecon:invertRule(rname)) then
return true
end
end
@@ -518,9 +502,10 @@ function mesecon:find_receptor_on(pos, checked, rulename)
-- add current position to checked
table.insert(checked, {x=pos.x, y=pos.y, z=pos.z, metaindex = metaindex})
for _, rule in ipairs(mesecon:rule2meta(rulename, rules)) do
- local np = mesecon:addPosRule(pos, rule)
- if mesecon:rules_link(np, pos) then
- if mesecon:find_receptor_on(np, checked, mesecon:invertRule(rule)) then
+ local rulenames = mesecon:rules_link_rule_all_inverted(pos, rule)
+ for _, rname in ipairs(rulenames) do
+ local np = mesecon:addPosRule(pos, rname)
+ if mesecon:find_receptor_on(np, checked, mesecon:invertRule(rname)) then
return true
end
end
@@ -583,6 +568,26 @@ function mesecon:rules_link_rule_all(output, rule) --output/input are positions
return rules
end
+function mesecon:rules_link_rule_all_inverted(input, rule)
+ --local irule = mesecon:invertRule(rule)
+ local output = mesecon:addPosRule(input, rule)
+ local outputnode = minetest.get_node(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 mesecon:cmpPos(mesecon:addPosRule(output, outputrule), input) then
+ if outputrule.sx == nil or rule.sx == nil or mesecon:cmpSpecial(outputrule, rule) then
+ rules[#rules+1] = mesecon:invertRule(outputrule)
+ end
+ end
+ end
+ return rules
+end
+
function mesecon:rules_link_anydir(pos1, pos2)
return mesecon:rules_link(pos1, pos2) or mesecon:rules_link(pos2, pos1)
end
@@ -594,21 +599,23 @@ function mesecon:is_powered(pos, rule)
if not rule then
for _, rule in ipairs(mesecon:flattenrules(rules)) do
- local np = mesecon:addPosRule(pos, rule)
- local nn = minetest.get_node(np)
-
- if (mesecon:is_conductor_on (nn, mesecon:invertRule(rule)) or mesecon:is_receptor_on (nn.name))
- and mesecon:rules_link(np, pos) then
- return true
+ local rulenames = mesecon:rules_link_rule_all_inverted(pos, rule)
+ for _, rname in ipairs(rulenames) do
+ local np = mesecon:addPosRule(pos, rname)
+ local nn = minetest.get_node(np)
+ if (mesecon:is_conductor_on (nn, mesecon:invertRule(rname)) or mesecon:is_receptor_on (nn.name)) then
+ return true
+ end
end
end
else
- local np = mesecon:addPosRule(pos, rule)
- local nn = minetest.get_node(np)
-
- if (mesecon:is_conductor_on (nn, mesecon:invertRule(rule)) or mesecon:is_receptor_on (nn.name))
- and mesecon:rules_link(np, pos) then
- return true
+ local rulenames = mesecon:rules_link_rule_all_inverted(pos, rule)
+ for _, rname in ipairs(rulenames) do
+ local np = mesecon:addPosRule(pos, rname)
+ local nn = minetest.get_node(np)
+ if (mesecon:is_conductor_on (nn, mesecon:invertRule(rname)) or mesecon:is_receptor_on (nn.name)) then
+ return true
+ end
end
end
diff --git a/mesecons/legacy.lua b/mesecons/legacy.lua
index a68bab4..c4334cf 100644
--- a/mesecons/legacy.lua
+++ b/mesecons/legacy.lua
@@ -2,4 +2,31 @@ 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 \ No newline at end of file
+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"}
+
+function legacy_update_ports(pos)
+ local meta = minetest.get_meta(pos)
+ L = {
+ a = mesecon:is_power_on(mesecon:addPosRule(pos, rules.a),
+ mesecon:invertRule(rules.a)) and
+ mesecon:rules_link(mesecon:addPosRule(pos, rules.a), pos),
+ b = mesecon:is_power_on(mesecon:addPosRule(pos, rules.b),
+ mesecon:invertRule(rules.b)) and
+ mesecon:rules_link(mesecon:addPosRule(pos, rules.b), pos),
+ c = mesecon:is_power_on(mesecon:addPosRule(pos, rules.c),
+ mesecon:invertRule(rules.c)) and
+ mesecon:rules_link(mesecon:addPosRule(pos, rules.c), pos),
+ d = mesecon:is_power_on(mesecon:addPosRule(pos, rules.d),
+ 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
+ meta:set_int("real_portstates", n)
+ return L
+end
diff --git a/mesecons/services.lua b/mesecons/services.lua
index 9d192ae..a1ab03a 100644
--- a/mesecons/services.lua
+++ b/mesecons/services.lua
@@ -3,22 +3,23 @@ mesecon.on_placenode = function (pos, node)
mesecon:receptor_on(pos, mesecon:receptor_get_rules(node))
elseif mesecon:is_powered(pos) then
if mesecon:is_conductor(node.name) then
+ -- receptor_on if itself is powered already
mesecon:turnon (pos)
- --mesecon:receptor_on (pos, mesecon:conductor_get_rules(node))
+ mesecon:receptor_on (pos, mesecon:conductor_get_rules(node))
else
- mesecon:changesignal(pos, node, mesecon:effector_get_rules(node), "on")
- mesecon:activate(pos, node)
+ mesecon:changesignal(pos, node, mesecon:effector_get_rules(node), "on", 1)
+ mesecon:activate(pos, node, nil, 1)
end
elseif mesecon:is_conductor_on(node) then
minetest.swap_node(pos, {name = mesecon:get_conductor_off(node)})
elseif mesecon:is_effector_on (node.name) then
- mesecon:deactivate(pos, node)
+ mesecon:deactivate(pos, node, nil, 1)
end
end
mesecon.on_dignode = function (pos, node)
if mesecon:is_conductor_on(node) then
- mesecon:receptor_off_i(pos, mesecon:conductor_get_rules(node))
+ mesecon:receptor_off(pos, mesecon:conductor_get_rules(node))
elseif mesecon:is_receptor_on(node.name) then
mesecon:receptor_off(pos, mesecon:receptor_get_rules(node))
end
diff --git a/mesecons/settings.lua b/mesecons/settings.lua
index e35bb1e..593a79b 100644
--- a/mesecons/settings.lua
+++ b/mesecons/settings.lua
@@ -7,3 +7,5 @@ PISTON_MAXIMUM_PUSH = 15
MOVESTONE_MAXIMUM_PUSH = 100
MESECONS_GLOBALSTEP = true -- true = receptors/effectors won't be updated
-- until next globalstep, decreases server load
+MESECONS_RESUMETIME = 4 -- time to wait when starting the server before
+ -- processing the ActionQueue, don't set this too low
diff --git a/mesecons/util.lua b/mesecons/util.lua
index b3ca7a0..91d435a 100644
--- a/mesecons/util.lua
+++ b/mesecons/util.lua
@@ -169,6 +169,7 @@ function mesecon:cmpSpecial(r1, r2)
end
function mesecon:tablecopy(table) -- deep table copy
+ if type(table) ~= "table" then return table end -- no need to copy
local newtable = {}
for idx, item in pairs(table) do
@@ -181,3 +182,14 @@ function mesecon:tablecopy(table) -- deep table copy
return newtable
end
+
+function mesecon:cmpAny(t1, t2)
+ if type(t1) ~= type(t2) then return false end
+ if type(t1) ~= "table" and type(t2) ~= "table" then return t1 == t2 end
+
+ for i, e in pairs(t1) do
+ if not mesecon:cmpAny(e, t2[i]) then return false end
+ end
+
+ return true
+end
diff --git a/mesecons_delayer/init.lua b/mesecons_delayer/init.lua
index a03737c..4ec0ebc 100644
--- a/mesecons_delayer/init.lua
+++ b/mesecons_delayer/init.lua
@@ -17,28 +17,18 @@ end
-- Functions that are called after the delay time
-local delayer_turnon = function(params)
- local rules = delayer_get_output_rules(params.node)
- mesecon:receptor_on(params.pos, rules)
-end
-
-local delayer_turnoff = function(params)
- local rules = delayer_get_output_rules(params.node)
- mesecon:receptor_off(params.pos, rules)
-end
-
local delayer_activate = function(pos, node)
local def = minetest.registered_nodes[node.name]
local time = def.delayer_time
minetest.swap_node(pos, {name = def.delayer_onstate, param2=node.param2})
- minetest.after(time, delayer_turnon , {pos = pos, node = node})
+ mesecon.queue:add_action(pos, "receptor_on", {rules=delayer_get_output_rules(node)}, time, nil)
end
local delayer_deactivate = function(pos, node)
local def = minetest.registered_nodes[node.name]
local time = def.delayer_time
minetest.swap_node(pos, {name = def.delayer_offstate, param2=node.param2})
- minetest.after(time, delayer_turnoff, {pos = pos, node = node})
+ mesecon.queue:add_action(pos, "receptor_off", {rules=delayer_get_output_rules(node)}, time, nil)
end
-- Register the 2 (states) x 4 (delay times) delayers
diff --git a/mesecons_detector/init.lua b/mesecons_detector/init.lua
index 7bb516b..79aa1f9 100644
--- a/mesecons_detector/init.lua
+++ b/mesecons_detector/init.lua
@@ -6,7 +6,8 @@ local object_detector_make_formspec = function (pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", "size[9,2.5]" ..
"field[0.3, 0;9,2;scanname;Name of player to scan for (empty for any):;${scanname}]"..
- "field[0.3,1.5;4,2;digiline_channel;Digiline Channel (optional):;${digiline_channel}]")
+ "field[0.3,1.5;4,2;digiline_channel;Digiline Channel (optional):;${digiline_channel}]"..
+ "button_exit[7,0.75;2,3;;Save]")
end
local object_detector_on_receive_fields = function(pos, formname, fields)
diff --git a/mesecons_gates/init.lua b/mesecons_gates/init.lua
index 1a9ee83..51ed4af 100644
--- a/mesecons_gates/init.lua
+++ b/mesecons_gates/init.lua
@@ -23,7 +23,8 @@ function gate_get_input_rules_twoinputs(node)
return gate_rotate_rules(node)
end
-function update_gate(pos)
+function update_gate(pos, node, rulename, newstate)
+ yc_update_real_portstates(pos, node, rulename, newstate)
gate = get_gate(pos)
L = rotate_ports(
yc_get_real_portstates(pos),
diff --git a/mesecons_luacontroller/init.lua b/mesecons_luacontroller/init.lua
index acbf023..de3a972 100644
--- a/mesecons_luacontroller/init.lua
+++ b/mesecons_luacontroller/init.lua
@@ -31,18 +31,46 @@ rules.d = {x = 0, y = 0, z = -1, name="D"}
------------------
-- These helpers are required to set the portstates of the luacontroller
+function lc_update_real_portstates(pos, rulename, newstate)
+ local meta = minetest.get_meta(pos)
+ if rulename == 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)
+ n = meta:get_int("real_portstates") - 1
+ end
+ local L = {}
+ for i = 1, 4 do
+ 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]
+ 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
+ end
+ meta:set_int("real_portstates", 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)
- 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),
- b = mesecon:is_power_on(mesecon:addPosRule(pos, rules.b), mesecon:invertRule(rules.b))
- and mesecon:rules_link(mesecon:addPosRule(pos, rules.b), pos),
- c = mesecon:is_power_on(mesecon:addPosRule(pos, rules.c), mesecon:invertRule(rules.c))
- and mesecon:rules_link(mesecon:addPosRule(pos, rules.c), pos),
- d = mesecon:is_power_on(mesecon:addPosRule(pos, rules.d), mesecon:invertRule(rules.d))
- and mesecon:rules_link(mesecon:addPosRule(pos, rules.d), pos),
- }
- return ports
+ 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)
+ end
+ for _, index in ipairs({"a", "b", "c", "d"}) do
+ L[index] = ((n%2) == 1)
+ n = math.floor(n/2)
+ end
+ return L
end
local merge_portstates = function (ports, vports)
@@ -164,40 +192,29 @@ local safe_serialize = function(value)
return minetest.serialize(deep_copy(value))
end
-local interrupt = function(params)
- lc_update(params.pos, {type="interrupt", iid = params.iid})
-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
if type(time) ~= "number" then return end
- local iid = iid or math.random()
- local meta = minetest.get_meta(pos)
- local interrupts = minetest.deserialize(meta:get_string("lc_interrupts")) or {}
- local found = false
- local search = safe_serialize(iid)
- for _, i in ipairs(interrupts) do
- if safe_serialize(i) == search then
- found = true
- break
- end
- end
- if not found then
- table.insert(interrupts, iid)
- meta:set_string("lc_interrupts", safe_serialize(interrupts))
- end
- minetest.after(time, interrupt, {pos=pos, iid = iid})
+ luac_id = minetest.get_meta(pos):get_int("luac_id")
+ mesecon.queue:add_action(pos, "lc_interrupt", {iid, luac_id}, time, iid, 1)
end
return interrupt
end
-local getdigiline_send = function (pos)
- local digiline_send = function (channel, msg)
- if digiline then
+local getdigiline_send = function(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)
- end
+ end)
end
- return digiline_send
end
local create_environment = function(pos, mem, event)
@@ -224,6 +241,7 @@ local create_environment = function(pos, mem, event)
gsub = string.gsub,
len = string.len,
lower = string.lower,
+ upper = string.upper,
match = string.match,
rep = string.rep,
reverse = string.reverse,
@@ -288,7 +306,6 @@ local do_overheat = function (pos, meta)
if overheat(meta) then
local node = minetest.get_node(pos)
minetest.swap_node(pos, {name = BASENAME.."_burnt", param2 = node.param2})
- minetest.get_meta(pos):set_string("lc_interrupts", "")
minetest.after(0.2, overheat_off, pos) -- wait for pending operations
return true
end
@@ -302,20 +319,6 @@ local save_memory = function(meta, mem)
meta:set_string("lc_memory", safe_serialize(mem))
end
-local interrupt_allow = function (meta, event)
- if event.type ~= "interrupt" then return true end
-
- local interrupts = minetest.deserialize(meta:get_string("lc_interrupts")) or {}
- local search = safe_serialize(event.iid)
- for _, i in ipairs(interrupts) do
- if safe_serialize(i) == search then
- return true
- end
- end
-
- return false
-end
-
local ports_invalid = function (var)
if type(var) == "table" then
return false
@@ -329,7 +332,6 @@ end
lc_update = function (pos, event)
local meta = minetest.get_meta(pos)
- if not interrupt_allow(meta, event) then return end
if do_overheat(pos, meta) then return end
-- load code & mem from memory
@@ -338,7 +340,7 @@ lc_update = function (pos, event)
-- make sure code is ok and create environment
local prohibited = code_prohibited(code)
- if prohibited then return prohibited end
+ if prohibited then return prohibited end
local env = create_environment(pos, mem, event)
-- create the sandbox and execute code
@@ -351,7 +353,7 @@ lc_update = function (pos, event)
save_memory(meta, mem)
-- Actually set the ports
- minetest.after(0, action, pos, env.port)
+ action(pos, env.port)
end
local reset_meta = function(pos, code, errmsg)
@@ -366,11 +368,11 @@ 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))
end
local reset = function (pos)
- minetest.get_meta(pos):set_string("lc_interrupts", "")
- action(pos, {a=false, b=false, c=false, d=false}, true)
+ action(pos, {a=false, b=false, c=false, d=false})
end
-- ______
@@ -457,6 +459,7 @@ local mesecons = {
{
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,
},
@@ -488,19 +491,21 @@ minetest.register_node(nodename, {
node_box = nodebox,
on_construct = reset_meta,
on_receive_fields = function(pos, formname, fields)
- if fields.quit then
+ 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) end
- reset_meta(pos, fields.code, err)
+ 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,
- is_luacontroller = true,
virtual_portstates = { a = a == 1, -- virtual portstates are
b = b == 1, -- the ports the the
c = c == 1, -- controller powers itself
@@ -508,6 +513,7 @@ minetest.register_node(nodename, {
after_dig_node = function (pos, node)
mesecon:receptor_off(pos, output_rules)
end,
+ is_luacontroller = true,
})
end
end
@@ -540,11 +546,12 @@ minetest.register_node(BASENAME .. "_burnt", {
reset(pos)
reset_meta(pos, fields.code)
local err = lc_update(pos, {type="program"})
- if err then print(err) end
- reset_meta(pos, fields.code, err)
+ if err then
+ print(err)
+ reset_meta(pos, fields.code, err)
+ end
end,
sounds = default.node_sound_stone_defaults(),
- is_luacontroller = true,
virtual_portstates = {a = false, b = false, c = false, d = false},
})
diff --git a/mesecons_microcontroller/init.lua b/mesecons_microcontroller/init.lua
index 55faa5c..b6134da 100644
--- a/mesecons_microcontroller/init.lua
+++ b/mesecons_microcontroller/init.lua
@@ -39,7 +39,8 @@ mesecon:add_rules(nodename, rules)
local mesecons = {effector =
{
rules = input_rules,
- action_change = function (pos, node, rulename)
+ action_change = function (pos, node, rulename, newstate)
+ yc_update_real_portstates(pos, node, rulename, newstate)
update_yc(pos)
end
}}
@@ -633,25 +634,45 @@ function yc_set_portstate(port, state, L)
return L
end
-function yc_get_real_portstates(pos) -- port powered or not (by itself or from outside)?
- rulesA = mesecon:get_rules("mesecons_microcontroller:microcontroller0001")
- rulesB = mesecon:get_rules("mesecons_microcontroller:microcontroller0010")
- rulesC = mesecon:get_rules("mesecons_microcontroller:microcontroller0100")
- rulesD = mesecon:get_rules("mesecons_microcontroller:microcontroller1000")
- L = {
- a = mesecon:is_power_on(mesecon:addPosRule(pos, rulesA[1]),
- mesecon:invertRule(rulesA[1])) and
- mesecon:rules_link(mesecon:addPosRule(pos, rulesA[1]), pos),
- b = mesecon:is_power_on(mesecon:addPosRule(pos, rulesB[1]),
- mesecon:invertRule(rulesB[1])) and
- mesecon:rules_link(mesecon:addPosRule(pos, rulesB[1]), pos),
- c = mesecon:is_power_on(mesecon:addPosRule(pos, rulesC[1]),
- mesecon:invertRule(rulesC[1])) and
- mesecon:rules_link(mesecon:addPosRule(pos, rulesC[1]), pos),
- d = mesecon:is_power_on(mesecon:addPosRule(pos, rulesD[1]),
- mesecon:invertRule(rulesD[1])) and
- mesecon:rules_link(mesecon:addPosRule(pos, rulesD[1]), pos),
- }
+function yc_update_real_portstates(pos, node, rulename, newstate)
+ local meta = minetest.get_meta(pos)
+ if rulename == 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)
+ n = meta:get_int("real_portstates") - 1
+ end
+ local L = {}
+ for i = 1, 4 do
+ 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]
+ 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
+ end
+ meta:set_int("real_portstates", 1 + L[1] + 2*L[2] + 4*L[3] + 8*L[4])
+end
+
+function yc_get_real_portstates(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)
+ end
+ for _, index in ipairs({"a", "b", "c", "d"}) do
+ L[index] = ((n%2) == 1)
+ n = math.floor(n/2)
+ end
return L
end