forked from Mirrorlandia_minetest/mesecons
First draft of some kind of Action Queue (just like the globalstep queue in to_update), but more flexible and also including delay functionality (mesecon_delayer).
The queue is also saved to a file, so that when restarting mesecons, delayers resume to the state they had when the game shut down. Needs testing.
This commit is contained in:
parent
3f76b77001
commit
2d004b19ea
90
mesecons/actionqueue.lua
Normal file
90
mesecons/actionqueue.lua
Normal file
@ -0,0 +1,90 @@
|
||||
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
|
||||
function mesecon.queue:add_action(pos, func, params, time, overwritecheck)
|
||||
-- Create Action Table:
|
||||
time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution
|
||||
action = { pos=pos,
|
||||
func=func,
|
||||
params=mesecon:tablecopy(params),
|
||||
time=time,
|
||||
owcheck=overwritecheck}
|
||||
|
||||
--print(dump(action))
|
||||
-- if not using the queue, (MESECONS_GLOBALSTEP off), just execute the function an we're done
|
||||
if not MESECONS_GLOBALSTEP then
|
||||
mesecon.queue:execute(action)
|
||||
return
|
||||
end
|
||||
|
||||
-- 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, action.pos)
|
||||
and mesecon:cmpAny(overwritecheck, ac.owcheck)) then
|
||||
table.remove(mesecon.queue.actions, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
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 m_time = 0
|
||||
minetest.register_globalstep(function (dtime)
|
||||
m_time = m_time + dtime
|
||||
if (m_time < 5) then return end -- don't even try if server has not been running for 2 seconds
|
||||
local actions = mesecon:tablecopy(mesecon.queue.actions)
|
||||
mesecon.queue.actions = {}
|
||||
|
||||
for i, action in ipairs(actions) do
|
||||
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
|
||||
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
|
||||
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,31 +76,33 @@ 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
|
||||
|
||||
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
|
||||
mesecon.queue:add_action(pos, "receptor_on", {rules})
|
||||
end
|
||||
local rulenames = mesecon:rules_link_rule_all(pos, rule)
|
||||
for _, rulename in ipairs(rulenames) do
|
||||
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})
|
||||
end
|
||||
|
||||
function mesecon:receptor_off_i(pos, rules)
|
||||
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
|
||||
mesecon.queue:add_action(pos, "receptor_off", {rules})
|
||||
end
|
||||
local rulenames = mesecon:rules_link_rule_all(pos, rule)
|
||||
for _, rulename in ipairs(rulenames) do
|
||||
if not mesecon:connected_to_receptor(np, mesecon:invertRule(rule)) then
|
||||
@ -135,15 +112,10 @@ function mesecon:receptor_off_i(pos, rules)
|
||||
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})
|
||||
end
|
||||
|
||||
|
||||
|
@ -177,121 +177,76 @@ function mesecon:effector_get_rules(node)
|
||||
return mesecon.rules.default
|
||||
end
|
||||
|
||||
--Signals
|
||||
-- #######################
|
||||
-- # Signals (effectors) #
|
||||
-- #######################
|
||||
|
||||
-- Activation:
|
||||
mesecon.queue:add_function("activate", function (pos, rulename)
|
||||
node = minetest.get_node(pos)
|
||||
effector = mesecon:get_effector(node.name)
|
||||
|
||||
if effector and effector.action_on then
|
||||
effector.action_on(pos, node, rulename)
|
||||
end
|
||||
end)
|
||||
|
||||
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
|
||||
mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename)
|
||||
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
|
||||
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
|
||||
mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename)
|
||||
end
|
||||
|
||||
function mesecon:changesignal(pos, node, rulename, newstate)
|
||||
|
||||
newstate = newstate or "on"
|
||||
--rulename = rulename or mesecon.rules.default
|
||||
if MESECONS_GLOBALSTEP then
|
||||
-- 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)
|
||||
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
|
||||
|
||||
mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, rulename)
|
||||
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
|
||||
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
|
||||
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
|
||||
end
|
||||
end
|
||||
mesecon.to_update[#mesecon.to_update+1] = {pos = pos, action = action, rname = rname}
|
||||
end
|
||||
|
||||
--Rules
|
||||
-- #########
|
||||
-- # Rules # "Database" for rulenames
|
||||
-- #########
|
||||
|
||||
function mesecon:add_rules(name, rules)
|
||||
mesecon.rules[name] = rules
|
||||
|
@ -18,7 +18,7 @@ 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
|
||||
|
@ -181,3 +181,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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user