forked from minetest-mods/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:
		
							
								
								
									
										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) | ||||
| 	if rulename == nil then | ||||
| 		for _,rule in ipairs(mesecon:effector_get_rules(node)) do | ||||
| 			mesecon:activate(pos, node, rule) | ||||
| 		end | ||||
| 		return | ||||
| 	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) | ||||
| 	if rulename == nil then | ||||
| 		for _,rule in ipairs(mesecon:effector_get_rules(node)) do | ||||
| 			mesecon:deactivate(pos, node, rule) | ||||
| 		end | ||||
| 		return | ||||
| 	end | ||||
| 	mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename) | ||||
| end | ||||
|  | ||||
|  | ||||
| -- 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) | ||||
| 	 | ||||
| 	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 | ||||
| 	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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user