Add ActionQueue priority system

This makes effectors nearer to the source of the action (the receptor) update first.

This defines behaviour for this piston circuit: http://i.imgur.com/9Pp2Mzb.png
And defines, that this memory circuit does not work from this direction: http://i.imgur.com/jJn0aFh.png
But it will work when using the switch from the other side: http://i.imgur.com/nvw0oZB.png

Only if two effectors have the same distance, there is nothing we can do about it, behaviour is not defined.
"Distance" is determined by the stack size of recursions in turnon / turnoff.
Priorities are between 0 (lowest) and 1 (highest).
This commit is contained in:
Jeija 2014-01-11 10:04:32 +01:00
parent 93fb489bdb
commit f1211f7dae
5 changed files with 63 additions and 44 deletions

@ -6,15 +6,19 @@ end
-- If add_action with twice the same overwritecheck and same position are called, the first one is overwritten -- 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 -- use overwritecheck nil to never overwrite, but just add the event to the queue
function mesecon.queue:add_action(pos, func, params, time, overwritecheck) -- 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: -- Create Action Table:
time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution
priority = priority or 1
overwritecheck = overwritecheck or {} overwritecheck = overwritecheck or {}
action = { pos=mesecon:tablecopy(pos), action = { pos=mesecon:tablecopy(pos),
func=func, func=func,
params=mesecon:tablecopy(params), params=mesecon:tablecopy(params),
time=time, time=time,
owcheck=mesecon:tablecopy(overwritecheck)} owcheck=(overwritecheck and mesecon:tablecopy(overwritecheck)) or nil,
priority=priority}
--print(dump(action)) --print(dump(action))
-- if not using the queue, (MESECONS_GLOBALSTEP off), just execute the function an we're done -- if not using the queue, (MESECONS_GLOBALSTEP off), just execute the function an we're done
@ -42,6 +46,18 @@ end
-- this makes sure that resuming mesecons circuits when restarting minetest works fine -- 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 -- 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 -- 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 local m_time = 0
minetest.register_globalstep(function (dtime) minetest.register_globalstep(function (dtime)
m_time = m_time + dtime m_time = m_time + dtime
@ -53,10 +69,14 @@ minetest.register_globalstep(function (dtime)
if action.time > 0 then if action.time > 0 then
action.time = action.time - dtime action.time = action.time - dtime
table.insert(mesecon.queue.actions, action) -- will be handled another time table.insert(mesecon.queue.actions, action) -- will be handled another time
else -- execute and remove
mesecon.queue:execute(action)
end end
end end
while(#actions > 0) do -- execute highest priorities first, until all are executed
local hp = get_highest_priority(actions)
mesecon.queue:execute(actions[hp])
table.remove(actions, hp)
end
end) end)
function mesecon.queue:execute(action) function mesecon.queue:execute(action)

@ -79,12 +79,18 @@ dofile(minetest.get_modpath("mesecons").."/legacy.lua");
mesecon.queue:add_function("receptor_on", function (pos, rules) mesecon.queue:add_function("receptor_on", function (pos, rules)
rules = rules or mesecon.rules.default rules = rules or mesecon.rules.default
-- if area (any of the neighbors) is not loaded, keep trying and call this again later
for _, rule in ipairs(mesecon:flattenrules(rules)) do for _, rule in ipairs(mesecon:flattenrules(rules)) do
local np = mesecon:addPosRule(pos, rule) local np = mesecon:addPosRule(pos, rule)
-- if area is not loaded, keep trying if minetest.get_node_or_nil(np) == nil then
if minetest.env:get_node_or_nil(np) == nil then
mesecon.queue:add_action(pos, "receptor_on", {rules}) mesecon.queue:add_action(pos, "receptor_on", {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) local rulenames = mesecon:rules_link_rule_all(pos, rule)
for _, rulename in ipairs(rulenames) do for _, rulename in ipairs(rulenames) do
mesecon:turnon(np, rulename) mesecon:turnon(np, rulename)
@ -100,7 +106,7 @@ mesecon.queue:add_function("receptor_off", function (pos, rules)
rules = rules or mesecon.rules.default rules = rules or mesecon.rules.default
for _, rule in ipairs(mesecon:flattenrules(rules)) do for _, rule in ipairs(mesecon:flattenrules(rules)) do
local np = mesecon:addPosRule(pos, rule) local np = mesecon:addPosRule(pos, rule)
if minetest.env:get_node_or_nil(np) == nil then if minetest.get_node_or_nil(np) == nil then
mesecon.queue:add_action(pos, "receptor_off", {rules}) mesecon.queue:add_action(pos, "receptor_off", {rules})
end end
local rulenames = mesecon:rules_link_rule_all(pos, rule) local rulenames = mesecon:rules_link_rule_all(pos, rule)
@ -108,7 +114,7 @@ mesecon.queue:add_function("receptor_off", function (pos, rules)
if not mesecon:connected_to_receptor(np, mesecon:invertRule(rule)) then if not mesecon:connected_to_receptor(np, mesecon:invertRule(rule)) then
mesecon:turnoff(np, rulename) mesecon:turnoff(np, rulename)
else else
mesecon:changesignal(np, minetest.get_node(np), rulename, mesecon.state.off) mesecon:changesignal(np, minetest.get_node(np), rulename, mesecon.state.off, 1)
end end
end end
end end

@ -22,9 +22,9 @@
-- mesecon:effector_get_rules(node) --> Returns the input rules of the effector (mesecon.rules.default if none specified) -- mesecon:effector_get_rules(node) --> Returns the input rules of the effector (mesecon.rules.default if none specified)
-- SIGNALS -- SIGNALS
-- mesecon:activate(pos, node) --> Activates the effector node at the specific pos (calls nodedef.mesecons.effector.action_on) -- 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) --> Deactivates the effector node at the specific pos (calls nodedef.mesecons.effector.action_off) -- 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) -- mesecon:changesignal(pos, node, rulename, newstate) --> Changes the effector node at the specific pos (calls nodedef.mesecons.effector.action_change), "
-- RULES -- RULES
-- mesecon:add_rules(name, rules) | deprecated? --> Saves rules table by name -- mesecon:add_rules(name, rules) | deprecated? --> Saves rules table by name
@ -41,8 +41,8 @@
-- HIGH-LEVEL Internals -- HIGH-LEVEL Internals
-- mesecon:is_power_on(pos) --> Returns true if pos emits power in any way -- 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: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: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 -- 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: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(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 -- mesecon:rules_link_anydir(outp., inp., d_outpr.) --> Same as rules mesecon:rules_link but also returns true if output and input are swapped
@ -191,14 +191,14 @@ mesecon.queue:add_function("activate", function (pos, rulename)
end end
end) end)
function mesecon:activate(pos, node, rulename) function mesecon:activate(pos, node, rulename, recdepth)
if rulename == nil then if rulename == nil then
for _,rule in ipairs(mesecon:effector_get_rules(node)) do for _,rule in ipairs(mesecon:effector_get_rules(node)) do
mesecon:activate(pos, node, rule) mesecon:activate(pos, node, rule, recdepth + 1)
end end
return return
end end
mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename) mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename, 1 / recdepth)
end end
@ -212,14 +212,14 @@ mesecon.queue:add_function("deactivate", function (pos, rulename)
end end
end) end)
function mesecon:deactivate(pos, node, rulename) function mesecon:deactivate(pos, node, rulename, recdepth)
if rulename == nil then if rulename == nil then
for _,rule in ipairs(mesecon:effector_get_rules(node)) do for _,rule in ipairs(mesecon:effector_get_rules(node)) do
mesecon:deactivate(pos, node, rule) mesecon:deactivate(pos, node, rule, recdepth + 1)
end end
return return
end end
mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename) mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename, 1 / recdepth)
end end
@ -233,15 +233,15 @@ mesecon.queue:add_function("change", function (pos, rulename, changetype)
end end
end) end)
function mesecon:changesignal(pos, node, rulename, newstate) function mesecon:changesignal(pos, node, rulename, newstate, recdepth)
if rulename == nil then if rulename == nil then
for _,rule in ipairs(mesecon:effector_get_rules(node)) do for _,rule in ipairs(mesecon:effector_get_rules(node)) do
mesecon:changesignal(pos, node, rule, newstate) mesecon:changesignal(pos, node, rule, newstate, recdepth + 1)
end end
return return
end end
mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, rulename) mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, rulename, 1 / recdepth)
end end
-- ######### -- #########
@ -365,7 +365,8 @@ function mesecon:is_power_off(pos, rulename)
return false return false
end end
function mesecon:turnon(pos, rulename) function mesecon:turnon(pos, rulename, recdepth)
recdepth = recdepth or 2
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
if mesecon:is_conductor_off(node, rulename) then if mesecon:is_conductor_off(node, rulename) then
@ -374,7 +375,7 @@ function mesecon:turnon(pos, rulename)
if not rulename then if not rulename then
for _, rule in ipairs(mesecon:flattenrules(rules)) do for _, rule in ipairs(mesecon:flattenrules(rules)) do
if mesecon:connected_to_receptor(pos, rule) then if mesecon:connected_to_receptor(pos, rule) then
mesecon:turnon(pos, rule) mesecon:turnon(pos, rule, recdepth + 1)
end end
end end
return return
@ -387,32 +388,23 @@ function mesecon:turnon(pos, rulename)
local rulenames = mesecon:rules_link_rule_all(pos, rule) local rulenames = mesecon:rules_link_rule_all(pos, rule)
for _, rulename in ipairs(rulenames) do for _, rulename in ipairs(rulenames) do
mesecon:turnon(np, rulename) mesecon:turnon(np, rulename, recdepth + 1)
end end
end end
elseif mesecon:is_effector(node.name) then 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 if mesecon:is_effector_off(node.name) then
mesecon:activate(pos, node, rulename) mesecon:activate(pos, node, rulename, recdepth)
end end
end end
end end
function mesecon:turnoff(pos, rulename) function mesecon:turnoff(pos, rulename, recdepth)
recdepth = recdepth or 0
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
if mesecon:is_conductor_on(node, rulename) then if mesecon:is_conductor_on(node, rulename) then
local rules = mesecon:conductor_get_rules(node) 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}) minetest.swap_node(pos, {name = mesecon:get_conductor_off(node, rulename), param2 = node.param2})
for _, rule in ipairs(mesecon:rule2meta(rulename, rules)) do for _, rule in ipairs(mesecon:rule2meta(rulename, rules)) do
@ -420,14 +412,14 @@ function mesecon:turnoff(pos, rulename)
local rulenames = mesecon:rules_link_rule_all(pos, rule) local rulenames = mesecon:rules_link_rule_all(pos, rule)
for _, rulename in ipairs(rulenames) do for _, rulename in ipairs(rulenames) do
mesecon:turnoff(np, rulename) mesecon:turnoff(np, rulename, recdepth + 1)
end end
end end
elseif mesecon:is_effector(node.name) then 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) if mesecon:is_effector_on(node.name)
and not mesecon:is_powered(pos) then and not mesecon:is_powered(pos) then
mesecon:deactivate(pos, node, rulename) mesecon:deactivate(pos, node, rulename, recdepth + 1)
end end
end end
end end

@ -6,13 +6,13 @@ mesecon.on_placenode = function (pos, node)
mesecon:turnon (pos) mesecon:turnon (pos)
--mesecon:receptor_on (pos, mesecon:conductor_get_rules(node)) --mesecon:receptor_on (pos, mesecon:conductor_get_rules(node))
else else
mesecon:changesignal(pos, node, mesecon:effector_get_rules(node), "on") mesecon:changesignal(pos, node, mesecon:effector_get_rules(node), "on", 1)
mesecon:activate(pos, node) mesecon:activate(pos, node, nil, 1)
end end
elseif mesecon:is_conductor_on(node) then elseif mesecon:is_conductor_on(node) then
minetest.swap_node(pos, {name = mesecon:get_conductor_off(node)}) minetest.swap_node(pos, {name = mesecon:get_conductor_off(node)})
elseif mesecon:is_effector_on (node.name) then elseif mesecon:is_effector_on (node.name) then
mesecon:deactivate(pos, node) mesecon:deactivate(pos, node, nil, 1)
end end
end end

@ -169,6 +169,7 @@ function mesecon:cmpSpecial(r1, r2)
end end
function mesecon:tablecopy(table) -- deep table copy function mesecon:tablecopy(table) -- deep table copy
if type(table) ~= "table" then return table end -- no need to copy
local newtable = {} local newtable = {}
for idx, item in pairs(table) do for idx, item in pairs(table) do