Skip to content

Commit

Permalink
Greatly improve performance by making use of VoxelManips in turnoff
Browse files Browse the repository at this point in the history
Instead of seperately looking for onstate receptors along equipotential
sections of the circuit before turning off, do that while already
modifying the VoxelManip. In case an onstate receptor is found, discard
the VoxelManip cache, otherwise commit it after turnoff is completed.
  • Loading branch information
Jeija committed Aug 30, 2016
1 parent 89153f6 commit e561be7
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 95 deletions.
16 changes: 9 additions & 7 deletions mesecons/init.lua
Expand Up @@ -101,8 +101,6 @@ function mesecon.receptor_on(pos, rules)
end

mesecon.queue:add_function("receptor_off", function (pos, rules)
mesecon.vm_begin()

rules = rules or mesecon.rules.default

-- if area (any of the rule targets) is not loaded, keep trying and call this again later
Expand All @@ -118,15 +116,19 @@ mesecon.queue:add_function("receptor_off", function (pos, rules)
local np = vector.add(pos, rule)
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
mesecon.turnoff(np, rulename)
mesecon.vm_begin()
mesecon.changesignal(np, minetest.get_node(np), rulename, mesecon.state.off, 2)

-- Turnoff returns true if turnoff process was successful, no onstate receptor
-- was found along the way. Commit changes that were made in voxelmanip. If turnoff
-- returns true, an onstate receptor was found, abort voxelmanip transaction.
if (mesecon.turnoff(np, rulename)) then
mesecon.vm_commit()
else
mesecon.changesignal(np, minetest.get_node(np), rulename, mesecon.state.off, 2)
mesecon.vm_abort()
end
end
end

mesecon.vm_commit()
end)

function mesecon.receptor_off(pos, rules)
Expand Down
132 changes: 44 additions & 88 deletions mesecons/internal.lua
Expand Up @@ -41,7 +41,6 @@
-- mesecon.turnoff(pos, link) --> link is the input rule that caused calling turnoff, turns off every connected node, iterative
-- mesecon.connected_to_receptor(pos, link) --> Returns true if pos is connected to a receptor directly or via conductors, iterative
-- 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.is_powered(pos) --> Returns true if pos is powered by a receptor or a conductor

-- RULES ROTATION helpers
Expand Down Expand Up @@ -371,6 +370,10 @@ function mesecon.is_power_off(pos, rulename)
return false
end

-- Turn off an equipotential section starting at `pos`, which outputs in the direction of `link`.
-- Breadth-first search. Map is abstracted away in a voxelmanip.
-- Follow all all conductor paths replacing conductors that were already
-- looked at, activating / changing all effectors along the way.
function mesecon.turnon(pos, link)
local frontiers = {{pos = pos, link = link}}

Expand All @@ -384,7 +387,7 @@ function mesecon.turnon(pos, link)
elseif mesecon.is_conductor_off(node, f.link) then
local rules = mesecon.conductor_get_rules(node)

-- call turnon on neighbors
-- Call turnon on neighbors
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
local np = vector.add(f.pos, r)
for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
Expand All @@ -403,8 +406,25 @@ function mesecon.turnon(pos, link)
end
end

-- Turn on an equipotential section starting at `pos`, which outputs in the direction of `link`.
-- Breadth-first search. Map is abstracted away in a voxelmanip.
-- Follow all all conductor paths replacing conductors that were already
-- looked at, deactivating / changing all effectors along the way.
-- In case an onstate receptor is discovered, abort the process by returning false, which will
-- cause `receptor_off` to discard all changes made in the voxelmanip.
-- Contrary to turnon, turnoff has to cache all change and deactivate signals so that they will only
-- be called in the very end when we can be sure that no conductor was found along the path.
--
-- Signal table entry structure:
-- {
-- pos = position of effector,
-- node = node descriptor (name, param1 and param2),
-- link = link the effector is connected to,
-- depth = indicates order in which signals wire fired, higher is later
-- }
function mesecon.turnoff(pos, link)
local frontiers = {{pos = pos, link = link}}
local signals = {}

local depth = 1
while frontiers[1] do
Expand All @@ -415,104 +435,44 @@ function mesecon.turnoff(pos, link)
-- Area does not exist; do nothing
elseif mesecon.is_conductor_on(node, f.link) then
local rules = mesecon.conductor_get_rules(node)

-- call turnoff on neighbors
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
local np = vector.add(f.pos, r)

-- Check if an onstate receptor is connected. If that is the case,
-- abort this turnoff process by returning false. `receptor_off` will
-- discard all the changes that we made in the voxelmanip:
for _, l in ipairs(mesecon.rules_link_rule_all_inverted(f.pos, r)) do
if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then
return false
end
end

-- Call turnoff on neighbors
for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
table.insert(frontiers, {pos = np, link = l})
end
end

mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link))
elseif mesecon.is_effector(node.name) then
mesecon.changesignal(f.pos, node, f.link, mesecon.state.off, depth)
if mesecon.is_effector_on(node.name) and not mesecon.is_powered(f.pos) then
mesecon.deactivate(f.pos, node, f.link, depth)
end
table.insert(signals, {
pos = f.pos,
node = node,
link = f.link,
depth = depth
})
end
depth = depth + 1
end
end

function mesecon.connected_to_receptor(pos, link)
local node = mesecon.get_node_force(pos)
if not node then return false end

-- Check if conductors around are connected
local rules = mesecon.get_any_inputrules(node)
if not rules then return false end

for _, rule in ipairs(mesecon.rule2meta(link, rules)) do
local links = mesecon.rules_link_rule_all_inverted(pos, rule)
for _, l in ipairs(links) do
local np = vector.add(pos, l)
if mesecon.find_receptor_on(np, mesecon.invertRule(l)) then
return true
end
for _, sig in ipairs(signals) do
mesecon.changesignal(sig.pos, sig.node, sig.link, mesecon.state.off, sig.depth)
if mesecon.is_effector_on(sig.node.name) and not mesecon.is_powered(sig.pos) then
mesecon.deactivate(sig.pos, sig.node, sig.link, sig.depth)
end
end

return false
end

function mesecon.find_receptor_on(pos, link)
local frontiers = {{pos = pos, link = link}}
local checked = {}

-- List of positions that have been searched for onstate receptors
local depth = 1
while frontiers[depth] do
local f = frontiers[depth]
local node = mesecon.get_node_force(f.pos)

if not node then return false end
if mesecon.is_receptor_on(node.name) then return true end
if mesecon.is_conductor_on(node, f.link) then
local rules = mesecon.conductor_get_rules(node)

-- call turnoff on neighbors: normal rules
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
local np = vector.add(f.pos, r)

local links = mesecon.rules_link_rule_all_inverted(f.pos, r)
for _, l in ipairs(links) do
local checkedstring = np.x..np.y..np.z..l.x..l.y..l.z
if not checked[checkedstring] then
table.insert(frontiers, {pos = np, link = l})
checked[checkedstring] = true
end
end
end

end
depth = depth + 1
end
end

function mesecon.rules_link(output, input, dug_outputrules) --output/input are positions (outputrules optional, used if node has been dug), second return value: the name of the affected input rule
local outputnode = mesecon.get_node_force(output)
local inputnode = mesecon.get_node_force(input)

local outputrules = dug_outputrules or mesecon.get_any_outputrules(outputnode)
local inputrules = mesecon.get_any_inputrules(inputnode)
if not outputrules or not inputrules then
return
end

for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do
-- Check if output sends to input
if vector.equals(vector.add(output, outputrule), input) then
for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do
-- Check if input accepts from output
if vector.equals(vector.add(input, inputrule), output) then
return true, inputrule
end
end
end
end

return false
return true
end

function mesecon.rules_link_rule_all(output, rule)
Expand Down Expand Up @@ -552,10 +512,6 @@ function mesecon.rules_link_rule_all_inverted(input, rule)
return rules
end

function mesecon.rules_link_anydir(pos1, pos2)
return mesecon.rules_link(pos1, pos2) or mesecon.rules_link(pos2, pos1)
end

function mesecon.is_powered(pos, rule)
local node = mesecon.get_node_force(pos)
local rules = mesecon.get_any_inputrules(node)
Expand Down

0 comments on commit e561be7

Please sign in to comment.