From f1211f7dae58ff4298b6bf4fcaa572e7995ab5e2 Mon Sep 17 00:00:00 2001 From: Jeija Date: Sat, 11 Jan 2014 10:04:32 +0100 Subject: [PATCH] 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). --- mesecons/actionqueue.lua | 28 ++++++++++++++++--- mesecons/init.lua | 14 +++++++--- mesecons/internal.lua | 58 +++++++++++++++++----------------------- mesecons/services.lua | 6 ++--- mesecons/util.lua | 1 + 5 files changed, 63 insertions(+), 44 deletions(-) diff --git a/mesecons/actionqueue.lua b/mesecons/actionqueue.lua index 9d31426..a00054a 100644 --- a/mesecons/actionqueue.lua +++ b/mesecons/actionqueue.lua @@ -6,15 +6,19 @@ 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 -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: time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution + priority = priority or 1 overwritecheck = overwritecheck or {} action = { pos=mesecon:tablecopy(pos), func=func, params=mesecon:tablecopy(params), time=time, - owcheck=mesecon:tablecopy(overwritecheck)} + owcheck=(overwritecheck and mesecon:tablecopy(overwritecheck)) or nil, + priority=priority} --print(dump(action)) -- 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 -- 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 @@ -53,10 +69,14 @@ minetest.register_globalstep(function (dtime) if action.time > 0 then action.time = action.time - dtime table.insert(mesecon.queue.actions, action) -- will be handled another time - else -- execute and remove - mesecon.queue:execute(action) 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) function mesecon.queue:execute(action) diff --git a/mesecons/init.lua b/mesecons/init.lua index 8facf73..4d4819c 100644 --- a/mesecons/init.lua +++ b/mesecons/init.lua @@ -79,12 +79,18 @@ dofile(minetest.get_modpath("mesecons").."/legacy.lua"); mesecon.queue:add_function("receptor_on", function (pos, rules) 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 local np = mesecon:addPosRule(pos, rule) - -- if area is not loaded, keep trying - 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_on", {rules}) + return 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) for _, rulename in ipairs(rulenames) do mesecon:turnon(np, rulename) @@ -100,7 +106,7 @@ mesecon.queue:add_function("receptor_off", function (pos, rules) rules = rules or mesecon.rules.default for _, rule in ipairs(mesecon:flattenrules(rules)) do 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}) end 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 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, 1) end end end diff --git a/mesecons/internal.lua b/mesecons/internal.lua index f575c83..f713a7c 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 @@ -191,14 +191,14 @@ mesecon.queue:add_function("activate", function (pos, rulename) end end) -function mesecon:activate(pos, node, rulename) +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) + mesecon:activate(pos, node, rule, recdepth + 1) end return end - mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename) + mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename, 1 / recdepth) end @@ -212,14 +212,14 @@ mesecon.queue:add_function("deactivate", function (pos, rulename) end end) -function mesecon:deactivate(pos, node, rulename) +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) + mesecon:deactivate(pos, node, rule, recdepth + 1) end return end - mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename) + mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename, 1 / recdepth) end @@ -233,15 +233,15 @@ mesecon.queue:add_function("change", function (pos, rulename, changetype) end end) -function mesecon:changesignal(pos, node, rulename, newstate) +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) + mesecon:changesignal(pos, node, rule, newstate, recdepth + 1) end return end - mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, rulename) + mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, rulename, 1 / recdepth) end -- ######### @@ -365,7 +365,8 @@ 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 mesecon:is_conductor_off(node, rulename) then @@ -374,7 +375,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 @@ -387,32 +388,23 @@ function mesecon:turnon(pos, rulename) local rulenames = mesecon:rules_link_rule_all(pos, rule) for _, rulename in ipairs(rulenames) do - mesecon:turnon(np, rulename) + mesecon:turnon(np, rulename, recdepth + 1) 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) +function mesecon:turnoff(pos, rulename, recdepth) + recdepth = recdepth or 0 local node = minetest.get_node(pos) 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 @@ -420,14 +412,14 @@ function mesecon:turnoff(pos, rulename) local rulenames = mesecon:rules_link_rule_all(pos, rule) for _, rulename in ipairs(rulenames) do - mesecon:turnoff(np, rulename) + mesecon:turnoff(np, rulename, recdepth + 1) 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 diff --git a/mesecons/services.lua b/mesecons/services.lua index 29b5183..de0c8b6 100644 --- a/mesecons/services.lua +++ b/mesecons/services.lua @@ -6,13 +6,13 @@ 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 diff --git a/mesecons/util.lua b/mesecons/util.lua index ad2a4ae..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