diff options
author | Jeija <jeija@mesecons.net> | 2014-01-19 13:59:22 +0100 |
---|---|---|
committer | Jeija <jeija@mesecons.net> | 2014-01-19 13:59:22 +0100 |
commit | 8a71f51b265e7f62dd070e9a9315ccc7abccc7c0 (patch) | |
tree | 6080a79bb6ada8550b4c64a0e238c28eab38f28a | |
parent | 7517cc4af16f225ff2a48fa9f8ed82f608bf0082 (diff) | |
parent | a632a8abc80155118708108d846f52bbc230f257 (diff) | |
download | mesecons-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.lua | 119 | ||||
-rw-r--r-- | mesecons/init.lua | 86 | ||||
-rw-r--r-- | mesecons/internal.lua | 233 | ||||
-rw-r--r-- | mesecons/legacy.lua | 29 | ||||
-rw-r--r-- | mesecons/services.lua | 8 | ||||
-rw-r--r-- | mesecons/settings.lua | 2 | ||||
-rw-r--r-- | mesecons/util.lua | 12 | ||||
-rw-r--r-- | mesecons_delayer/init.lua | 14 | ||||
-rw-r--r-- | mesecons_gates/init.lua | 3 | ||||
-rw-r--r-- | mesecons_luacontroller/init.lua | 51 | ||||
-rw-r--r-- | mesecons_microcontroller/init.lua | 61 |
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 |