Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: effector with conductor #648

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .test_fixtures/mesecons.lua
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,67 @@ do
end
end

-- Utility node: this effector with conductor is used to test outputs.
do
local _rules = {
{x = 1, y = 0, z = 0, name = "000001"},
{x = -1, y = 0, z = 0, name = "000010"},
{x = 0, y = 1, z = 0, name = "000100"},
{x = 0, y = -1, z = 0, name = "001000"},
{x = 0, y = 0, z = 1, name = "010000"},
{x = 0, y = 0, z = -1, name = "100000"},
}
-- This is a list of actions in the form {<kind>, <pos>},
-- where <kind> is "on", "off", or "overheat".
mesecon._test_eff_conductor_events = {}
local function action_on(pos, node)
table.insert(mesecon._test_eff_conductor_events, {"on", pos})
minetest.swap_node(pos, {name = "mesecons:test_effect_conductor_on", param2 = node.param2})
end
local function action_off(pos, node)
table.insert(mesecon._test_eff_conductor_events, {"off", pos})
minetest.swap_node(pos, {name = "mesecons:test_effect_conductor_off", param2 = node.param2})
end
local function action_change(pos, _node, _rule_name, _new_state)
if mesecon.do_overheat(pos) then
table.insert(mesecon._test_eff_conductor_events, {"overheat", pos})
minetest.remove_node(pos)
return
end
-- minetest.swap_node(pos, node)
end
local off_spec = {
effector = {
rules = _rules,
action_on = action_on,
action_off = action_off,
action_change = action_change,
},
conductor = {
state = mesecon.state.off,
rules = _rules,
onstate = "mesecons:test_effect_conductor_on",
}
}
local on_spec = {
effector = {
rules = _rules,
action_on = action_on,
action_off = action_off,
action_change = action_change,
},
conductor = {
state = mesecon.state.on,
rules = _rules,
offstate = "mesecons:test_effect_conductor_off",
}
}
mesecon.register_node("mesecons:test_effect_conductor", {
description = "Test Effector With Conductor",
}, {mesecons = off_spec}, {mesecons = on_spec})
end


mesecon._test_autoconnects = {}
mesecon.register_autoconnect_hook("test", function(pos, node)
table.insert(mesecon._test_autoconnects, {pos, node})
Expand Down
132 changes: 77 additions & 55 deletions mesecons/internal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ function mesecon.get_any_outputrules(node)
end
end

-- TODO: howto get the rules when conductor has rules and effector has rules
function mesecon.get_any_inputrules(node)
if not node then return nil end

Expand Down Expand Up @@ -191,6 +192,12 @@ mesecon.queue:add_function("activate", function (pos, rulename)

if effector and effector.action_on then
effector.action_on(pos, node, rulename)
elseif mesecon.is_conductor(node.name) then
local node_name = mesecon.get_conductor_off(node, rulename)
effector = mesecon.get_effector(node_name)
if effector and effector.action_on then
effector.action_on(pos, node, rulename)
end
end
end)

Expand All @@ -214,6 +221,12 @@ mesecon.queue:add_function("deactivate", function (pos, rulename)

if effector and effector.action_off then
effector.action_off(pos, node, rulename)
elseif mesecon.is_conductor(node.name) then
local node_name = mesecon.get_conductor_on(node, rulename)
effector = mesecon.get_effector(node_name)
if effector and effector.action_off then
effector.action_off(pos, node, rulename)
end
end
end)

Expand Down Expand Up @@ -433,34 +446,38 @@ function mesecon.turnon(pos, link)
if not node then
-- Area does not exist; do nothing
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
elseif mesecon.is_conductor(node.name) then
local rules = mesecon.conductor_get_rules(node)

if mesecon.is_conductor_off(node, f.link) then
-- Call turnon on neighbors
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
local np = vector.add(f.pos, r)
if not pos_can_be_skipped[minetest.hash_node_position(np)] then
for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
frontiers:add({pos = np, link = l})
else
local processed = false
local isEffector = mesecon.is_effector(node.name)
local isOff = true
if mesecon.is_conductor(node.name) then
local rules = mesecon.conductor_get_rules(node)
isOff = mesecon.is_conductor_off(node, f.link)
if isOff then
-- Call turnon on neighbors
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
local np = vector.add(f.pos, r)
if not pos_can_be_skipped[minetest.hash_node_position(np)] then
for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
frontiers:add({pos = np, link = l})
end
end
end
end

mesecon.swap_node_force(f.pos, mesecon.get_conductor_on(node, f.link), light_update_conductors[node.name] ~= nil)
end
mesecon.swap_node_force(f.pos, mesecon.get_conductor_on(node, f.link), light_update_conductors[node.name] ~= nil)
end

-- Only conductors with flat rules can be reliably skipped later
if not rules[1] or rules[1].x then
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
-- Only conductors with flat rules can be reliably skipped later
processed = rules[1] and not rules[1].x
end
elseif mesecon.is_effector(node.name) then
mesecon.changesignal(f.pos, node, f.link, mesecon.state.on, depth)
if mesecon.is_effector_off(node.name) then
mesecon.activate(f.pos, node, f.link, depth)
if isEffector and isOff then
processed = true
mesecon.changesignal(f.pos, node, f.link, mesecon.state.on, depth)
if mesecon.is_effector_off(node.name) then
mesecon.activate(f.pos, node, f.link, depth)
end
end
else
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
if not processed then pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true end
end
depth = depth + 1
end
Expand Down Expand Up @@ -497,46 +514,51 @@ function mesecon.turnoff(pos, link)
if not node then
-- Area does not exist; do nothing
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
elseif mesecon.is_conductor(node.name) then
local rules = mesecon.conductor_get_rules(node)

if mesecon.is_conductor_on(node, f.link) then
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
local np = vector.add(f.pos, r)

if not pos_can_be_skipped[minetest.hash_node_position(np)] then
-- 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:
if mesecon.rules_link_rule_all_inverted(f.pos, r)[1] then
if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then
return false
else
local processed = false
local isEffector = mesecon.is_effector(node.name)
local isOn = true
if mesecon.is_conductor(node.name) then
local rules = mesecon.conductor_get_rules(node)
isOn = mesecon.is_conductor_on(node, f.link)
if isOn then
for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
local np = vector.add(f.pos, r)

if not pos_can_be_skipped[minetest.hash_node_position(np)] then
-- 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:
if mesecon.rules_link_rule_all_inverted(f.pos, r)[1] then
if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then
return false
end
end
end

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

mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link), light_update_conductors[node.name] ~= nil)
end
mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link), light_update_conductors[node.name] ~= nil)
end

-- Only conductors with flat rules can be reliably skipped later
if not rules[1] or rules[1].x then
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
-- Only conductors with flat rules can be reliably skipped later
processed = rules[1] and not rules[1].x
end

if isEffector and isOn then
processed = true
table.insert(signals, {
pos = f.pos,
node = node,
link = f.link,
depth = depth
})
end
elseif mesecon.is_effector(node.name) then
table.insert(signals, {
pos = f.pos,
node = node,
link = f.link,
depth = depth
})
else
pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true
if not processed then pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true end
end
depth = depth + 1
end
Expand Down
42 changes: 42 additions & 0 deletions mesecons/spec/effector_conductor_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require("mineunit")

fixture("mesecons")


describe("action effector with conductor", function()
local layout = {
{{x = 1, y = 0, z = 0}, "mesecons:test_receptor_off"},
{{x = 0, y = 0, z = 0}, "mesecons:test_conductor_off"},
{{x = 0, y = 1, z = 0}, "mesecons:test_effect_conductor_off"},
{{x = 0, y = 2, z = 0}, "mesecons:test_effect_conductor_off"},
}

before_each(function()
world.layout(layout)
end)

after_each(function()
mesecon._test_reset()
world.clear()
end)

it("executes in order", function()
world.set_node(layout[1][1], "mesecons:test_receptor_on")
mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs)
mineunit:execute_globalstep() -- Execute receptor_on action
mineunit:execute_globalstep() -- Execute activate/change actions
assert.equal(2, #mesecon._test_eff_conductor_events)
assert.same({"on", layout[3][1]}, mesecon._test_eff_conductor_events[1])
assert.same({"on", layout[4][1]}, mesecon._test_eff_conductor_events[2])
world.set_node(layout[1][1], "mesecons:test_receptor_off")
mesecon.receptor_off(layout[1][1], mesecon.rules.alldirs)
mineunit:execute_globalstep() -- Execute receptor_off action
mineunit:execute_globalstep() -- Execute deactivate/change actions
assert.equal(4, #mesecon._test_eff_conductor_events)
assert.same({"off", layout[3][1]}, mesecon._test_eff_conductor_events[3])
assert.same({"off", layout[4][1]}, mesecon._test_eff_conductor_events[4])

end)


end)