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!
This commit is contained in:
Jeija 2014-01-19 13:59:22 +01:00
commit 8a71f51b26
11 changed files with 397 additions and 223 deletions

119
mesecons/actionqueue.lua Normal file

@ -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)

@ -42,37 +42,8 @@
-- PUBLIC VARIABLES -- PUBLIC VARIABLES
mesecon={} -- contains all functions and all global variables mesecon={} -- contains all functions and all global variables
mesecon.actions_on={} -- Saves registered function callbacks for mesecon on | DEPRECATED mesecon.queue={} -- contains the ActionQueue
mesecon.actions_off={} -- Saves registered function callbacks for mesecon off | DEPRECATED mesecon.queue.funcs={} -- contains all ActionQueue functions
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)
-- Settings -- Settings
dofile(minetest.get_modpath("mesecons").."/settings.lua") 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 -- mostly things that make the source look cleaner
dofile(minetest.get_modpath("mesecons").."/util.lua"); 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 -- Internal stuff
-- This is the most important file -- This is the most important file
-- it handles signal transmission and basically everything else -- it handles signal transmission and basically everything else
@ -101,9 +76,22 @@ dofile(minetest.get_modpath("mesecons").."/legacy.lua");
-- API -- API
-- these are the only functions you need to remember -- 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 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 for _, rule in ipairs(mesecon:flattenrules(rules)) do
local np = mesecon:addPosRule(pos, rule) local np = mesecon:addPosRule(pos, rule)
local rulenames = mesecon:rules_link_rule_all(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) mesecon:turnon(np, rulename)
end end
end end
end end)
function mesecon:receptor_on(pos, rules) function mesecon:receptor_on(pos, rules)
if MESECONS_GLOBALSTEP then mesecon.queue:add_action(pos, "receptor_on", {rules}, nil, rules)
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
end end
function mesecon:receptor_off_i(pos, rules) mesecon.queue:add_function("receptor_off", function (pos, rules)
rules = rules or mesecon.rules.default 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 for _, rule in ipairs(mesecon:flattenrules(rules)) do
local np = mesecon:addPosRule(pos, rule) local np = mesecon:addPosRule(pos, rule)
local rulenames = mesecon:rules_link_rule_all(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 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, 2)
end end
end end
end end
end end)
function mesecon:receptor_off(pos, rules) function mesecon:receptor_off(pos, rules)
if MESECONS_GLOBALSTEP then mesecon.queue:add_action(pos, "receptor_off", {rules}, nil, rules)
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
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
@ -177,121 +177,76 @@ function mesecon:effector_get_rules(node)
return mesecon.rules.default return mesecon.rules.default
end end
--Signals -- #######################
-- # Signals (effectors) #
-- #######################
function mesecon:activate(pos, node, rulename) -- Activation:
if MESECONS_GLOBALSTEP then mesecon.queue:add_function("activate", function (pos, rulename)
if rulename == nil then node = minetest.get_node(pos)
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
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
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)
end
end
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) effector = mesecon:get_effector(node.name)
if i.action == "on" then
if effector and effector.action_on then if effector and effector.action_on then
effector.action_on(i.pos, node, i.rname) effector.action_on(pos, node, rulename)
end end
elseif i.action == "off" then end)
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
-- 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 if effector and effector.action_off then
effector.action_off(i.pos, node, i.rname) effector.action_off(pos, node, rulename)
end end
elseif i.action == "con" then end)
if effector and effector.action_change then
effector.action_change(i.pos, node, i.rname, "on") function mesecon:deactivate(pos, node, rulename, recdepth)
end if rulename == nil then
elseif i.action == "coff" then for _,rule in ipairs(mesecon:effector_get_rules(node)) do
if effector and effector.action_change then mesecon:deactivate(pos, node, rule, recdepth + 1)
effector.action_change(i.pos, node, i.rname, "off")
end
end
end
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 end
return
end end
mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename, 1 / recdepth)
end end
minetest.register_globalstep(execute_actions)
function add_action(pos, action, rname) -- Change
for _,i in ipairs(mesecon.to_update) do mesecon.queue:add_function("change", function (pos, rulename, changetype)
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 node = minetest.get_node(pos)
if (i.action == "on" and action == "on") or (i.action == "off" and action == "off") then effector = mesecon:get_effector(node.name)
--nothing
elseif i.action == "coff" and action == "on" then i.action = "on" if effector and effector.action_change then
elseif i.action == "con" and action == "off" then i.action = "off" effector.action_change(pos, node, rulename, changetype)
else
if action == "on" or action == "con" then i.action = "con" end
if action == "off" or action == "coff" then i.action = "coff" end
end end
break 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 end
return
end 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 end
--Rules -- #########
-- # Rules # "Database" for rulenames
-- #########
function mesecon:add_rules(name, rules) function mesecon:add_rules(name, rules)
mesecon.rules[name] = rules mesecon.rules[name] = rules
@ -410,16 +365,23 @@ 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(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 if mesecon:is_conductor_off(node, rulename) then
local rules = mesecon:conductor_get_rules(node) local rules = mesecon:conductor_get_rules(node)
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
@ -429,54 +391,75 @@ function mesecon:turnon(pos, rulename)
for _, rule in ipairs(mesecon:rule2meta(rulename, rules)) do for _, rule in ipairs(mesecon:rule2meta(rulename, rules)) do
local np = mesecon:addPosRule(pos, rule) local np = mesecon:addPosRule(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) 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 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) 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) 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 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
local np = mesecon:addPosRule(pos, rule) local np = mesecon:addPosRule(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) 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 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
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) function mesecon:connected_to_receptor(pos, rulename)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)

@ -3,3 +3,30 @@ minetest.swap_node = minetest.swap_node or function(pos, node)
minetest.add_node(pos, node) minetest.add_node(pos, node)
minetest.get_meta(pos):from_table(data) minetest.get_meta(pos):from_table(data)
end 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

@ -6,19 +6,19 @@ 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
mesecon.on_dignode = function (pos, node) mesecon.on_dignode = function (pos, node)
if mesecon:is_conductor_on(node) then 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 elseif mesecon:is_receptor_on(node.name) then
mesecon:receptor_off(pos, mesecon:receptor_get_rules(node)) mesecon:receptor_off(pos, mesecon:receptor_get_rules(node))
end end

@ -7,3 +7,5 @@ PISTON_MAXIMUM_PUSH = 15
MOVESTONE_MAXIMUM_PUSH = 100 MOVESTONE_MAXIMUM_PUSH = 100
MESECONS_GLOBALSTEP = true -- true = receptors/effectors won't be updated MESECONS_GLOBALSTEP = true -- true = receptors/effectors won't be updated
-- until next globalstep, decreases server load -- 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

@ -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
@ -181,3 +182,14 @@ function mesecon:tablecopy(table) -- deep table copy
return newtable return newtable
end 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

@ -17,28 +17,18 @@ end
-- Functions that are called after the delay time -- 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 delayer_activate = function(pos, node)
local def = minetest.registered_nodes[node.name] local def = minetest.registered_nodes[node.name]
local time = def.delayer_time local time = def.delayer_time
minetest.swap_node(pos, {name = def.delayer_onstate, param2=node.param2}) 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 end
local delayer_deactivate = function(pos, node) local delayer_deactivate = function(pos, node)
local def = minetest.registered_nodes[node.name] local def = minetest.registered_nodes[node.name]
local time = def.delayer_time local time = def.delayer_time
minetest.swap_node(pos, {name = def.delayer_offstate, param2=node.param2}) 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 end
-- Register the 2 (states) x 4 (delay times) delayers -- Register the 2 (states) x 4 (delay times) delayers

@ -23,7 +23,8 @@ function gate_get_input_rules_twoinputs(node)
return gate_rotate_rules(node) return gate_rotate_rules(node)
end end
function update_gate(pos) function update_gate(pos, node, rulename, newstate)
yc_update_real_portstates(pos, node, rulename, newstate)
gate = get_gate(pos) gate = get_gate(pos)
L = rotate_ports( L = rotate_ports(
yc_get_real_portstates(pos), yc_get_real_portstates(pos),

@ -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 -- 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) local get_real_portstates = function(pos) -- determine if ports are powered (by itself or from outside)
ports = { local meta = minetest.get_meta(pos)
a = mesecon:is_power_on(mesecon:addPosRule(pos, rules.a), mesecon:invertRule(rules.a)) local L = {}
and mesecon:rules_link(mesecon:addPosRule(pos, rules.a), pos), local n = meta:get_int("real_portstates") - 1
b = mesecon:is_power_on(mesecon:addPosRule(pos, rules.b), mesecon:invertRule(rules.b)) if n < 0 then
and mesecon:rules_link(mesecon:addPosRule(pos, rules.b), pos), return legacy_update_ports(pos)
c = mesecon:is_power_on(mesecon:addPosRule(pos, rules.c), mesecon:invertRule(rules.c)) end
and mesecon:rules_link(mesecon:addPosRule(pos, rules.c), pos), for _, index in ipairs({"a", "b", "c", "d"}) do
d = mesecon:is_power_on(mesecon:addPosRule(pos, rules.d), mesecon:invertRule(rules.d)) L[index] = ((n%2) == 1)
and mesecon:rules_link(mesecon:addPosRule(pos, rules.d), pos), n = math.floor(n/2)
} end
return ports return L
end end
local merge_portstates = function (ports, vports) local merge_portstates = function (ports, vports)
@ -457,6 +485,7 @@ local mesecons = {
{ {
rules = input_rules[cid], rules = input_rules[cid],
action_change = function (pos, _, rulename, newstate) action_change = function (pos, _, rulename, newstate)
lc_update_real_portstates(pos, rulename, newstate)
lc_update(pos, {type=newstate, pin=rulename}) lc_update(pos, {type=newstate, pin=rulename})
end, end,
}, },

@ -39,7 +39,8 @@ mesecon:add_rules(nodename, rules)
local mesecons = {effector = local mesecons = {effector =
{ {
rules = input_rules, 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) update_yc(pos)
end end
}} }}
@ -633,25 +634,45 @@ function yc_set_portstate(port, state, L)
return L return L
end end
function yc_get_real_portstates(pos) -- port powered or not (by itself or from outside)? function yc_update_real_portstates(pos, node, rulename, newstate)
rulesA = mesecon:get_rules("mesecons_microcontroller:microcontroller0001") local meta = minetest.get_meta(pos)
rulesB = mesecon:get_rules("mesecons_microcontroller:microcontroller0010") if rulename == nil then
rulesC = mesecon:get_rules("mesecons_microcontroller:microcontroller0100") meta:set_int("real_portstates", 1)
rulesD = mesecon:get_rules("mesecons_microcontroller:microcontroller1000") return
L = { end
a = mesecon:is_power_on(mesecon:addPosRule(pos, rulesA[1]), local n = meta:get_int("real_portstates") - 1
mesecon:invertRule(rulesA[1])) and if n < 0 then
mesecon:rules_link(mesecon:addPosRule(pos, rulesA[1]), pos), legacy_update_ports(pos)
b = mesecon:is_power_on(mesecon:addPosRule(pos, rulesB[1]), n = meta:get_int("real_portstates") - 1
mesecon:invertRule(rulesB[1])) and end
mesecon:rules_link(mesecon:addPosRule(pos, rulesB[1]), pos), local L = {}
c = mesecon:is_power_on(mesecon:addPosRule(pos, rulesC[1]), for i = 1, 4 do
mesecon:invertRule(rulesC[1])) and L[i] = n%2
mesecon:rules_link(mesecon:addPosRule(pos, rulesC[1]), pos), n = math.floor(n/2)
d = mesecon:is_power_on(mesecon:addPosRule(pos, rulesD[1]), end
mesecon:invertRule(rulesD[1])) and if rulename.x == nil then
mesecon:rules_link(mesecon:addPosRule(pos, rulesD[1]), pos), 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 return L
end end