code:
-- WALL BUTTON
-- A button that when pressed emits power for 1 second
-- and then turns off again

mesecon.button_turnoff = function (pos)
	local node = minetest.get_node(pos)
	if node.name ~= "mesecons_button:button_on" then -- has been dug
		return
	end
	minetest.swap_node(pos, {name = "mesecons_button:button_off", param2 = node.param2})
	minetest.sound_play("mesecons_button_pop", { pos = pos }, true)
	local rules = mesecon.rules.buttonlike_get(node)
	mesecon.receptor_off(pos, rules)
end

local use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or nil

minetest.register_node("mesecons_button:button_off", {
	drawtype = "nodebox",
	tiles = {
		"jeija_wall_button_sides.png",
		"jeija_wall_button_sides.png",
		"jeija_wall_button_sides.png",
		"jeija_wall_button_sides.png",
		"jeija_wall_button_sides.png",
		"jeija_wall_button_off.png"
	},
	use_texture_alpha = use_texture_alpha,
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	legacy_wallmounted = true,
	walkable = false,
	on_rotate = mesecon.buttonlike_onrotate,
	sunlight_propagates = true,
	selection_box = {
	type = "fixed",
		fixed = { -6/16, -6/16, 5/16, 6/16, 6/16, 8/16 }
	},
	node_box = {
		type = "fixed",
		fixed = {
		{ -6/16, -6/16, 6/16, 6/16, 6/16, 8/16 },	-- the thin plate behind the button
		{ -4/16, -2/16, 4/16, 4/16, 2/16, 6/16 }	-- the button itself
	}
	},
	groups = {dig_immediate=2, mesecon_needs_receiver = 1},
	description = "Button",
	on_rightclick = function (pos, node)
		minetest.swap_node(pos, {name = "mesecons_button:button_on", param2=node.param2})
		mesecon.receptor_on(pos, mesecon.rules.buttonlike_get(node))
		minetest.sound_play("mesecons_button_push", { pos = pos }, true)
		minetest.get_node_timer(pos):start(1)
	end,
	sounds = mesecon.node_sound.stone,
	mesecons = {receptor = {
		state = mesecon.state.off,
		rules = mesecon.rules.buttonlike_get
	}},
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_button:button_on", {
	drawtype = "nodebox",
	tiles = {
		"jeija_wall_button_sides.png",
		"jeija_wall_button_sides.png",
		"jeija_wall_button_sides.png",
		"jeija_wall_button_sides.png",
		"jeija_wall_button_sides.png",
		"jeija_wall_button_on.png"
	},
	use_texture_alpha = use_texture_alpha,
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	legacy_wallmounted = true,
	walkable = false,
	on_rotate = false,
	light_source = minetest.LIGHT_MAX-7,
	sunlight_propagates = true,
	selection_box = {
		type = "fixed",
		fixed = { -6/16, -6/16, 5/16, 6/16, 6/16, 8/16 }
	},
	node_box = {
	type = "fixed",
	fixed = {
		{ -6/16, -6/16,  6/16, 6/16, 6/16, 8/16 },
		{ -4/16, -2/16, 11/32, 4/16, 2/16, 6/16 }
	}
    },
	groups = {dig_immediate=2, not_in_creative_inventory=1, mesecon_needs_receiver = 1},
	drop = 'mesecons_button:button_off',
	description = "Button",
	sounds = mesecon.node_sound.stone,
	mesecons = {receptor = {
		state = mesecon.state.on,
		rules = mesecon.rules.buttonlike_get
	}},
	on_timer = mesecon.button_turnoff,
	on_blast = mesecon.on_blastnode,
})

minetest.register_craft({
	output = "mesecons_button:button_off 2",
	recipe = {
		{"group:mesecon_conductor_craftable","mesecons_gamecompat:stone"},
	}
})


code:
name = mesecons_button
depends = mesecons, mesecons_gamecompat, mesecons_receiver

repo_name: korkut/MeseCraft

license: mit


code:
There is no crafting recipe as this should only be available for server admins. Quite similar to the Minecraft counterpart. Executes server commands.
It works in inactive blocks.


code:
minetest.register_chatcommand("say", {
	params = "<text>",
	description = "Say <text> as the server",
	privs = {server=true},
	func = function(name, param)
		minetest.chat_send_all(name .. ": " .. param)
	end
})

minetest.register_chatcommand("tell", {
	params = "<name> <text>",
	description = "Say <text> to <name> privately",
	privs = {shout=true},
	func = function(name, param)
		local found, _, target, message = param:find("^([^%s]+)%s+(.*)$")
		if found == nil then
			minetest.chat_send_player(name, "Invalid usage: " .. param)
			return
		end
		if not minetest.get_player_by_name(target) then
			minetest.chat_send_player(name, "Invalid target: " .. target)
		end
		minetest.chat_send_player(target, name .. " whispers: " .. message, false)
	end
})

minetest.register_chatcommand("hp", {
	params = "<name> <value>",
	description = "Set health of <name> to <value> hitpoints",
	privs = {ban=true},
	func = function(name, param)
		local found, _, target, value = param:find("^([^%s]+)%s+(%d+)$")
		if found == nil then
			minetest.chat_send_player(name, "Invalid usage: " .. param)
			return
		end
		local player = minetest.get_player_by_name(target)
		if player then
			player:set_hp(value)
		else
			minetest.chat_send_player(name, "Invalid target: " .. target)
		end
	end
})

local function initialize_data(meta)
	local commands = minetest.formspec_escape(meta:get_string("commands"))
	meta:set_string("formspec",
		"invsize[9,5;]" ..
		"textarea[0.5,0.5;8.5,4;commands;Commands;"..commands.."]" ..
		"label[1,3.8;@nearest, @farthest, and @random are replaced by the respective player names]" ..
		"button_exit[3.3,4.5;2,1;submit;Submit]")
	local owner = meta:get_string("owner")
	if owner == "" then
		owner = "not owned"
	else
		owner = "owned by " .. owner
	end
	meta:set_string("infotext", "Command Block\n" ..
		"(" .. owner .. ")\n" ..
		"Commands: "..commands)
end

local function construct(pos)
	local meta = minetest.get_meta(pos)

	meta:set_string("commands", "tell @nearest Commandblock unconfigured")

	meta:set_string("owner", "")

	initialize_data(meta)
end

local function after_place(pos, placer)
	if placer then
		local meta = minetest.get_meta(pos)
		meta:set_string("owner", placer:get_player_name())
		initialize_data(meta)
	end
end

local function receive_fields(pos, _, fields, sender)
	if not fields.submit then
		return
	end
	local meta = minetest.get_meta(pos)
	local owner = meta:get_string("owner")
	if owner ~= "" and sender:get_player_name() ~= owner then
		return
	end
	meta:set_string("commands", fields.commands)

	initialize_data(meta)
end

local function resolve_commands(commands, pos)
	local players = minetest.get_connected_players()

	-- No players online: remove all commands containing
	-- @nearest, @farthest and @random
	if #players == 0 then
		commands = commands:gsub("[^\r\n]+", function (line)
			if line:find("@nearest") then return "" end
			if line:find("@farthest") then return "" end
			if line:find("@random") then return "" end
			return line
		end)
		return commands
	end

	local nearest, farthest = nil, nil
	local min_distance, max_distance = math.huge, -1
	for index, player in pairs(players) do
		local distance = vector.distance(pos, player:get_pos())
		if distance < min_distance then
			min_distance = distance
			nearest = player:get_player_name()
		end
		if distance > max_distance then
			max_distance = distance
			farthest = player:get_player_name()
		end
	end
	local random = players[math.random(#players)]:get_player_name()
	commands = commands:gsub("@nearest", nearest)
	commands = commands:gsub("@farthest", farthest)
	commands = commands:gsub("@random", random)
	return commands
end

local function commandblock_action_on(pos, node)
	if node.name ~= "mesecons_commandblock:commandblock_off" then
		return
	end

	minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_on"})

	local meta = minetest.get_meta(pos)
	local owner = meta:get_string("owner")
	if owner == "" then
		return
	end

	local commands = resolve_commands(meta:get_string("commands"), pos)
	for _, command in pairs(commands:split("\n")) do
		local pos = command:find(" ")
		local cmd, param = command, ""
		if pos then
			cmd = command:sub(1, pos - 1)
			param = command:sub(pos + 1)
		end
		local cmddef = minetest.chatcommands[cmd]
		if not cmddef then
			minetest.chat_send_player(owner, "The command "..cmd.." does not exist")
			return
		end
		local has_privs, missing_privs = minetest.check_player_privs(owner, cmddef.privs)
		if not has_privs then
			minetest.chat_send_player(owner, "You don't have permission "
					.."to run "..cmd
					.." (missing privileges: "
					..table.concat(missing_privs, ", ")..")")
			return
		end
		cmddef.func(owner, param)
	end
end

local function commandblock_action_off(pos, node)
	if node.name == "mesecons_commandblock:commandblock_on" then
		minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_off"})
	end
end

local function can_dig(pos, player)
	local meta = minetest.get_meta(pos)
	local owner = meta:get_string("owner")
	return owner == "" or owner == player:get_player_name() or
		minetest.check_player_privs(player, "protection_bypass")
end

minetest.register_node("mesecons_commandblock:commandblock_off", {
	description = "Command Block",
	tiles = {"jeija_commandblock_off.png"},
	inventory_image = minetest.inventorycube("jeija_commandblock_off.png"),
	is_ground_content = false,
	groups = {cracky=2, mesecon_effector_off=1},
	on_construct = construct,
	after_place_node = after_place,
	on_receive_fields = receive_fields,
	can_dig = can_dig,
	sounds = mesecon.node_sound.stone,
	mesecons = {effector = {
		action_on = commandblock_action_on
	}},
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_commandblock:commandblock_on", {
	tiles = {"jeija_commandblock_on.png"},
	is_ground_content = false,
	groups = {cracky=2, mesecon_effector_on=1, not_in_creative_inventory=1},
	light_source = 10,
	drop = "mesecons_commandblock:commandblock_off",
	on_construct = construct,
	after_place_node = after_place,
	on_receive_fields = receive_fields,
	can_dig = can_dig,
	sounds = mesecon.node_sound.stone,
	mesecons = {effector = {
		action_off = commandblock_action_off
	}},
	on_blast = mesecon.on_blastnode,
})


code:
name = mesecons_commandblock
depends = mesecons, mesecons_gamecompat


code: The delayer delays the signal from the input for a determined time. The time can be set by punching the delayer. Possible delays are: 0.1 seconds, 0.3 seconds, 0.5 seconds and 1 second. You may try to use it for creating songs with the noteblock. It works in unloaded blocks.


code:
-- Function that get the input/output rules of the delayer
local delayer_get_output_rules = mesecon.horiz_rules_getter({{x = 1, y = 0, z = 0}})

local delayer_get_input_rules = mesecon.horiz_rules_getter({{x = -1, y = 0, z = 0}})

-- Functions that are called after the delay time

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})
	mesecon.queue:add_action(pos, "receptor_on", {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})
	mesecon.queue:add_action(pos, "receptor_off", {delayer_get_output_rules(node)}, time, nil)
end

-- Register the 2 (states) x 4 (delay times) delayers

local delaytime = { 0.1, 0.3, 0.5, 1.0 }

for i = 1, 4 do

local boxes = {
	 { -6/16, -8/16, -6/16, 6/16, -7/16, 6/16 },		-- the main slab

	 { -2/16, -7/16, -4/16, 2/16, -26/64, -3/16 },		-- the jeweled "on" indicator
	 { -3/16, -7/16, -3/16, 3/16, -26/64, -2/16 },
	 { -4/16, -7/16, -2/16, 4/16, -26/64, 2/16 },
	 { -3/16, -7/16,  2/16, 3/16, -26/64, 3/16 },
	 { -2/16, -7/16,  3/16, 2/16, -26/64, 4/16 },

	 { -6/16, -7/16, -6/16, -4/16, -27/64, -4/16 },		-- the timer indicator
	 { -8/16, -8/16, -1/16, -6/16, -7/16, 1/16 },		-- the two wire stubs
	 { 6/16, -8/16, -1/16, 8/16, -7/16, 1/16 }
}

-- Delayer definition defaults
local def = {
	drawtype = "nodebox",
	use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or nil,
	walkable = true,
	selection_box = {
		type = "fixed",
		fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
	},
	node_box = {
		type = "fixed",
		fixed = boxes
	},
	paramtype = "light",
	paramtype2 = "facedir",
	sunlight_propagates = true,
	is_ground_content = false,
	delayer_time = delaytime[i],
	sounds = mesecon.node_sound.stone,
	on_blast = mesecon.on_blastnode,
	drop = "mesecons_delayer:delayer_off_1",
}

-- Deactivated delayer definition defaults
local off_groups = {bendy=2,snappy=1,dig_immediate=2}
if i > 1 then
	off_groups.not_in_creative_inventory = 1
end

local off_state = {
	description = "Delayer",
	tiles = {
		"mesecons_delayer_off_"..tostring(i)..".png",
		"mesecons_delayer_bottom.png",
		"mesecons_delayer_ends_off.png",
		"mesecons_delayer_ends_off.png",
		"mesecons_delayer_sides_off.png",
		"mesecons_delayer_sides_off.png"
	},
	inventory_image = "mesecons_delayer_off_1.png",
	wield_image = "mesecons_delayer_off_1.png",
	groups = off_groups,
	on_punch = function(pos, node, puncher)
		if minetest.is_protected(pos, puncher and puncher:get_player_name() or "") then
			return
		end

		minetest.swap_node(pos, {
			name = "mesecons_delayer:delayer_off_"..tostring(i % 4 + 1),
			param2 = node.param2
		})
	end,
	delayer_onstate = "mesecons_delayer:delayer_on_"..tostring(i),
	mesecons = {
		receptor =
		{
			state = mesecon.state.off,
			rules = delayer_get_output_rules
		},
		effector =
		{
			rules = delayer_get_input_rules,
			action_on = delayer_activate
		}
	},
}
for k, v in pairs(def) do
	off_state[k] = off_state[k] or v
end
minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), off_state)

-- Activated delayer definition defaults
local on_state = {
	description = "You hacker you",
	tiles = {
		"mesecons_delayer_on_"..tostring(i)..".png",
		"mesecons_delayer_bottom.png",
		"mesecons_delayer_ends_on.png",
		"mesecons_delayer_ends_on.png",
		"mesecons_delayer_sides_on.png",
		"mesecons_delayer_sides_on.png"
	},
	groups = {bendy = 2, snappy = 1, dig_immediate = 2, not_in_creative_inventory = 1},
	on_punch = function(pos, node, puncher)
		if minetest.is_protected(pos, puncher and puncher:get_player_name() or "") then
			return
		end

		minetest.swap_node(pos, {
			name = "mesecons_delayer:delayer_on_"..tostring(i % 4 + 1),
			param2 = node.param2
		})
	end,
	delayer_offstate = "mesecons_delayer:delayer_off_"..tostring(i),
	mesecons = {
		receptor =
		{
			state = mesecon.state.on,
			rules = delayer_get_output_rules
		},
		effector =
		{
			rules = delayer_get_input_rules,
			action_off = delayer_deactivate
		}
	},
}
for k, v in pairs(def) do
	on_state[k] = on_state[k] or v
end
minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), on_state)

end

minetest.register_craft({
	output = "mesecons_delayer:delayer_off_1",
	recipe = {
		{"mesecons_torch:mesecon_torch_on", "group:mesecon_conductor_craftable", "mesecons_torch:mesecon_torch_on"},
		{"mesecons_gamecompat:cobble","mesecons_gamecompat:cobble", "mesecons_gamecompat:cobble"},
	}
})


code:
name = mesecons_delayer
depends = mesecons, mesecons_gamecompat


code:
The node detector is a receptor. It changes its state when either any node
or a specific node is detected. Right-click it to set a nodename to scan for.
It can also receive digiline signals. For example, you can send
{distance=4, scanname="default:dirt"}
to set distance to 4 and scan for dirt. You can omit either parameter.
There is also a command parameter: {command="get"} will respond
with the detected nodename and {command="scan"} will respond with
a boolean using the distance and nodename of the detector.
Nodenames must include the mod they reside in, so for instance default:dirt, not just dirt.
The distance parameter specifies how many blocks are between the node detector and the node to detect.
Automatic scanning with Mesecons output only works when the detector is in an active block, but Digilines queries always work.


code:
The object detector is a receptor. It changes its state when a player approaches.
Right-click it to set a name to scan for.
You can also search for comma-separated lists of players where the detector gets activated if any of the names in the list are found.
It can also receive digiline signals which are the name to scan for on the specified channel in the right-click menu.
Automatic scanning with Mesecons output only works when the detector is in an active block, but Digilines queries always work.


code:
local side_texture = mesecon.texture.steel_block or "mesecons_detector_side.png"

local GET_COMMAND = "GET"

-- Object detector
-- Detects players in a certain radius
-- The radius can be specified in mesecons/settings.lua

local function object_detector_make_formspec(pos)
	local meta = minetest.get_meta(pos)
	meta:set_string("formspec", "size[9,2.5]" ..
		"field[0.3,  0;9,2;scanname;Name of player to scan for (empty for any):;${scanname}]"..
		"field[0.3,1.5;4,2;digiline_channel;Digiline Channel (optional):;${digiline_channel}]"..
		"button_exit[7,0.75;2,3;;Save]")
end

local function object_detector_on_receive_fields(pos, _, fields, sender)
	if not fields.scanname or not fields.digiline_channel then return end

	if minetest.is_protected(pos, sender:get_player_name()) then return end

	local meta = minetest.get_meta(pos)
	meta:set_string("scanname", fields.scanname)
	meta:set_string("digiline_channel", fields.digiline_channel)
	object_detector_make_formspec(pos)
end

-- returns true if player was found, false if not
local function object_detector_scan(pos)
	local objs = minetest.get_objects_inside_radius(pos, mesecon.setting("detector_radius", 6))

	-- abort if no scan results were found
	if next(objs) == nil then return false end

	local scanname = minetest.get_meta(pos):get_string("scanname")
	local scan_for = {}
	for _, str in pairs(string.split(scanname:gsub(" ", ""), ",")) do
		scan_for[str] = true
	end

	local every_player = scanname == ""
	for _, obj in pairs(objs) do
		-- "" is returned if it is not a player; "" ~= nil; so only handle objects with foundname ~= ""
		local foundname = obj:get_player_name()
		if foundname ~= "" then
			if every_player or scan_for[foundname] then
				return true
			end
		end
	end

	return false
end

-- set player name when receiving a digiline signal on a specific channel
local object_detector_digiline = {
	effector = {
		action = function(pos, _, channel, msg)
			local meta = minetest.get_meta(pos)
			if channel == meta:get_string("digiline_channel") then
				meta:set_string("scanname", msg)
				object_detector_make_formspec(pos)
			end
		end,
	}
}

minetest.register_node("mesecons_detector:object_detector_off", {
	tiles = {side_texture, side_texture, "jeija_object_detector_off.png", "jeija_object_detector_off.png", "jeija_object_detector_off.png", "jeija_object_detector_off.png"},
	paramtype = "light",
	is_ground_content = false,
	walkable = true,
	groups = {cracky=3},
	description="Player Detector",
	mesecons = {receptor = {
		state = mesecon.state.off,
		rules = mesecon.rules.pplate
	}},
	on_construct = object_detector_make_formspec,
	on_receive_fields = object_detector_on_receive_fields,
	sounds = mesecon.node_sound.stone,
	digiline = object_detector_digiline,
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_detector:object_detector_on", {
	tiles = {side_texture, side_texture, "jeija_object_detector_on.png", "jeija_object_detector_on.png", "jeija_object_detector_on.png", "jeija_object_detector_on.png"},
	paramtype = "light",
	is_ground_content = false,
	walkable = true,
	groups = {cracky=3,not_in_creative_inventory=1},
	drop = 'mesecons_detector:object_detector_off',
	mesecons = {receptor = {
		state = mesecon.state.on,
		rules = mesecon.rules.pplate
	}},
	on_construct = object_detector_make_formspec,
	on_receive_fields = object_detector_on_receive_fields,
	sounds = mesecon.node_sound.stone,
	digiline = object_detector_digiline,
	on_blast = mesecon.on_blastnode,
})

minetest.register_craft({
	output = 'mesecons_detector:object_detector_off',
	recipe = {
		{"mesecons_gamecompat:steel_ingot", "mesecons_gamecompat:steel_ingot", "mesecons_gamecompat:steel_ingot"},
		{"mesecons_gamecompat:steel_ingot", "mesecons_luacontroller:luacontroller0000", "mesecons_gamecompat:steel_ingot"},
		{"mesecons_gamecompat:steel_ingot", "group:mesecon_conductor_craftable", "mesecons_gamecompat:steel_ingot"},
	}
})

minetest.register_craft({
	output = 'mesecons_detector:object_detector_off',
	recipe = {
		{"mesecons_gamecompat:steel_ingot", "mesecons_gamecompat:steel_ingot", "mesecons_gamecompat:steel_ingot"},
		{"mesecons_gamecompat:steel_ingot", "mesecons_microcontroller:microcontroller0000", "mesecons_gamecompat:steel_ingot"},
		{"mesecons_gamecompat:steel_ingot", "group:mesecon_conductor_craftable", "mesecons_gamecompat:steel_ingot"},
	}
})

minetest.register_abm({
	nodenames = {"mesecons_detector:object_detector_off"},
	interval = 1,
	chance = 1,
	action = function(pos, node)
		if not object_detector_scan(pos) then return end

		node.name = "mesecons_detector:object_detector_on"
		minetest.swap_node(pos, node)
		mesecon.receptor_on(pos, mesecon.rules.pplate)
	end,
})

minetest.register_abm({
	nodenames = {"mesecons_detector:object_detector_on"},
	interval = 1,
	chance = 1,
	action = function(pos, node)
		if object_detector_scan(pos) then return end

		node.name = "mesecons_detector:object_detector_off"
		minetest.swap_node(pos, node)
		mesecon.receptor_off(pos, mesecon.rules.pplate)
	end,
})

-- Node detector
-- Detects the node in front of it

local function node_detector_make_formspec(pos)
	local meta = minetest.get_meta(pos)
	if meta:get_string("distance") == ""  then meta:set_string("distance", "0") end
	meta:set_string("formspec", "size[9,2.5]" ..
		"field[0.3,  0;9,2;scanname;Name of node to scan for (empty for any):;${scanname}]"..
		"field[0.3,1.5;2.5,2;distance;Distance (0-"..mesecon.setting("node_detector_distance_max", 10).."):;${distance}]"..
		"field[3,1.5;4,2;digiline_channel;Digiline Channel (optional):;${digiline_channel}]"..
		"button_exit[7,0.75;2,3;;Save]")
end

local function node_detector_on_receive_fields(pos, _, fields, sender)
	if not fields.scanname or not fields.digiline_channel then return end

	if minetest.is_protected(pos, sender:get_player_name()) then return end

	local meta = minetest.get_meta(pos)
	meta:set_string("scanname", fields.scanname)
	meta:set_string("distance", fields.distance or "0")
	meta:set_string("digiline_channel", fields.digiline_channel)
	node_detector_make_formspec(pos)
end

-- returns true if node was found, false if not
local function node_detector_scan(pos)
	local node = minetest.get_node_or_nil(pos)
	if not node then return end

	local meta = minetest.get_meta(pos)

	local distance = meta:get_int("distance")
	local distance_max = mesecon.setting("node_detector_distance_max", 10)
	if distance < 0 then distance = 0 end
	if distance > distance_max then distance = distance_max end

	local frontname = minetest.get_node(
		vector.subtract(pos, vector.multiply(minetest.facedir_to_dir(node.param2), distance + 1))
	).name
	local scanname = meta:get_string("scanname")

	return (frontname == scanname) or
		(frontname ~= "air" and frontname ~= "ignore" and scanname == "")
end

local function node_detector_send_node_name(pos, node, channel, meta)
	local distance = meta:get_int("distance")
	local distance_max = mesecon.setting("node_detector_distance_max", 10)
	if distance < 0 then distance = 0 end
	if distance > distance_max then distance = distance_max end
	local nodename = minetest.get_node(
		vector.subtract(pos, vector.multiply(minetest.facedir_to_dir(node.param2), distance + 1))
	).name

	digiline:receptor_send(pos, digiline.rules.default, channel, nodename)
end

-- set player name when receiving a digiline signal on a specific channel
local node_detector_digiline = {
	effector = {
		action = function(pos, node, channel, msg)
			local meta = minetest.get_meta(pos)

			if channel ~= meta:get_string("digiline_channel") then return end

			if type(msg) == "table" then
				if msg.distance or msg.scanname then
					if msg.distance then
						meta:set_string("distance", msg.distance)
					end
					if msg.scanname then
						meta:set_string("scanname", msg.scanname)
					end
					node_detector_make_formspec(pos)
				end
				if msg.command == "get" then
					node_detector_send_node_name(pos, node, channel, meta)
				elseif msg.command == "scan" then
					local result = node_detector_scan(pos)
					digiline:receptor_send(pos, digiline.rules.default, channel, result)
				end
			else
				if msg == GET_COMMAND then
					node_detector_send_node_name(pos, node, channel, meta)
				else
					meta:set_string("scanname", msg)
					node_detector_make_formspec(pos)
				end
			end
		end,
	},
	receptor = {}
}

minetest.register_node("mesecons_detector:node_detector_off", {
	tiles = {side_texture, side_texture, side_texture, side_texture, side_texture, "jeija_node_detector_off.png"},
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	walkable = true,
	groups = {cracky=3},
	description="Node Detector",
	mesecons = {receptor = {
		state = mesecon.state.off
	}},
	on_construct = node_detector_make_formspec,
	on_receive_fields = node_detector_on_receive_fields,
	sounds = mesecon.node_sound.stone,
	digiline = node_detector_digiline,
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_detector:node_detector_on", {
	tiles = {side_texture, side_texture, side_texture, side_texture, side_texture, "jeija_node_detector_on.png"},
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	walkable = true,
	groups = {cracky=3,not_in_creative_inventory=1},
	drop = 'mesecons_detector:node_detector_off',
	mesecons = {receptor = {
		state = mesecon.state.on
	}},
	on_construct = node_detector_make_formspec,
	on_receive_fields = node_detector_on_receive_fields,
	sounds = mesecon.node_sound.stone,
	digiline = node_detector_digiline,
	on_blast = mesecon.on_blastnode,
})

minetest.register_craft({
	output = 'mesecons_detector:node_detector_off',
	recipe = {
		{"mesecons_gamecompat:steel_ingot", "group:mesecon_conductor_craftable", "mesecons_gamecompat:steel_ingot"},
		{"mesecons_gamecompat:steel_ingot", "mesecons_luacontroller:luacontroller0000", "mesecons_gamecompat:steel_ingot"},
		{"mesecons_gamecompat:steel_ingot", "mesecons_gamecompat:steel_ingot", "mesecons_gamecompat:steel_ingot"},
	}
})

minetest.register_craft({
	output = 'mesecons_detector:node_detector_off',
	recipe = {
		{"mesecons_gamecompat:steel_ingot", "group:mesecon_conductor_craftable", "mesecons_gamecompat:steel_ingot"},
		{"mesecons_gamecompat:steel_ingot", "mesecons_microcontroller:microcontroller0000", "mesecons_gamecompat:steel_ingot"},
		{"mesecons_gamecompat:steel_ingot", "mesecons_gamecompat:steel_ingot", "mesecons_gamecompat:steel_ingot"},
	}
})

minetest.register_abm({
	nodenames = {"mesecons_detector:node_detector_off"},
	interval = 1,
	chance = 1,
	action = function(pos, node)
		if not node_detector_scan(pos) then return end

		node.name = "mesecons_detector:node_detector_on"
		minetest.swap_node(pos, node)
		mesecon.receptor_on(pos)
	end,
})

minetest.register_abm({
	nodenames = {"mesecons_detector:node_detector_on"},
	interval = 1,
	chance = 1,
	action = function(pos, node)
		if node_detector_scan(pos) then return end

		node.name = "mesecons_detector:node_detector_off"
		minetest.swap_node(pos, node)
		mesecon.receptor_off(pos)
	end,
})


code:
name = mesecons_detector
depends = mesecons, mesecons_gamecompat, mesecons_materials


code:
-- Modified, from minetest_game/mods/doors/init.lua
local function on_rightclick(pos, dir, check_name, replace, replace_dir, params)
	pos.y = pos.y + dir
	if minetest.get_node(pos).name ~= check_name then
		return
	end
	local p2 = minetest.get_node(pos).param2
	p2 = params[p2 + 1]

	minetest.swap_node(pos, {name = replace_dir, param2 = p2})

	pos.y = pos.y - dir
	minetest.swap_node(pos, {name = replace, param2 = p2})

	if (minetest.get_meta(pos):get_int("right") ~= 0) == (params[1] ~= 3) then
		minetest.sound_play("doors_door_close", { pos = pos, gain = 0.3, max_hear_distance = 10 }, true)
	else
		minetest.sound_play("doors_door_open", { pos = pos, gain = 0.3, max_hear_distance = 10 }, true)
	end
end

local function meseconify_door(name)
	if minetest.registered_items[name .. "_b_1"] then
		-- old style double-node doors
		local function toggle_state1 (pos)
			on_rightclick(pos, 1, name.."_t_1", name.."_b_2", name.."_t_2", {1,2,3,0})
		end

		local function toggle_state2 (pos)
			on_rightclick(pos, 1, name.."_t_2", name.."_b_1", name.."_t_1", {3,0,1,2})
		end

		minetest.override_item(name.."_b_1", {
			mesecons = {effector = {
				action_on = toggle_state1,
				action_off = toggle_state1,
				rules = mesecon.rules.pplate
			}}
		})

		minetest.override_item(name.."_b_2", {
			mesecons = {effector = {
				action_on = toggle_state2,
				action_off = toggle_state2,
				rules = mesecon.rules.pplate
			}}
		})
	elseif minetest.registered_items[name .. "_a"] then
		-- new style mesh node based doors
		local override = {
			mesecons = {effector = {
				action_on = function(pos)
					local door = doors.get(pos)
					if door then
						door:open()
					end
				end,
				action_off = function(pos)
					local door = doors.get(pos)
					if door then
						door:close()
					end
				end,
				rules = mesecon.rules.pplate
			}}
		}
		minetest.override_item(name .. "_a", override)
		minetest.override_item(name .. "_b", override)
		if minetest.registered_items[name .. "_c"] then
			minetest.override_item(name .. "_c", override)
			minetest.override_item(name .. "_d", override)
		end
	end
end

meseconify_door("doors:door_wood")
meseconify_door("doors:door_steel")
meseconify_door("doors:door_glass")
meseconify_door("doors:door_obsidian_glass")
meseconify_door("xpanes:door_steel_bar")

-- Trapdoor
local function trapdoor_switch(pos, node)
	local state = minetest.get_meta(pos):get_int("state")

	if state == 1 then
		minetest.sound_play("doors_door_close", { pos = pos, gain = 0.3, max_hear_distance = 10 }, true)
		minetest.set_node(pos, {name="doors:trapdoor", param2 = node.param2})
	else
		minetest.sound_play("doors_door_open", { pos = pos, gain = 0.3, max_hear_distance = 10 }, true)
		minetest.set_node(pos, {name="doors:trapdoor_open", param2 = node.param2})
	end

	minetest.get_meta(pos):set_int("state", state == 1 and 0 or 1)
end

if doors and doors.get then
	local override = {
		mesecons = {effector = {
			action_on = function(pos)
				local door = doors.get(pos)
				if door then
					door:open()
				end
			end,
			action_off = function(pos)
				local door = doors.get(pos)
				if door then
					door:close()
				end
			end,
		}},
	}
	minetest.override_item("doors:trapdoor", override)
	minetest.override_item("doors:trapdoor_open", override)
	minetest.override_item("doors:trapdoor_steel", override)
	minetest.override_item("doors:trapdoor_steel_open", override)

	if minetest.registered_items["xpanes:trapdoor_steel_bar"] then
		minetest.override_item("xpanes:trapdoor_steel_bar", override)
		minetest.override_item("xpanes:trapdoor_steel_bar_open", override)
	end

else
	if minetest.registered_nodes["doors:trapdoor"] then
		minetest.override_item("doors:trapdoor", {
			mesecons = {effector = {
				action_on = trapdoor_switch,
				action_off = trapdoor_switch
			}},
		})

		minetest.override_item("doors:trapdoor_open", {
			mesecons = {effector = {
				action_on = trapdoor_switch,
				action_off = trapdoor_switch
			}},
		})
	end
end


code:
name = mesecons_doors
depends = mesecons, doors
optional_depends = xpanes


code:
local corner_selectionbox = {
		type = "fixed",
		fixed = { -16/32, -16/32, -16/32, 5/32, -12/32, 5/32 },
}

local corner_get_rules = mesecon.horiz_rules_getter({
	{x = 0, y = 0, z = -1},
	{x = -1, y = 0, z = 0},
})

minetest.register_node("mesecons_extrawires:corner_on", {
	drawtype = "mesh",
	mesh = "mesecons_extrawires_corner.obj",
	tiles = {
		{ name = "jeija_insulated_wire_sides_on.png", backface_culling = true },
		{ name = "jeija_insulated_wire_ends_on.png", backface_culling = true },
	},
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	walkable = false,
	sunlight_propagates = true,
	selection_box = corner_selectionbox,
	groups = {dig_immediate = 3, not_in_creative_inventory = 1},
	drop = "mesecons_extrawires:corner_off",
	sounds = mesecon.node_sound.default,
	mesecons = {conductor =
	{
		state = mesecon.state.on,
		rules = corner_get_rules,
		offstate = "mesecons_extrawires:corner_off"
	}},
	on_blast = mesecon.on_blastnode,
	on_rotate = mesecon.on_rotate_horiz,
})

minetest.register_node("mesecons_extrawires:corner_off", {
	drawtype = "mesh",
	description = "Insulated Mesecon Corner",
	mesh = "mesecons_extrawires_corner.obj",
	tiles = {
		{ name = "jeija_insulated_wire_sides_off.png", backface_culling = true },
		{ name = "jeija_insulated_wire_ends_off.png", backface_culling = true },
	},
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	walkable = false,
	sunlight_propagates = true,
	selection_box = corner_selectionbox,
	groups = {dig_immediate = 3},
	sounds = mesecon.node_sound.default,
	mesecons = {conductor =
	{
		state = mesecon.state.off,
		rules = corner_get_rules,
		onstate = "mesecons_extrawires:corner_on"
	}},
	on_blast = mesecon.on_blastnode,
	on_rotate = mesecon.on_rotate_horiz,
})

minetest.register_craft({
	output = "mesecons_extrawires:corner_off 3",
	recipe = {
		{"mesecons_insulated:insulated_off", "mesecons_insulated:insulated_off"},
		{"", "mesecons_insulated:insulated_off"},
	}
})


code:
local crossover_rules = {
	{--first wire
		{x=-1,y=0,z=0},
		{x=1,y=0,z=0},
	},
	{--second wire
		{x=0,y=0,z=-1},
		{x=0,y=0,z=1},
	},
}

local crossover_states = {
	"mesecons_extrawires:crossover_off",
	"mesecons_extrawires:crossover_01",
	"mesecons_extrawires:crossover_10",
	"mesecons_extrawires:crossover_on",
}

minetest.register_node("mesecons_extrawires:crossover_off", {
	description = "Insulated Mesecon Crossover",
	drawtype = "mesh",
	mesh = "mesecons_extrawires_crossover.b3d",
	tiles = {
		"jeija_insulated_wire_ends_off.png",
		"jeija_insulated_wire_sides_off.png",
		"jeija_insulated_wire_sides_off.png",
		"jeija_insulated_wire_ends_off.png"
	},
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	stack_max = 99,
	selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
	groups = {dig_immediate=3, mesecon=3},
	sounds = mesecon.node_sound.default,
	mesecons = {
		conductor = {
			states = crossover_states,
			rules = crossover_rules,
		}
	},
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_extrawires:crossover_01", {
	description = "You hacker you!",
	drop = "mesecons_extrawires:crossover_off",
	drawtype = "mesh",
	mesh = "mesecons_extrawires_crossover.b3d",
	tiles = {
		"jeija_insulated_wire_ends_on.png",
		"jeija_insulated_wire_sides_on.png",
		"jeija_insulated_wire_sides_off.png",
		"jeija_insulated_wire_ends_off.png"
	},
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	stack_max = 99,
	selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
	groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1},
	sounds = mesecon.node_sound.default,
	mesecons = {
		conductor = {
			states = crossover_states,
			rules = crossover_rules,
		}
	},
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_extrawires:crossover_10", {
	description = "You hacker you!",
	drop = "mesecons_extrawires:crossover_off",
	drawtype = "mesh",
	mesh = "mesecons_extrawires_crossover.b3d",
	tiles = {
		"jeija_insulated_wire_ends_off.png",
		"jeija_insulated_wire_sides_off.png",
		"jeija_insulated_wire_sides_on.png",
		"jeija_insulated_wire_ends_on.png"
	},
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	stack_max = 99,
	selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
	groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1},
	sounds = mesecon.node_sound.default,
	mesecons = {
		conductor = {
			states = crossover_states,
			rules = crossover_rules,
		}
	},
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_extrawires:crossover_on", {
	description = "You hacker you!",
	drop = "mesecons_extrawires:crossover_off",
	drawtype = "mesh",
	mesh = "mesecons_extrawires_crossover.b3d",
	tiles = {
		"jeija_insulated_wire_ends_on.png",
		"jeija_insulated_wire_sides_on.png",
		"jeija_insulated_wire_sides_on.png",
		"jeija_insulated_wire_ends_on.png"
	},
	paramtype = "light",
	is_ground_content = false,
	walkable = false,
	stack_max = 99,
	selection_box = {type="fixed", fixed={-16/32, -16/32, -16/32, 16/32, -5/32, 16/32}},
	groups = {dig_immediate=3, mesecon=3, not_in_creative_inventory=1},
	sounds = mesecon.node_sound.default,
	mesecons = {
		conductor = {
			states = crossover_states,
			rules = crossover_rules,
		}
	},
	on_blast = mesecon.on_blastnode,
})

minetest.register_craft({
	type = "shapeless",
	output = "mesecons_extrawires:crossover_off",
	recipe = {
		"mesecons_insulated:insulated_off",
		"mesecons_insulated:insulated_off",
	},
})

minetest.register_craft({
	type = "shapeless",
	output = "mesecons_insulated:insulated_off 2",
	recipe = {
		"mesecons_extrawires:crossover_off",
	},
})


code: Insulated corners are conductors that only conduct between the inputs (also not up or down). When placing they always point to the left in direction of your vision. Like uninsulated wires, they work through unloaded blocks.


code: Insulated crossing are conductors that conduct two signals between the opposing sides, the signals are insulated to each other. Like uninsulated wires, they work through unloaded blocks.


code: The basic prerequesite for mesecons, can be crafted into wires and other stuff. Have a look at the Minetest Wiki for more information. Mese is a conductor. It conducts in all six directions: Up/Down/Left/Right/Forward/Backward. Like horizontal wires, Mese conduction works through unloaded blocks.


code: Insulated T-Junctions are conductors that only conduct between the inputs (also not up or down). Like uninsulated wires, they work through unloaded blocks.


code: Vertical Mesecons only conduct up and down. Plates appear at the ends, at that place they also conduct to the side. Like horizontal wires, they work through unloaded blocks.


code:
local doublecorner_selectionbox = {
	type = "fixed",
	fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
}

local doublecorner_get_rules = mesecon.horiz_rules_getter({
	{
		{ x = 1, y = 0, z = 0 },
		{ x = 0, y = 0, z = 1 },
	},
	{
		{ x = -1, y = 0, z = 0 },
		{ x = 0, y = 0, z = -1 },
	},
})

local doublecorner_states = {
	"mesecons_extrawires:doublecorner_00",
	"mesecons_extrawires:doublecorner_01",
	"mesecons_extrawires:doublecorner_10",
	"mesecons_extrawires:doublecorner_11",
}
local wire1_states = { "off", "off", "on", "on" }
local wire2_states = { "off", "on", "off", "on" }

for k, state in ipairs(doublecorner_states) do
	local w1 = wire1_states[k]
	local w2 = wire2_states[k]
	local groups =  { dig_immediate = 3 }
	if k ~= 1 then groups.not_in_creative_inventory = 1 end
	minetest.register_node(state, {
		drawtype = "mesh",
		mesh = "mesecons_extrawires_doublecorner.obj",
		description = "Insulated Mesecon Double Corner",
		tiles = {
			{ name = "jeija_insulated_wire_sides_" .. w1 .. ".png", backface_culling = true },
			{ name = "jeija_insulated_wire_ends_" .. w1 .. ".png", backface_culling = true },
			{ name = "jeija_insulated_wire_sides_" .. w2 .. ".png", backface_culling = true },
			{ name = "jeija_insulated_wire_ends_" .. w2 .. ".png", backface_culling = true },
		},
		paramtype = "light",
		paramtype2 = "facedir",
		is_ground_content = false,
		walkable = false,
		sunlight_propagates = true,
		selection_box = doublecorner_selectionbox,
		groups = groups,
		drop = doublecorner_states[1],
		sounds = mesecon.node_sound.default,
		mesecons = {
			conductor = {
				states = doublecorner_states,
				rules = doublecorner_get_rules,
			},
		},
		on_blast = mesecon.on_blastnode,
		on_rotate = mesecon.on_rotate_horiz,
	})
end

minetest.register_craft({
	type = "shapeless",
	output = "mesecons_extrawires:doublecorner_00",
	recipe = {
		"mesecons_extrawires:corner_off",
		"mesecons_extrawires:corner_off",
	},
})

minetest.register_craft({
	type = "shapeless",
	output = "mesecons_extrawires:corner_off 2",
	recipe = {
		"mesecons_extrawires:doublecorner_00",
	},
})


code:
local mese_nodename = minetest.registered_aliases["mesecons_gamecompat:mese"]
if mese_nodename then
	-- Convert placeholders.
	minetest.register_alias("mesecons_extrawires:mese", mese_nodename)
else
	-- Register placeholder.
	mese_nodename = "mesecons_extrawires:mese"
	minetest.register_node("mesecons_extrawires:mese", {
		description = "Mese Wire",
		tiles = {"mesecons_wire_off.png"},
		paramtype = "light",
		light_source = 3,
		groups = {cracky = 1},
		sounds = mesecon.node_sound.stone,
	})
end

local mesewire_rules =
{
	{x = 1, y = 0, z = 0},
	{x =-1, y = 0, z = 0},
	{x = 0, y = 1, z = 0},
	{x = 0, y =-1, z = 0},
	{x = 0, y = 0, z = 1},
	{x = 0, y = 0, z =-1},
}

minetest.override_item(mese_nodename, {
	mesecons = {conductor = {
		state = mesecon.state.off,
		onstate = "mesecons_extrawires:mese_powered",
		rules = mesewire_rules
	}}
})

-- Copy node definition of powered mese from normal mese
-- and brighten texture tiles to indicate mese is powered
local unpowered_def = minetest.registered_nodes[mese_nodename]
local powered_def = mesecon.merge_tables(unpowered_def, {
	drop = mese_nodename,
	paramtype = "light",
	light_source = math.min(unpowered_def.light_source + 2, minetest.LIGHT_MAX),
	mesecons = {conductor = {
		state = mesecon.state.on,
		offstate = mese_nodename,
		rules = mesewire_rules
	}},
	groups = mesecon.merge_tables(unpowered_def.groups or {}, {not_in_creative_inventory = 1}),
	on_blast = mesecon.on_blastnode,
})

for i, v in pairs(powered_def.tiles) do
	powered_def.tiles[i] = v .. "^[brighten"
end

minetest.register_node("mesecons_extrawires:mese_powered", powered_def)


code:
name = mesecons_extrawires
depends = mesecons, mesecons_gamecompat


code:
local tjunction_nodebox = {
	type = "fixed",
	-- ±0.001 is to prevent z-fighting
	fixed = {{ -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 },
		 { -3/32, -17/32, -16/32+0.001, 3/32, -13/32, -3/32},}
}

local tjunction_selectionbox = {
		type = "fixed",
		fixed = { -16/32, -16/32, -16/32, 16/32, -12/32, 7/32 },
}

local tjunction_get_rules = mesecon.horiz_rules_getter({
	{x = 1, y = 0, z = 0},
	{x = 0, y = 0, z = -1},
	{x = -1, y = 0, z = 0},
})

minetest.register_node("mesecons_extrawires:tjunction_on", {
	drawtype = "nodebox",
	tiles = {
		"jeija_insulated_wire_tjunction_tb_on.png",
		"jeija_insulated_wire_tjunction_tb_on.png^[transformR180",
		"jeija_insulated_wire_ends_on.png",
		"jeija_insulated_wire_ends_on.png",
		"jeija_insulated_wire_sides_on.png",
		"jeija_insulated_wire_ends_on.png"
	},
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	walkable = false,
	sunlight_propagates = true,
	selection_box = tjunction_selectionbox,
	node_box = tjunction_nodebox,
	groups = {dig_immediate = 3, not_in_creative_inventory = 1},
	drop = "mesecons_extrawires:tjunction_off",
	sounds = mesecon.node_sound.default,
	mesecons = {conductor =
	{
		state = mesecon.state.on,
		rules = tjunction_get_rules,
		offstate = "mesecons_extrawires:tjunction_off"
	}},
	on_blast = mesecon.on_blastnode,
	on_rotate = mesecon.on_rotate_horiz,
})

minetest.register_node("mesecons_extrawires:tjunction_off", {
	drawtype = "nodebox",
	description = "Insulated Mesecon T-junction",
	tiles = {
		"jeija_insulated_wire_tjunction_tb_off.png",
		"jeija_insulated_wire_tjunction_tb_off.png^[transformR180",
		"jeija_insulated_wire_ends_off.png",
		"jeija_insulated_wire_ends_off.png",
		"jeija_insulated_wire_sides_off.png",
		"jeija_insulated_wire_ends_off.png"
	},
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	walkable = false,
	sunlight_propagates = true,
	selection_box = tjunction_selectionbox,
	node_box = tjunction_nodebox,
	groups = {dig_immediate = 3},
	sounds = mesecon.node_sound.default,
	mesecons = {conductor =
	{
		state = mesecon.state.off,
		rules = tjunction_get_rules,
		onstate = "mesecons_extrawires:tjunction_on"
	}},
	on_blast = mesecon.on_blastnode,
	on_rotate = mesecon.on_rotate_horiz,
})

minetest.register_craft({
	output = "mesecons_extrawires:tjunction_off 3",
	recipe = {
		{"mesecons_insulated:insulated_off", "mesecons_insulated:insulated_off", "mesecons_insulated:insulated_off"},
		{"", "mesecons_insulated:insulated_off", ""},
	}
})


code:
local vertical_box = {
	type = "fixed",
	fixed = {-1/16, -8/16, -1/16, 1/16, 8/16, 1/16}
}

local top_box = {
	type = "fixed",
	fixed = {{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16}}
}

local bottom_box = {
	type = "fixed",
	fixed = {
		{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16},
		{-1/16, -7/16, -1/16, 1/16, 8/16, 1/16},
	}
}

local vertical_rules = {
	{x=0, y=1, z=0},
	{x=0, y=-1, z=0}
}

local top_rules = {
	{x=1,y=0, z=0},
	{x=-1,y=0, z=0},
	{x=0,y=0, z=1},
	{x=0,y=0, z=-1},
	{x=0,y=-1, z=0}
}

local bottom_rules = {
	{x=1, y=0, z=0},
	{x=-1, y=0, z=0},
	{x=0, y=0, z=1},
	{x=0, y=0, z=-1},
	{x=0, y=1, z=0},
	{x=0, y=2, z=0} -- receive power from pressure plate / detector / ... 2 nodes above
}

local vertical_updatepos = function (pos)
	local node = minetest.get_node(pos)
	if minetest.registered_nodes[node.name]
	and minetest.registered_nodes[node.name].is_vertical_conductor then
		local node_above = minetest.get_node(vector.add(pos, vertical_rules[1]))
		local node_below = minetest.get_node(vector.add(pos, vertical_rules[2]))

		local above = minetest.registered_nodes[node_above.name]
			and minetest.registered_nodes[node_above.name].is_vertical_conductor
		local below = minetest.registered_nodes[node_below.name]
			and minetest.registered_nodes[node_below.name].is_vertical_conductor

		mesecon.on_dignode(pos, node)

		-- Always place offstate conductor and let mesecon.on_placenode take care
		local newname = "mesecons_extrawires:vertical_"
		if above and below then -- above and below: vertical mesecon
			newname = newname .. "off"
		elseif above and not below then -- above only: bottom
			newname = newname .. "bottom_off"
		elseif not above and below then -- below only: top
			newname = newname .. "top_off"
		else -- no vertical wire above, no vertical wire below: use bottom
			newname = newname .. "bottom_off"
		end

		minetest.set_node(pos, {name = newname})
		mesecon.on_placenode(pos, {name = newname})
	end
end

local vertical_update = function (pos)
	vertical_updatepos(pos) -- this one
	vertical_updatepos(vector.add(pos, vertical_rules[1])) -- above
	vertical_updatepos(vector.add(pos, vertical_rules[2])) -- below
end

-- Vertical wire
mesecon.register_node("mesecons_extrawires:vertical", {
	description = "Vertical Mesecon",
	drawtype = "nodebox",
	walkable = false,
	paramtype = "light",
	is_ground_content = false,
	sunlight_propagates = true,
	selection_box = vertical_box,
	node_box = vertical_box,
	is_vertical_conductor = true,
	drop = "mesecons_extrawires:vertical_off",
	after_place_node = vertical_update,
	after_dig_node = vertical_update,
	sounds = mesecon.node_sound.default,
},{
	tiles = {"mesecons_wire_off.png"},
	groups = {dig_immediate=3},
	mesecons = {conductor = {
		state = mesecon.state.off,
		onstate = "mesecons_extrawires:vertical_on",
		rules = vertical_rules,
	}}
},{
	tiles = {"mesecons_wire_on.png"},
	groups = {dig_immediate=3, not_in_creative_inventory=1},
	mesecons = {conductor = {
		state = mesecon.state.on,
		offstate = "mesecons_extrawires:vertical_off",
		rules = vertical_rules,
	}}
})

-- Vertical wire top
mesecon.register_node("mesecons_extrawires:vertical_top", {
	description = "Vertical mesecon",
	drawtype = "nodebox",
	walkable = false,
	paramtype = "light",
	is_ground_content = false,
	sunlight_propagates = true,
	groups = {dig_immediate=3, not_in_creative_inventory=1},
	selection_box = top_box,
	node_box = top_box,
	is_vertical_conductor = true,
	drop = "mesecons_extrawires:vertical_off",
	after_place_node = vertical_update,
	after_dig_node = vertical_update,
	sounds = mesecon.node_sound.default,
},{
	tiles = {"mesecons_wire_off.png"},
	mesecons = {conductor = {
		state = mesecon.state.off,
		onstate = "mesecons_extrawires:vertical_top_on",
		rules = top_rules,
	}}
},{
	tiles = {"mesecons_wire_on.png"},
	mesecons = {conductor = {
		state = mesecon.state.on,
		offstate = "mesecons_extrawires:vertical_top_off",
		rules = top_rules,
	}}
})

-- Vertical wire bottom
mesecon.register_node("mesecons_extrawires:vertical_bottom", {
	description = "Vertical mesecon",
	drawtype = "nodebox",
	walkable = false,
	paramtype = "light",
	is_ground_content = false,
	sunlight_propagates = true,
	groups = {dig_immediate = 3, not_in_creative_inventory = 1},
	selection_box = bottom_box,
	node_box = bottom_box,
	is_vertical_conductor = true,
	drop = "mesecons_extrawires:vertical_off",
	after_place_node = vertical_update,
	after_dig_node = vertical_update,
	sounds = mesecon.node_sound.default,
},{
	tiles = {"mesecons_wire_off.png"},
	mesecons = {conductor = {
		state = mesecon.state.off,
		onstate = "mesecons_extrawires:vertical_bottom_on",
		rules = bottom_rules,
	}}
},{
	tiles = {"mesecons_wire_on.png"},
	mesecons = {conductor = {
		state = mesecon.state.on,
		offstate = "mesecons_extrawires:vertical_bottom_off",
		rules = bottom_rules,
	}}
})

minetest.register_craft({
	output = "mesecons_extrawires:vertical_off 3",
	recipe = {
		{"group:mesecon_conductor_craftable"},
		{"group:mesecon_conductor_craftable"},
		{"group:mesecon_conductor_craftable"},
	}
})

minetest.register_craft({
	output = "mesecons:wire_00000000_off",
	recipe = {{"mesecons_extrawires:vertical_off"}}
})


code:
FPGAs can be used to chain multiple logic gates together in a compact manner.
They come with 4 I/O ports and 10 internal registers,
which can then be connected with each other to form logic circuits.
They work fine in unloaded blocks.

Supported gate types: AND, OR, NOT, XOR, NAND, XNOR, Buffer (=)

I/O ports: A B C D; Registers: numbered 0 to 9


code:
The FPGA programmer can be used to copy gate configurations from one FPGA to another.<br />
Shift+Right-Click an FPGA to read its configuration and "remember" it.
Left-click (punch) FPGAs to write the saved configuration to them.


code:
local plg = {}
plg.rules = {}
-- per-player formspec positions
plg.open_formspecs = {}

local lcore = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/logic.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/tool.lua")(plg)

plg.register_nodes = function(template)
	-- each loop is for one of the 4 IO ports
	for a = 0, 1 do
	for b = 0, 1 do
	for c = 0, 1 do
	for d = 0, 1 do
		local ndef = table.copy(template)
		local nodename = "mesecons_fpga:fpga"
				.. tostring(d) .. tostring(c) .. tostring(b) .. tostring(a)

		-- build top texture string
		local texture = "jeija_fpga_top.png"
		if a == 1 then texture = texture .. "^jeija_microcontroller_LED_A.png" end
		if b == 1 then texture = texture .. "^jeija_microcontroller_LED_B.png" end
		if c == 1 then texture = texture .. "^jeija_microcontroller_LED_C.png" end
		if d == 1 then texture = texture .. "^jeija_microcontroller_LED_D.png" end
		ndef.tiles[1] = texture
		ndef.inventory_image = texture

		if (a + b + c + d) > 0 then
			ndef.groups["not_in_creative_inventory"] = 1
		end

		-- interaction with mesecons (input / output)
		local rules_out = {}
		if a == 1 then table.insert(rules_out, {x = -1, y = 0, z =  0}) end
		if b == 1 then table.insert(rules_out, {x =  0, y = 0, z =  1}) end
		if c == 1 then table.insert(rules_out, {x =  1, y = 0, z =  0}) end
		if d == 1 then table.insert(rules_out, {x =  0, y = 0, z = -1}) end
		plg.rules[nodename] = rules_out

		local rules_in = {}
		if a == 0 then table.insert(rules_in, {x = -1, y = 0, z =  0}) end
		if b == 0 then table.insert(rules_in, {x =  0, y = 0, z =  1}) end
		if c == 0 then table.insert(rules_in, {x =  1, y = 0, z =  0}) end
		if d == 0 then table.insert(rules_in, {x =  0, y = 0, z = -1}) end
		ndef.mesecons.effector.rules = rules_in

		if (a + b + c + d) > 0 then
			ndef.mesecons.receptor = {
				state = mesecon.state.on,
				rules = rules_out,
			}
		end

		minetest.register_node(nodename, ndef)
	end
	end
	end
	end
end

plg.register_nodes({
	description = "FPGA",
	drawtype = "nodebox",
	tiles = {
		"", -- replaced later
		"jeija_microcontroller_bottom.png",
		"jeija_fpga_sides.png",
		"jeija_fpga_sides.png",
		"jeija_fpga_sides.png",
		"jeija_fpga_sides.png"
	},
	inventory_image = "", -- replaced later
	is_ground_content = false,
	sunlight_propagates = true,
	paramtype = "light",
	walkable = true,
	groups = {dig_immediate = 2, mesecon = 3, overheat = 1},
	drop = "mesecons_fpga:fpga0000",
	selection_box = {
		type = "fixed",
		fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
	},
	node_box = {
		type = "fixed",
		fixed = {
			{ -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- bottom slab
			{ -5/16, -7/16, -5/16, 5/16, -6/16, 5/16 }, -- circuit board
			{ -3/16, -6/16, -3/16, 3/16, -5/16, 3/16 }, -- IC
		}
	},
	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		local is = { {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }

		meta:set_string("instr", lcore.serialize(is))
		meta:set_int("valid", 0)
		meta:set_string("infotext", "FPGA")
	end,
	on_rightclick = function(pos, _, clicker)
		if not minetest.is_player(clicker) then
			return
		end
		local meta = minetest.get_meta(pos)
		local name = clicker:get_player_name()
		-- Erase formspecs of old FPGAs
		meta:set_string("formspec", "")

		plg.open_formspecs[name] = pos
		local is = lcore.deserialize(meta:get_string("instr"))
		minetest.show_formspec(name, "mesecons:fpga", plg.to_formspec_string(is, nil))
	end,
	sounds = mesecon.node_sound.stone,
	mesecons = {
		effector = {
			rules = {}, -- replaced later
			action_change = function(pos, _, rule, newstate)
				if plg.ports_changed(pos, rule, newstate) then
					plg.update(pos)
				end
			end
		}
	},
	after_dig_node = function(pos, node)
		mesecon.receptor_off(pos, plg.rules[node.name])
		for name, open_pos in pairs(plg.open_formspecs) do
			if vector.equals(pos, open_pos) then
				minetest.close_formspec(name, "mesecons:fpga")
				plg.open_formspecs[name] = nil
			end
		end
	end,
	on_blast = mesecon.on_blastnode,
	on_rotate = function(pos, _, user, mode)
		local abcd1 = {"A", "B", "C", "D"}
		local abcd2 = {A = 1, B = 2, C = 3, D = 4}
		local ops = {"op1", "op2", "dst"}
		local dir = 0
		if mode == screwdriver.ROTATE_FACE then -- clock-wise
			dir = 1
			if user and user:is_player() then
				minetest.chat_send_player(user:get_player_name(),
						"FPGA ports have been rotated clockwise.")
			end
		elseif mode == screwdriver.ROTATE_AXIS then -- counter-clockwise
			dir = -1
			if user and user:is_player() then
				minetest.chat_send_player(user:get_player_name(),
						"FPGA ports have been rotated counter-clockwise.")
			end
		end

		local meta = minetest.get_meta(pos)
		local instr = lcore.deserialize(meta:get_string("instr"))
		for i = 1, #instr do
			for _, op in ipairs(ops) do
				local o = instr[i][op]
				if o and o.type == "io" then
					local num = abcd2[o.port]
					num = num + dir
					if num > 4 then num = 1
					elseif num < 1 then num = 4 end
					instr[i][op].port = abcd1[num]
				end
			end
		end

		meta:set_string("instr", lcore.serialize(instr))
		plg.update_meta(pos, instr)
		return true
	end,
})

plg.to_formspec_string = function(is, err)
	local function dropdown_op(x, y, name, val)
		local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
				.. "0.75,0.5;" .. name .. ";" -- the height seems to be ignored?
		s = s .. " ,A,B,C,D,0,1,2,3,4,5,6,7,8,9;"
		if val == nil then
			s = s .. "0" -- actually selects no field at all
		elseif val.type == "io" then
			local mapping = {
				["A"] = 1,
				["B"] = 2,
				["C"] = 3,
				["D"] = 4,
			}
			s = s .. tostring(1 + mapping[val.port])
		else -- "reg"
			s = s .. tostring(6 + val.n)
		end
		return s .. "]"
	end
	local function dropdown_action(x, y, name, val)
		local selected = 0
		local titles = { " " }
		for i, data in ipairs(lcore.get_operations()) do
			titles[i + 1] = data.fs_name
			if val == data.gate then
				selected = i + 1
			end
		end
		return ("dropdown[%f,%f;1.125,0.5;%s;%s;%i]"):format(
			x, y, name, table.concat(titles, ","), selected)
	end
	local s = "size[9,9]"..
		"label[3.4,-0.15;FPGA gate configuration]"..
		"button[7,7.5;2,2.5;program;Program]"..
		"box[4.2,0.5;0.03,7;#ffffff]"..
		"label[0.25,0.25;op. 1]"..
		"label[1.0,0.25;gate type]"..
		"label[2.125,0.25;op. 2]"..
		"label[3.15,0.25;dest]"..
		"label[4.5,0.25;op. 1]"..
		"label[5.25,0.25;gate type]"..
		"label[6.375,0.25;op. 2]"..
		"label[7.4,0.25;dest]"
	local x = 1 - 0.75
	local y = 1 - 0.25
	for i = 1, 14 do
		local cur = is[i]
		s = s .. dropdown_op    (x      , y, tostring(i).."op1", cur.op1)
		s = s .. dropdown_action(x+0.75 , y, tostring(i).."act", cur.action)
		s = s .. dropdown_op    (x+1.875, y, tostring(i).."op2", cur.op2)
		s = s .. "label[" .. tostring(x+2.625) .. "," .. tostring(y+0.1) .. "; ->]"
		s = s .. dropdown_op    (x+2.9  , y, tostring(i).."dst", cur.dst)
		y = y + 1

		if i == 7 then
			x = 4.5
			y = 1 - 0.25
		end
	end
	if err then
		local fmsg = minetest.colorize("#ff0000", minetest.formspec_escape(err.msg))
		s = s .. plg.red_box_around(err.i) ..
			"label[0.25,8.25;The gate configuration is erroneous in the marked area:]"..
			"label[0.25,8.5;" .. fmsg .. "]"
	end
	return s
end

plg.from_formspec_fields = function(fields)
	local function read_op(s)
		if s == nil or s == " " then
			return nil
		elseif s == "A" or s == "B" or s == "C" or s == "D" then
			return {type = "io", port = s}
		else
			return {type = "reg", n = tonumber(s)}
		end
	end
	local function read_action(s)
		for i, data in ipairs(lcore.get_operations()) do
			if data.fs_name == s then
				return data.gate
			end
		end
	end
	local is = {}
	for i = 1, 14 do
		local cur = {}
		cur.op1 = read_op(fields[tonumber(i) .. "op1"])
		cur.action = read_action(fields[tonumber(i) .. "act"])
		cur.op2 = read_op(fields[tonumber(i) .. "op2"])
		cur.dst = read_op(fields[tonumber(i) .. "dst"])
		is[#is + 1] = cur
	end
	return is
end

plg.update_meta = function(pos, is)
	if type(is) == "string" then -- serialized string
		is = lcore.deserialize(is)
	end
	local meta = minetest.get_meta(pos)

	local err = lcore.validate(is)
	if err == nil then
		meta:set_int("valid", 1)
		meta:set_string("infotext", "FPGA (functional)")
	else
		meta:set_int("valid", 0)
		meta:set_string("infotext", "FPGA")
	end

	-- reset ports and run programmed logic
	plg.setports(pos, false, false, false, false)
	plg.update(pos)

	-- Refresh open formspecs
	local form = plg.to_formspec_string(is, err)
	for name, open_pos in pairs(plg.open_formspecs) do
		if vector.equals(pos, open_pos) then
			minetest.show_formspec(name, "mesecons:fpga", form)
		end
	end
	return err
end

plg.red_box_around = function(i)
	local x, y
	if i > 7 then
		x = 4.5
		y = 0.75 + (i - 8)
	else
		x = 0.25
		y = 0.75 + (i - 1)
	end
	return string.format("box[%f,%f;3.8,0.8;#ff0000]", x-0.1, y-0.05)
end

plg.update = function(pos)
	local meta = minetest.get_meta(pos)
	if meta:get_int("valid") ~= 1 then
		return
	elseif mesecon.do_overheat(pos) then
		plg.setports(pos, false, false, false, false)
		meta:set_int("valid", 0)
		meta:set_string("infotext", "FPGA (overheated)")
		return
	end

	local is = lcore.deserialize(meta:get_string("instr"))
	local A, B, C, D = plg.getports(pos)
	A, B, C, D = lcore.interpret(is, A, B, C, D)
	plg.setports(pos, A, B, C, D)
end

-- Updates the port states according to the signal change.
-- Returns whether the port states actually changed.
plg.ports_changed = function(pos, rule, newstate)
	if rule == nil then return false end
	local meta = minetest.get_meta(pos)
	local states

	local s = meta:get_string("portstates")
	if s == nil then
		states = {false, false, false, false}
	else
		states = {
			s:sub(1, 1) == "1",
			s:sub(2, 2) == "1",
			s:sub(3, 3) == "1",
			s:sub(4, 4) == "1",
		}
	end

	-- trick to transform rules (see register_node) into port number
	local portno = ({4, 1, nil, 3, 2})[3 + rule.x + 2*rule.z]
	states[portno] = (newstate == "on")

	local new_portstates =
			(states[1] and "1" or "0") .. (states[2] and "1" or "0") ..
			(states[3] and "1" or "0") .. (states[4] and "1" or "0")
	if new_portstates ~= s then
		meta:set_string("portstates", new_portstates)
		return true
	end
	return false
end

plg.getports = function(pos) -- gets merged states of INPUT & OUTPUT
	local sin, sout

	local s = minetest.get_meta(pos):get_string("portstates")
	if s == nil then
		sin = {false, false, false, false}
	else
		sin = {
			s:sub(1, 1) == "1",
			s:sub(2, 2) == "1",
			s:sub(3, 3) == "1",
			s:sub(4, 4) == "1",
		}
	end

	local name = minetest.get_node(pos).name
	assert(name:find("mesecons_fpga:fpga") == 1)
	local off = #"mesecons_fpga:fpga"
	sout = {
		name:sub(off+4, off+4) == "1",
		name:sub(off+3, off+3) == "1",
		name:sub(off+2, off+2) == "1",
		name:sub(off+1, off+1) == "1",
	}

	return unpack({
		sin[1] or sout[1],
		sin[2] or sout[2],
		sin[3] or sout[3],
		sin[4] or sout[4],
	})
end

plg.setports = function(pos, A, B, C, D) -- sets states of OUTPUT
	local base = "mesecons_fpga:fpga"

	local name = base
			.. (D and "1" or "0") .. (C and "1" or "0")
			.. (B and "1" or "0") .. (A and "1" or "0")
	minetest.swap_node(pos, {name = name, param2 = minetest.get_node(pos).param2})

	if A ~= nil then
		local ru = plg.rules[base .. "0001"]
		if A then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
	end
	if B ~= nil then
		local ru = plg.rules[base .. "0010"]
		if B then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
	end
	if C ~= nil then
		local ru = plg.rules[base .. "0100"]
		if C then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
	end
	if D ~= nil then
		local ru = plg.rules[base .. "1000"]
		if D then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
	end
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	local player_name = player:get_player_name()

	if formname ~= "mesecons:fpga" or fields.quit then
		plg.open_formspecs[player_name] = nil -- potential garbage
		return
	end
	if not fields.program then
		return -- we only care when the user clicks "Program"
	end
	local pos = plg.open_formspecs[player_name]
	if minetest.is_protected(pos, player_name) then
		minetest.record_protection_violation(pos, player_name)
		return
	end

	local meta = minetest.get_meta(pos)
	local is = plg.from_formspec_fields(fields)

	meta:set_string("instr", lcore.serialize(is))
	local err = plg.update_meta(pos, is)

	if not err then
		plg.open_formspecs[player_name] = nil
		-- Close on success
		minetest.close_formspec(player_name, "mesecons:fpga")
	end
end)

minetest.register_on_leaveplayer(function(player)
	plg.open_formspecs[player:get_player_name()] = nil
end)

minetest.register_craft({
	output = "mesecons_fpga:fpga0000 2",
	recipe = {
		{'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable'},
		{'mesecons_materials:silicon', 'mesecons_materials:silicon'},
		{'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable'},
	}
})


code:
local lg = {}

local operations = {
	-- table index: Index in the formspec dropdown
	-- gate:    Internal name
	-- short:   Serialized form, single character
	-- fs_name: Display name, padded to 4 characters
	-- func:    Function that applies the operation
	-- unary:   Whether this gate only has one input
	{ gate = "and",  short = "&", fs_name = " AND", func = function(a, b) return a and b end },
	{ gate = "or",   short = "|", fs_name = "  OR", func = function(a, b) return a or b end },
	{ gate = "not",  short = "~", fs_name = " NOT", func = function(_, b) return not b end, unary = true },
	{ gate = "xor",  short = "^", fs_name = " XOR", func = function(a, b) return a ~= b end },
	{ gate = "nand", short = "?", fs_name = "NAND", func = function(a, b) return not (a and b) end },
	{ gate = "buf",  short = "_", fs_name = "   =", func = function(_, b) return b end, unary = true },
	{ gate = "xnor", short = "=", fs_name = "XNOR", func = function(a, b) return a == b end },
	{ gate = "nor",  short = "!", fs_name = " NOR", func = function(a, b) return not (a or b) end },
}

lg.get_operations = function()
	return operations
end

-- (de)serialize
lg.serialize = function(t)
	local function _op(t)
		if t == nil then
			return " "
		elseif t.type == "io" then
			return t.port
		else -- t.type == "reg"
			return tostring(t.n)
		end
	end
	-- Serialize actions (gates) from eg. "and" to "&"
	local function _action(action)
		for i, data in ipairs(operations) do
			if data.gate == action then
				return data.short
			end
		end
		return " "
	end

	local s = ""
	for i = 1, 14 do
		local cur = t[i]
		if next(cur) ~= nil then
			s = s .. _op(cur.op1) .. _action(cur.action) .. _op(cur.op2) .. _op(cur.dst)
		end
		s = s .. "/"
	end
	return s
end

lg.deserialize = function(s)
	local function _op(c)
		if c == "A" or c == "B" or c == "C" or c == "D" then
			return {type = "io", port = c}
		elseif c == " " then
			return nil
		else
			return {type = "reg", n = tonumber(c)}
		end
	end
	-- Deserialize actions (gates) from eg. "&" to "and"
	local function _action(action)
		for i, data in ipairs(operations) do
			if data.short == action then
				return data.gate
			end
		end
		-- nil
	end

	local ret = {}
	for part in s:gmatch("(.-)/") do
		local parsed
		if part == "" then
			parsed = {}
		else
			parsed = {
				action = _action( part:sub(2,2) ),
				op1 = _op( part:sub(1,1) ),
				op2 = _op( part:sub(3,3) ),
				dst = _op( part:sub(4,4) ),
			}
		end
		ret[#ret + 1] = parsed
	end
	-- More than 14 instructions (write to all 10 regs + 4 outputs)
	-- will not pass the write-once requirement of the validator
	assert(#ret == 14)
	return ret
end

-- validation
lg.validate_single = function(t, i)
	local function is_reg_written_to(t, n, max)
		for i = 1, max-1 do
			if next(t[i]) ~= nil
					and t[i].dst and t[i].dst.type == "reg"
					and t[i].dst.n == n then
				return true
			end
		end
		return false
	end
	local function compare_op(t1, t2, allow_same_io)
		if t1 == nil or t2 == nil then
			return false
		elseif t1.type ~= t2.type then
			return false
		end
		if t1.type == "reg" and t1.n == t2.n then
			return true
		elseif t1.type == "io" and t1.port == t2.port then
			return not allow_same_io
		end
		return false
	end
	local elem = t[i]

	local gate_data
	for j, data in ipairs(operations) do
		if data.gate == elem.action then
			gate_data = data
			break
		end
	end

	-- check for completeness
	if not gate_data then
		return {i = i, msg = "Gate type is required"}
	elseif gate_data.unary then
		if elem.op1 ~= nil or elem.op2 == nil or elem.dst == nil then
			return {i = i, msg = "Second operand (only) and destination are required"}
		end
	else
		if elem.op1 == nil or elem.op2 == nil or elem.dst == nil then
			return {i = i, msg = "Operands and destination are required"}
		end
	end
	-- check whether operands/destination are identical
	if compare_op(elem.op1, elem.op2) then
		return {i = i, msg = "Operands cannot be identical"}
	end
	if compare_op(elem.op1, elem.dst, true) or compare_op(elem.op2, elem.dst, true) then
		return {i = i, msg = "Destination and operands must be different"}
	end
	-- check whether operands point to defined registers
	if elem.op1 ~= nil and elem.op1.type == "reg"
			and not is_reg_written_to(t, elem.op1.n, i) then
		return {i = i, msg = "First operand is undefined register"}
	end
	if elem.op2.type == "reg" and not is_reg_written_to(t, elem.op2.n, i) then
		return {i = i, msg = "Second operand is undefined register"}
	end
	-- check whether destination points to undefined register
	if elem.dst.type == "reg" and is_reg_written_to(t, elem.dst.n, i) then
		return {i = i, msg = "Destination is already used register"}
	end

	return nil
end

lg.validate = function(t)
	for i = 1, 14 do
		if next(t[i]) ~= nil then
			local r = lg.validate_single(t, i)
			if r ~= nil then
				return r
			end
		end
	end
	return nil
end

-- interpreter
lg.interpret = function(t, a, b, c, d)
	local function _action(s, v1, v2)
		for i, data in ipairs(operations) do
			if data.gate == s then
				return data.func(v1, v2)
			end
		end
		return false -- unknown gate
	end
	local function _op(t, regs, io_in)
		if t.type == "reg" then
			return regs[t.n]
		else -- t.type == "io"
			return io_in[t.port]
		end
	end

	local io_in = {A=a, B=b, C=c, D=d}
	local regs = {}
	local io_out = {}
	for i = 1, 14 do
		local cur = t[i]
		if next(cur) ~= nil then
			local v1, v2
			if cur.op1 ~= nil then
				v1 = _op(cur.op1, regs, io_in)
			end
			v2 = _op(cur.op2, regs, io_in)

			local result = _action(cur.action, v1, v2)

			if cur.dst.type == "reg" then
				regs[cur.dst.n] = result
			else -- cur.dst.type == "io"
				io_out[cur.dst.port] = result
			end
		end
	end
	return io_out.A, io_out.B, io_out.C, io_out.D
end

return lg


code:
name = mesecons_fpga
depends = mesecons, mesecons_gamecompat
optional_depends = screwdriver


code:
return function(plg)

minetest.register_tool("mesecons_fpga:programmer", {
	description = "FPGA Programmer",
	inventory_image = "jeija_fpga_programmer.png",
	stack_max = 1,
	on_place = function(itemstack, placer, pointed_thing)
		if pointed_thing.type ~= "node" then
			return itemstack
		end

		local pos = pointed_thing.under
		if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then
			return itemstack
		end

		local meta = minetest.get_meta(pos)
		if meta:get_string("instr") == "//////////////" then
			minetest.chat_send_player(placer:get_player_name(), "This FPGA is unprogrammed.")
			minetest.sound_play("mesecons_fpga_fail", { pos = placer:get_pos(), gain = 0.1, max_hear_distance = 4 }, true)
			return itemstack
		end
		itemstack:set_metadata(meta:get_string("instr"))
		minetest.chat_send_player(placer:get_player_name(), "FPGA gate configuration was successfully copied!")
		minetest.sound_play("mesecons_fpga_copy", { pos = placer:get_pos(), gain = 0.1, max_hear_distance = 4 }, true)

		return itemstack
	end,
	on_use = function(itemstack, user, pointed_thing)
		if pointed_thing.type ~= "node" then
			return itemstack
		end

		local pos = pointed_thing.under
		if minetest.get_node(pos).name:find("mesecons_fpga:fpga") ~= 1 then
			return itemstack
		end
		local player_name = user:get_player_name()
		if minetest.is_protected(pos, player_name) then
			minetest.record_protection_violation(pos, player_name)
			return itemstack
		end

		local imeta = itemstack:get_metadata()
		if imeta == "" then
			minetest.chat_send_player(player_name, "Use shift+right-click to copy a gate configuration first.")
			minetest.sound_play("mesecons_fpga_fail", { pos = user:get_pos(), gain = 0.1, max_hear_distance = 4 }, true)
			return itemstack
		end

		local meta = minetest.get_meta(pos)
		meta:set_string("instr", imeta)
		plg.update_meta(pos, imeta)
		minetest.chat_send_player(player_name, "Gate configuration was successfully written to FPGA!")
		minetest.sound_play("mesecons_fpga_write", { pos = user:get_pos(), gain = 0.1, max_hear_distance = 4 }, true)

		return itemstack
	end
})

minetest.register_craft({
	output = "mesecons_fpga:programmer",
	recipe = {
		{'group:mesecon_conductor_craftable'},
		{'mesecons_materials:silicon'},
	}
})

end


code:
--Aliases

minetest.register_alias("mesecons_gamecompat:chest", "default:chest")
minetest.register_alias("mesecons_gamecompat:chest_locked", "default:chest_locked")
minetest.register_alias("mesecons_gamecompat:coalblock", "default:coalblock")
minetest.register_alias("mesecons_gamecompat:cobble", "default:cobble")
minetest.register_alias("mesecons_gamecompat:glass", "default:glass")
minetest.register_alias("mesecons_gamecompat:lava_source", "default:lava_source")
minetest.register_alias("mesecons_gamecompat:mese", "default:mese")
minetest.register_alias("mesecons_gamecompat:mese_crystal", "default:mese_crystal")
minetest.register_alias("mesecons_gamecompat:mese_crystal_fragment", "default:mese_crystal_fragment")
minetest.register_alias("mesecons_gamecompat:obsidian_glass", "default:obsidian_glass")
minetest.register_alias("mesecons_gamecompat:stone", "default:stone")
minetest.register_alias("mesecons_gamecompat:steel_ingot", "default:steel_ingot")
minetest.register_alias("mesecons_gamecompat:steelblock", "default:steelblock")
minetest.register_alias("mesecons_gamecompat:torch", "default:torch")

if minetest.get_modpath("dye") then
	for _, color in ipairs(mesecon.dye_colors) do
		minetest.register_alias("mesecons_gamecompat:dye_" .. color, "dye:" .. color)
	end
end

-- Sounds

mesecon.node_sound.default = default.node_sound_defaults()
mesecon.node_sound.glass = default.node_sound_glass_defaults()
mesecon.node_sound.leaves = default.node_sound_leaves_defaults()
mesecon.node_sound.stone = default.node_sound_stone_defaults()
mesecon.node_sound.wood = default.node_sound_wood_defaults()

if minetest.get_modpath("fire") then
	mesecon.sound_name.fire = "fire_fire"
end

if minetest.get_modpath("tnt") then
	mesecon.sound_name.explode = "tnt_explode"
end

-- Textures

mesecon.texture.steel_block = "default_steel_block.png"

-- MVPS stoppers

if minetest.get_modpath("mesecons_mvps") then
	-- All of the locked and internal nodes in Minetest Game
	for _, name in ipairs({
		"default:chest_locked",
		"default:chest_locked_open",
		"doors:door_steel_b_1", -- old style doors
		"doors:door_steel_b_2", --
		"doors:door_steel_t_1", --
		"doors:door_steel_t_2", --
		"doors:door_steel_a",   -- new style doors
		"doors:door_steel_b",   --
		"doors:door_steel_c",   --
		"doors:door_steel_d",   --
		"doors:hidden",
		"doors:trapdoor_steel",
		"doors:trapdoor_steel_open",
		"xpanes:door_steel_bar_a",
		"xpanes:door_steel_bar_b",
		"xpanes:door_steel_bar_c",
		"xpanes:door_steel_bar_d",
		"xpanes:trapdoor_steel_bar",
		"xpanes:trapdoor_steel_bar_open",
	}) do
		mesecon.register_mvps_stopper(name)
	end
end


code:
mesecon.node_sound = {}

mesecon.sound_name = {}

mesecon.texture = {}

mesecon.dye_colors = {
	"red", "green", "blue", "grey", "dark_grey", "yellow",
	"orange", "white", "pink", "magenta", "cyan", "violet",
}

if minetest.get_modpath("default") then
	minetest.log("info", "Mesecons: detected Minetest Game for game compatibility")
	dofile(minetest.get_modpath("mesecons_gamecompat").."/compat_mtg.lua")
end


code:
name = mesecons_gamecompat
depends = mesecons
optional_depends = fire, default, dye, mesecons_mvps, tnt


code:
AND gates power their output if both inputs (from left and right) are powered.
They work in unloaded blocks.


code:
Diodes conduct signals in one direction only.
They work in unloaded blocks.


code:
NAND gates do not power their output if both inputs (from left and right) are powered, but power it in every other case.
They work in unloaded blocks.


code:
NOR gates only power their output if none of their two inputs is powered. They are basically OR gates with a NOT gate at their output.
They work in unloaded blocks.


code:
NOT gates invert signals, just like a mesecon torch does, but faster. The input is at the opposite side of the output.
They work in unloaded blocks.


code:
OR gates power their output if either of their inputs (or both) are powered. You could basically get the same behaviour with two diodes, but OR gates save some space.
They work in unloaded blocks.


code:
XOR gates power their output if only one input is powered, they're off if either both or none of the inputs is powered.
They work in unloaded blocks.


code:
local selection_box = {
	type = "fixed",
	fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }
}

local nodebox = {
	type = "fixed",
	fixed = {
		{ -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- bottom slab
		{ -6/16, -7/16, -6/16, 6/16, -6/16, 6/16 }
	},
}

local gate_get_output_rules = mesecon.horiz_rules_getter({{x = 1, y = 0, z = 0}})

local gate_get_input_rules_oneinput = mesecon.horiz_rules_getter({{x =-1, y = 0, z = 0}})

local gate_get_input_rules_twoinputs = mesecon.horiz_rules_getter({
	{x = 0, y = 0, z = 1, name = "input1"},
	{x = 0, y = 0, z = -1, name = "input2"},
})

local function set_gate(pos, node, state)
	local gate = minetest.registered_nodes[node.name]

	local new_nodename = state and gate.onstate or gate.offstate
	minetest.swap_node(pos, {name = new_nodename, param2 = node.param2})
	if new_nodename ~= node.name then
		if mesecon.do_overheat(pos) then
			minetest.remove_node(pos)
			mesecon.receptor_off(pos, gate_get_output_rules(node))
			minetest.add_item(pos, gate.drop)
		elseif state then
			mesecon.receptor_on(pos, gate_get_output_rules(node))
		else
			mesecon.receptor_off(pos, gate_get_output_rules(node))
		end
	end
end

local function update_gate(pos, node, link, newstate)
	local gate = minetest.registered_nodes[node.name]

	if gate.inputnumber == 1 then
		set_gate(pos, node, gate.assess(newstate == "on"))
	elseif gate.inputnumber == 2 then
		-- Inputs are stored in param2. Bit 5 is always set.
		-- input1 is bit 6 and input2 is bit 7.
		local val1, val2
		if node.param2 >= 32 then
			-- Bit 5 is set, so param2 is in the proper format.
			if link.name == "input1" then
				val1 = newstate == "on"
				val2 = node.param2 >= 128
			else
				val1 = node.param2 % 128 >= 64
				val2 = newstate == "on"
			end
		else
			-- Migrate old gates where the inputs are stored as metadata.
			-- This also triggers for newly placed gates.
			local meta = minetest.get_meta(pos)
			if link.name == "input1" then
				val1 = newstate == "on"
				val2 = meta:get_int("input2") == 1
			else
				val1 = meta:get_int("input1") == 1
				val2 = newstate == "on"
			end
			-- Set bit 5 so this won't happen again.
			node.param2 = node.param2 + 32
			-- Clear the metadata.
			meta:set_string("input1", "")
			meta:set_string("input2", "")
		end
		node.param2 = node.param2 % 64 + (val1 and 64 or 0) + (val2 and 128 or 0)
		set_gate(pos, node, gate.assess(val1, val2))
	end
end

local function register_gate(name, inputnumber, assess, recipe, description)
	local get_inputrules = inputnumber == 2 and gate_get_input_rules_twoinputs or
		gate_get_input_rules_oneinput

	local basename = "mesecons_gates:"..name
	mesecon.register_node(basename, {
		description = description,
		inventory_image = "jeija_gate_off.png^jeija_gate_"..name..".png",
		paramtype = "light",
		paramtype2 = "facedir",
		is_ground_content = false,
		drawtype = "nodebox",
		drop = basename.."_off",
		selection_box = selection_box,
		node_box = nodebox,
		walkable = true,
		sounds = mesecon.node_sound.stone,
		assess = assess,
		onstate = basename.."_on",
		offstate = basename.."_off",
		inputnumber = inputnumber,
		after_dig_node = mesecon.do_cooldown,
		on_rotate = mesecon.on_rotate_horiz,
	},{
		tiles = {
			"jeija_microcontroller_bottom.png^".."jeija_gate_off.png^"..
			"jeija_gate_output_off.png^".."jeija_gate_"..name..".png",
			"jeija_microcontroller_bottom.png^".."jeija_gate_output_off.png^"..
			"[transformFY",
			"jeija_gate_side.png^".."jeija_gate_side_output_off.png",
			"jeija_gate_side.png",
			"jeija_gate_side.png",
			"jeija_gate_side.png"
		},
		groups = {dig_immediate = 2, overheat = 1},
		mesecons = { receptor = {
			state = "off",
			rules = gate_get_output_rules
		}, effector = {
			rules = get_inputrules,
			action_change = update_gate
		}}
	},{
		tiles = {
			"jeija_microcontroller_bottom.png^".."jeija_gate_on.png^"..
			"jeija_gate_output_on.png^".."jeija_gate_"..name..".png",
			"jeija_microcontroller_bottom.png^".."jeija_gate_output_on.png^"..
			"[transformFY",
			"jeija_gate_side.png^".."jeija_gate_side_output_on.png",
			"jeija_gate_side.png",
			"jeija_gate_side.png",
			"jeija_gate_side.png"
		},
		groups = {dig_immediate = 2, not_in_creative_inventory = 1, overheat = 1},
		mesecons = { receptor = {
			state = "on",
			rules = gate_get_output_rules
		}, effector = {
			rules = get_inputrules,
			action_change = update_gate
		}}
	})

	minetest.register_craft({output = basename.."_off", recipe = recipe})
end

register_gate("diode", 1, function (input) return input end,
	{{"mesecons:mesecon", "mesecons_torch:mesecon_torch_on", "mesecons_torch:mesecon_torch_on"}},
	"Diode")

register_gate("not", 1, function (input) return not input end,
	{{"mesecons:mesecon", "mesecons_torch:mesecon_torch_on", "mesecons:mesecon"}},
	"NOT Gate")

register_gate("and", 2, function (val1, val2) return val1 and val2 end,
	{{"mesecons:mesecon", "", ""},
	 {"", "mesecons_materials:silicon", "mesecons:mesecon"},
	 {"mesecons:mesecon", "", ""}},
	"AND Gate")

register_gate("nand", 2, function (val1, val2) return not (val1 and val2) end,
	{{"mesecons:mesecon", "", ""},
	 {"", "mesecons_materials:silicon", "mesecons_torch:mesecon_torch_on"},
	 {"mesecons:mesecon", "", ""}},
	"NAND Gate")

register_gate("xor", 2, function (val1, val2) return (val1 or val2) and not (val1 and val2) end,
	{{"mesecons:mesecon", "", ""},
	 {"", "mesecons_materials:silicon", "mesecons_materials:silicon"},
	 {"mesecons:mesecon", "", ""}},
	"XOR Gate")

register_gate("nor", 2, function (val1, val2) return not (val1 or val2) end,
	{{"mesecons:mesecon", "", ""},
	 {"", "mesecons:mesecon", "mesecons_torch:mesecon_torch_on"},
	 {"mesecons:mesecon", "", ""}},
	"NOR Gate")

register_gate("or", 2, function (val1, val2) return (val1 or val2) end,
	{{"mesecons:mesecon", "", ""},
	 {"", "mesecons:mesecon", "mesecons:mesecon"},
	 {"mesecons:mesecon", "", ""}},
	"OR Gate")


code:
name = mesecons_gates
depends = mesecons, mesecons_gamecompat, mesecons_microcontroller, mesecons_delayer, mesecons_torch, mesecons_materials


code:
Water turbines are receptors that turn on if flowing water is above them.
They only work in active blocks; in inactive blocks they keep their old state.


code:
-- HYDRO_TURBINE
-- Water turbine:
-- Active if flowing >water< above it
-- (does not work with other liquids)

minetest.register_node("mesecons_hydroturbine:hydro_turbine_off", {
	drawtype = "mesh",
	mesh = "jeija_hydro_turbine_off.obj",
	tiles = {
		"jeija_hydro_turbine_sides_off.png",
		"jeija_hydro_turbine_top_bottom.png",
		"jeija_hydro_turbine_turbine_top_bottom_off.png",
		"jeija_hydro_turbine_turbine_misc_off.png"
	},
	use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or nil,
	inventory_image = "jeija_hydro_turbine_inv.png",
	is_ground_content = false,
	wield_scale = {x=0.75, y=0.75, z=0.75},
	groups = {dig_immediate=2},
	description="Water Turbine",
	paramtype = "light",
	selection_box = {
		type = "fixed",
		fixed = { -0.5, -0.5, -0.5, 0.5, 1.5, 0.5 },
	},
	sounds = mesecon.node_sound.metal,
	mesecons = {receptor = {
		state = mesecon.state.off
	}},
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_hydroturbine:hydro_turbine_on", {
	drawtype = "mesh",
	is_ground_content = false,
	mesh = "jeija_hydro_turbine_on.obj",
	wield_scale = {x=0.75, y=0.75, z=0.75},
	tiles = {
		"jeija_hydro_turbine_sides_on.png",
		"jeija_hydro_turbine_top_bottom.png",
		{ name = "jeija_hydro_turbine_turbine_top_bottom_on.png",
		    animation = {type = "vertical_frames", aspect_w = 128, aspect_h = 16, length = 1.6} },
		{ name = "jeija_hydro_turbine_turbine_misc_on.png",
		    animation = {type = "vertical_frames", aspect_w = 256, aspect_h = 32, length = 0.4} }
	},
	use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or nil,
	inventory_image = "jeija_hydro_turbine_inv.png",
	drop = "mesecons_hydroturbine:hydro_turbine_off 1",
	groups = {dig_immediate=2,not_in_creative_inventory=1},
	description="Water Turbine",
	paramtype = "light",
	selection_box = {
		type = "fixed",
		fixed = { -0.5, -0.5, -0.5, 0.5, 1.5, 0.5 },
	},
	sounds = mesecon.node_sound.metal,
	mesecons = {receptor = {
		state = mesecon.state.on
	}},
	on_blast = mesecon.on_blastnode,
})

local function is_flowing_water(pos)
	local name = minetest.get_node(pos).name
	local is_water = minetest.get_item_group(name, "water") > 0
	local def = minetest.registered_items[name]
	return is_water and (def and def.liquidtype == "flowing")
end

minetest.register_abm({
nodenames = {"mesecons_hydroturbine:hydro_turbine_off"},
	interval = 1,
	chance = 1,
	action = function(pos)
		local waterpos={x=pos.x, y=pos.y+1, z=pos.z}
		if is_flowing_water(waterpos) then
			minetest.set_node(pos, {name="mesecons_hydroturbine:hydro_turbine_on"})
			mesecon.receptor_on(pos)
		end
	end,
})

minetest.register_abm({
nodenames = {"mesecons_hydroturbine:hydro_turbine_on"},
	interval = 1,
	chance = 1,
	action = function(pos)
		local waterpos={x=pos.x, y=pos.y+1, z=pos.z}
		if not is_flowing_water(waterpos) then
			minetest.set_node(pos, {name="mesecons_hydroturbine:hydro_turbine_off"})
			mesecon.receptor_off(pos)
		end
	end,
})

minetest.register_craft({
	output = "mesecons_hydroturbine:hydro_turbine_off 2",
	recipe = {
	{"","group:stick", ""},
	{"group:stick", "mesecons_gamecompat:steel_ingot", "group:stick"},
	{"","group:stick", ""},
	}
})


code:
name = mesecons_hydroturbine
depends = mesecons, mesecons_gamecompat


code: Insulated mesecons are conductors that only conduct in one direction (and also not up or down). Like uninsulated wires, they work through unloaded blocks.


code:
local insulated_wire_get_rules = mesecon.horiz_rules_getter({
	{x = 1, y = 0, z = 0},
	{x = -1, y = 0, z = 0},
})

minetest.register_node("mesecons_insulated:insulated_on", {
	drawtype = "nodebox",
	description = "Straight Insulated Mesecon",
	tiles = {
		"jeija_insulated_wire_sides_on.png",
		"jeija_insulated_wire_sides_on.png",
		"jeija_insulated_wire_ends_on.png",
		"jeija_insulated_wire_ends_on.png",
		"jeija_insulated_wire_sides_on.png",
		"jeija_insulated_wire_sides_on.png"
	},
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	walkable = false,
	sunlight_propagates = true,
	selection_box = {
		type = "fixed",
		fixed = { -16/32, -16/32, -7/32, 16/32, -12/32, 7/32 }
	},
	node_box = {
		type = "fixed",
		-- ±0.001 is to prevent z-fighting
		fixed = { -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 }
	},
	groups = {dig_immediate = 3, not_in_creative_inventory = 1},
	drop = "mesecons_insulated:insulated_off",
	sounds = mesecon.node_sound.default,
	mesecons = {conductor = {
		state = mesecon.state.on,
		offstate = "mesecons_insulated:insulated_off",
		rules = insulated_wire_get_rules
	}},
	on_blast = mesecon.on_blastnode,
	on_rotate = mesecon.on_rotate_horiz,
})

minetest.register_node("mesecons_insulated:insulated_off", {
	drawtype = "nodebox",
	description = "Straight Insulated Mesecon",
	tiles = {
		"jeija_insulated_wire_sides_off.png",
		"jeija_insulated_wire_sides_off.png",
		"jeija_insulated_wire_ends_off.png",
		"jeija_insulated_wire_ends_off.png",
		"jeija_insulated_wire_sides_off.png",
		"jeija_insulated_wire_sides_off.png"
	},
	paramtype = "light",
	paramtype2 = "facedir",
	is_ground_content = false,
	walkable = false,
	sunlight_propagates = true,
	selection_box = {
		type = "fixed",
		fixed = { -16/32, -16/32, -7/32, 16/32, -12/32, 7/32 }
	},
	node_box = {
		type = "fixed",
		-- ±0.001 is to prevent z-fighting
		fixed = { -16/32-0.001, -17/32, -3/32, 16/32+0.001, -13/32, 3/32 }
	},
	groups = {dig_immediate = 3},
	sounds = mesecon.node_sound.default,
	mesecons = {conductor = {
		state = mesecon.state.off,
		onstate = "mesecons_insulated:insulated_on",
		rules = insulated_wire_get_rules
	}},
	on_blast = mesecon.on_blastnode,
	on_rotate = mesecon.on_rotate_horiz,
})

minetest.register_craft({
	output = "mesecons_insulated:insulated_off 3",
	recipe = {
		{"mesecons_materials:fiber", "mesecons_materials:fiber", "mesecons_materials:fiber"},
		{"group:mesecon_conductor_craftable", "group:mesecon_conductor_craftable", "group:mesecon_conductor_craftable"},
		{"mesecons_materials:fiber", "mesecons_materials:fiber", "mesecons_materials:fiber"},
	}
})


code:
name = mesecons_insulated
depends = mesecons, mesecons_gamecompat


code: Mesecon lamps are effectors that if powered emit light.


code:
-- MESELAMPS
-- A lamp is "is an electrical device used to create artificial light" (wikipedia)
-- guess what?

local mesecon_lamp_box = {
	type = "wallmounted",
	wall_top = {-0.3125,0.375,-0.3125,0.3125,0.5,0.3125},
	wall_bottom = {-0.3125,-0.5,-0.3125,0.3125,-0.375,0.3125},
	wall_side = {-0.375,-0.3125,-0.3125,-0.5,0.3125,0.3125},
}

local use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or nil

minetest.register_node("mesecons_lamp:lamp_on", {
	drawtype = "nodebox",
	tiles = {"jeija_meselamp_on.png"},
	use_texture_alpha = use_texture_alpha,
	paramtype = "light",
	paramtype2 = "wallmounted",
	is_ground_content = false,
	legacy_wallmounted = true,
	sunlight_propagates = true,
	walkable = true,
	light_source = minetest.LIGHT_MAX,
	node_box = mesecon_lamp_box,
	selection_box = mesecon_lamp_box,
	groups = {dig_immediate = 3,not_in_creative_inventory = 1, mesecon_effector_on = 1},
	drop = "mesecons_lamp:lamp_off 1",
	sounds = mesecon.node_sound.glass,
	mesecons = {effector = {
		action_off = function (pos, node)
			minetest.swap_node(pos, {name = "mesecons_lamp:lamp_off", param2 = node.param2})
		end,
		rules = mesecon.rules.wallmounted_get,
	}},
	on_blast = mesecon.on_blastnode,
})

minetest.register_node("mesecons_lamp:lamp_off", {
	drawtype = "nodebox",
	tiles = {"jeija_meselamp_off.png"},
	use_texture_alpha = use_texture_alpha,
	inventory_image = "jeija_meselamp.png",
	wield_image = "jeija_meselamp.png",
	paramtype = "light",
	paramtype2 = "wallmounted",
	is_ground_content = false,
	sunlight_propagates = true,
	walkable = true,
	node_box = mesecon_lamp_box,
	selection_box = mesecon_lamp_box,
	groups = {dig_immediate=3, mesecon_receptor_off = 1, mesecon_effector_off = 1},
	description = "Mesecon Lamp",
	sounds = mesecon.node_sound.glass,
	mesecons = {effector = {
		action_on = function (pos, node)
			minetest.swap_node(pos, {name = "mesecons_lamp:lamp_on", param2 = node.param2})
		end,
		rules = mesecon.rules.wallmounted_get,
	}},
	on_blast = mesecon.on_blastnode,
})

minetest.register_craft({
	output = "mesecons_lamp:lamp_off 1",
	recipe = {
		{"", "mesecons_gamecompat:glass", ""},
		{"group:mesecon_conductor_craftable", "mesecons_gamecompat:steel_ingot", "group:mesecon_conductor_craftable"},
		{"", "mesecons_gamecompat:glass", ""},
	}
})


code:
name = mesecons_lamp
depends = mesecons, mesecons_gamecompat


code:
Effector, glows blue when powered.
It works in an inactive block.


code:
Effector, glows dark grey when powered.
It works in an inactive block.


code:
Effector, glows green when powered.
It works in an inactive block.


code:
Effector, glows light grey when powered.
It works in an inactive block.


code:
Effector, glows red when powered.
It works in an inactive block.


code:
Effector, glows yellow when powered.
It works in an inactive block.


code:
local lightstone_rules = {
	{x=0,  y=0,  z=-1},
	{x=1,  y=0,  z=0},
	{x=-1, y=0,  z=0},
	{x=0,  y=0,  z=1},
	{x=1,  y=1,  z=0},
	{x=1,  y=-1, z=0},
	{x=-1, y=1,  z=0},
	{x=-1, y=-1, z=0},
	{x=0,  y=1,  z=1},
	{x=0,  y=-1, z=1},
	{x=0,  y=1,  z=-1},
	{x=0,  y=-1, z=-1},
	{x=0,  y=-1, z=0},
}

function mesecon.lightstone_add(name, base_item, texture_off, texture_on, desc)
	if not desc then
		desc = name .. " Lightstone"
	end
	minetest.register_node("mesecons_lightstone:lightstone_" .. name .. "_off", {
		tiles = {texture_off},
		is_ground_content = false,
		groups = {cracky = 2, mesecon_effector_off = 1, mesecon = 2},
		description = desc,
		sounds = mesecon.node_sound.stone,
		mesecons = {effector = {
			rules = lightstone_rules,
			action_on = function (pos, node)
				minetest.swap_node(pos, {name = "mesecons_lightstone:lightstone_" .. name .. "_on", param2 = node.param2})
			end,
		}},
		on_blast = mesecon.on_blastnode,
	})
	minetest.register_node("mesecons_lightstone:lightstone_" .. name .. "_on", {
		tiles = {texture_on},
		is_ground_content = false,
		groups = {cracky = 2, not_in_creative_inventory = 1, mesecon = 2},
		drop = "mesecons_lightstone:lightstone_" .. name .. "_off",
		light_source = minetest.LIGHT_MAX - 2,
		sounds = mesecon.node_sound.stone,
		mesecons = {effector = {
			rules = lightstone_rules,
			action_off = function (pos, node)
				minetest.swap_node(pos, {name = "mesecons_lightstone:lightstone_" .. name .. "_off", param2 = node.param2})
			end,
		}},
		on_blast = mesecon.on_blastnode,
	})

	minetest.register_craft({
		output = "mesecons_lightstone:lightstone_" .. name .. "_off",
		recipe = {
			{"",base_item,""},
			{base_item,"mesecons_gamecompat:torch",base_item},
			{"","group:mesecon_conductor_craftable",""}
		}
	})
end

mesecon.lightstone_add("red", "mesecons_gamecompat:dye_red", "jeija_lightstone_red_off.png", "jeija_lightstone_red_on.png", "Red Lightstone")
mesecon.lightstone_add("green", "mesecons_gamecompat:dye_green", "jeija_lightstone_green_off.png", "jeija_lightstone_green_on.png", "Green Lightstone")
mesecon.lightstone_add("blue", "mesecons_gamecompat:dye_blue", "jeija_lightstone_blue_off.png", "jeija_lightstone_blue_on.png", "Blue Lightstone")
mesecon.lightstone_add("gray", "mesecons_gamecompat:dye_grey", "jeija_lightstone_gray_off.png", "jeija_lightstone_gray_on.png", "Grey Lightstone")
mesecon.lightstone_add("darkgray", "mesecons_gamecompat:dye_dark_grey", "jeija_lightstone_darkgray_off.png", "jeija_lightstone_darkgray_on.png", "Dark Grey Lightstone")
mesecon.lightstone_add("yellow", "mesecons_gamecompat:dye_yellow", "jeija_lightstone_yellow_off.png", "jeija_lightstone_yellow_on.png", "Yellow Lightstone")
mesecon.lightstone_add("orange", "mesecons_gamecompat:dye_orange", "jeija_lightstone_orange_off.png", "jeija_lightstone_orange_on.png", "Orange Lightstone")
mesecon.lightstone_add("white", "mesecons_gamecompat:dye_white", "jeija_lightstone_white_off.png", "jeija_lightstone_white_on.png", "White Lightstone")
mesecon.lightstone_add("pink", "mesecons_gamecompat:dye_pink", "jeija_lightstone_pink_off.png", "jeija_lightstone_pink_on.png", "Pink Lightstone")
mesecon.lightstone_add("magenta", "mesecons_gamecompat:dye_magenta", "jeija_lightstone_magenta_off.png", "jeija_lightstone_magenta_on.png", "Magenta Lightstone")
mesecon.lightstone_add("cyan", "mesecons_gamecompat:dye_cyan", "jeija_lightstone_cyan_off.png", "jeija_lightstone_cyan_on.png", "Cyan Lightstone")
mesecon.lightstone_add("violet", "mesecons_gamecompat:dye_violet", "jeija_lightstone_violet_off.png", "jeija_lightstone_violet_on.png", "Violet Lightstone")


code:
name = mesecons_lightstone
depends = mesecons, mesecons_gamecompat


code:
The Luacontroller is an advanced programmable component.
You can simply code it in the language Mesecons uses itself: Lua!
All the code runs in a sandbox, so it's completely safe (but I won't guarantee that for absolute certainty!).
It works fine in an unloaded block, loading the block when the program needs to run.
Events are properly delivered after a server restart.

Documentation is available here!