summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeija <jeija@mesecons.net>2014-01-19 13:59:22 +0100
committerJeija <jeija@mesecons.net>2014-01-19 13:59:22 +0100
commit8a71f51b265e7f62dd070e9a9315ccc7abccc7c0 (patch)
tree6080a79bb6ada8550b4c64a0e238c28eab38f28a
parent7517cc4af16f225ff2a48fa9f8ed82f608bf0082 (diff)
parenta632a8abc80155118708108d846f52bbc230f257 (diff)
downloadmesecons-8a71f51b265e7f62dd070e9a9315ccc7abccc7c0.tar
mesecons-8a71f51b265e7f62dd070e9a9315ccc7abccc7c0.tar.gz
mesecons-8a71f51b265e7f62dd070e9a9315ccc7abccc7c0.tar.bz2
mesecons-8a71f51b265e7f62dd070e9a9315ccc7abccc7c0.tar.xz
mesecons-8a71f51b265e7f62dd070e9a9315ccc7abccc7c0.zip
Merge branch 'actionqueue'
This introduces the ActionQueue, a new kind of MESECONS_GLOBALSTEP. Circuits using delayers will now resume when restarting the server. Also, large circuits should automatically resume if parts of them are in unloaded chunks. Old circuits e.g. using gates will not resume when mesecons is updated, which means you have to restart them once. But after that, it should work just like it used to. This will fix a lot of stuff but may also introduce some new bugs. So please report them!
-rw-r--r--mesecons/actionqueue.lua119
-rw-r--r--mesecons/init.lua86
-rw-r--r--mesecons/internal.lua233
-rw-r--r--mesecons/legacy.lua29
-rw-r--r--mesecons/services.lua8
-rw-r--r--mesecons/settings.lua2
-rw-r--r--mesecons/util.lua12
-rw-r--r--mesecons_delayer/init.lua14
-rw-r--r--mesecons_gates/init.lua3
-rw-r--r--mesecons_luacontroller/init.lua51
-rw-r--r--mesecons_microcontroller/init.lua61
11 files changed, 396 insertions, 222 deletions
diff --git a/mesecons/actionqueue.lua b/mesecons/actionqueue.lua
new file mode 100644
index 0000000..cf74d47
--- /dev/null
+++ b/mesecons/actionqueue.lua
@@ -0,0 +1,119 @@
+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 by default
+-- 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
+ 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 = 0, 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 in execute now (actions_now) and for 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..3975b6a 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)
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..de0c8b6 100644
--- a/mesecons/services.lua
+++ b/mesecons/services.lua
@@ -6,19 +6,19 @@ mesecon.on_placenode = function (pos, node)
mesecon:turnon (pos)
--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_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..c9a765e 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)
@@ -457,6 +485,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,
},
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