From d91e0b66cb7971ba54d071a0955f17d1a7b0162e Mon Sep 17 00:00:00 2001
From: Jeija <norrepli@gmail.com>
Date: Wed, 26 Dec 2012 22:54:28 +0100
Subject: [PATCH] Re-write pistons from scratch, propably fixes a lot of bugs
 and doesn't cause too many new ones.

---
 .mesecons_pistons/depends.txt |   3 +
 .mesecons_pistons/init.lua    | 459 +++++++++++++++++++++
 mesecons/internal.lua         |  40 +-
 mesecons/services.lua         |   7 +-
 mesecons/settings.lua         |   1 +
 mesecons_lamp/init.lua        |  35 +-
 mesecons_movestones/init.lua  |  16 +-
 mesecons_mvps/init.lua        | 117 ++++--
 mesecons_pistons/depends.txt  |   1 -
 mesecons_pistons/init.lua     | 739 ++++++++++++++++++++--------------
 10 files changed, 1025 insertions(+), 393 deletions(-)
 create mode 100644 .mesecons_pistons/depends.txt
 create mode 100644 .mesecons_pistons/init.lua

diff --git a/.mesecons_pistons/depends.txt b/.mesecons_pistons/depends.txt
new file mode 100644
index 0000000..a596cf8
--- /dev/null
+++ b/.mesecons_pistons/depends.txt
@@ -0,0 +1,3 @@
+mesecons
+mesecons_materials
+mesecons_mvps
diff --git a/.mesecons_pistons/init.lua b/.mesecons_pistons/init.lua
new file mode 100644
index 0000000..0a3838d
--- /dev/null
+++ b/.mesecons_pistons/init.lua
@@ -0,0 +1,459 @@
+--PISTONS
+
+-- Get mesecon rules of pistons
+piston_rules =
+{{x=0,  y=0,  z=1}, --everything apart from z- (pusher side)
+ {x=1,  y=0,  z=0},
+ {x=-1, y=0,  z=0},
+ {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}}
+
+local piston_get_rules = function (node)
+	local rules = piston_rules
+	for i = 1, node.param2 do
+		rules = mesecon:rotate_rules_left(rules)
+	end
+	return rules
+end
+
+--starts the timer to make the piston update to its new state
+local update = function(pos, node)
+	local timer = minetest.env:get_node_timer(pos)
+	timer:stop()
+	timer:start(0)
+end
+
+--on_destruct callback, removes the piston pusher if it is present
+local destruct = function(pos, oldnode)
+	local dir = mesecon:piston_get_direction(oldnode)
+	pos.x, pos.y, pos.z = pos.x + dir.x, pos.y + dir.y, pos.z + dir.z --move to first node to check
+
+	--ensure piston is extended
+	local checknode = minetest.env:get_node(pos)
+	if checknode.name == "mesecons_pistons:piston_pusher_normal"
+	or checknode.name == "mesecons_pistons:piston_pusher_sticky" then
+		if checknode.param2 == oldnode.param2 then --pusher is facing the same direction as the piston
+			minetest.env:remove_node(pos) --remove the pusher
+		end
+	elseif oldnode.name == "mesecons_pistons:piston_up_normal" or oldnode.name == "mesecons_pistons:piston_up_sticky" then
+		if checknode.name == "mesecons_pistons:piston_up_pusher_normal" or checknode.name == "mesecons_pistons:piston_up_pusher_sticky" then
+			minetest.env:remove_node(pos) --remove the pusher
+		end
+	elseif oldnode.name == "mesecons_pistons:piston_down_normal" or oldnode.name == "mesecons_pistons:piston_down_sticky" then
+		if checknode.name == "mesecons_pistons:piston_down_pusher_normal" or checknode.name == "mesecons_pistons:piston_down_pusher_sticky" then
+			minetest.env:remove_node(pos) --remove the pusher
+		end
+	end
+end
+
+--node timer callback, pushes/pulls the piston depending on whether it is powered
+local timer = function(pos, elapsed)
+	if mesecon:is_powered(pos) then
+		mesecon:piston_push(pos)
+	else
+		mesecon:piston_pull(pos)
+	end
+	return false
+end
+
+--piston push action
+function mesecon:piston_push(pos)
+	local node = minetest.env:get_node(pos)
+	local dir = mesecon:piston_get_direction(node)
+	pos = mesecon:addPosRule(pos, dir) --move to first node being pushed
+
+	--determine the number of nodes that need to be pushed
+	local count = 0
+	local checkpos = {x=pos.x, y=pos.y, z=pos.z} --first node being pushed
+
+	while true do
+		local checknode = minetest.env:get_node(checkpos)
+
+		--check for collision with stopper or bounds
+		if mesecon:is_mvps_stopper(checknode.name) or checknode.name == "ignore" then
+			return
+		end
+
+		--check for column end
+		if checknode.name == "air"
+		or not(minetest.registered_nodes[checknode.name].liquidtype == "none") then
+			break
+		end
+
+		--limit piston pushing capacity
+		count = count + 1
+		if count > 15 then
+			return
+		end
+
+		checkpos = mesecon:addPosRule(checkpos, dir)
+	end
+
+	local thisnode = minetest.env:get_node(pos)
+	minetest.env:remove_node(pos)
+		mesecon.on_dignode(pos, thisnode)
+	local nextnode
+
+	--add pusher
+	if node.name == "mesecons_pistons:piston_normal" then
+		minetest.env:add_node(pos, {name="mesecons_pistons:piston_pusher_normal", param2=node.param2})
+	elseif node.name == "mesecons_pistons:piston_sticky" then
+		minetest.env:add_node(pos, {name="mesecons_pistons:piston_pusher_sticky", param2=node.param2})
+	elseif node.name == "mesecons_pistons:piston_up_normal" then
+		minetest.env:add_node(pos, {name="mesecons_pistons:piston_up_pusher_normal"})
+	elseif node.name == "mesecons_pistons:piston_up_sticky" then
+		minetest.env:add_node(pos, {name="mesecons_pistons:piston_up_pusher_sticky"})
+	elseif node.name == "mesecons_pistons:piston_down_normal" then
+		minetest.env:add_node(pos, {name="mesecons_pistons:piston_down_pusher_normal"})
+	elseif node.name == "mesecons_pistons:piston_down_sticky" then
+		minetest.env:add_node(pos, {name="mesecons_pistons:piston_down_pusher_sticky"})
+	end
+
+	--move nodes forward
+	for i = 1, count do
+		pos = mesecon:addPosRule(pos, dir)  --move to the next node
+
+		nextnode = minetest.env:get_node(pos)
+		minetest.env:remove_node(pos)
+			mesecon.on_dignode(pos, thisnode)
+		minetest.env:add_node(pos, thisnode)
+			mesecon.on_placenode(pos, thisnode)
+		thisnode = nextnode
+		nodeupdate(pos)
+	end
+end
+
+--piston pull action
+function mesecon:piston_pull(pos)
+	local node = minetest.env:get_node(pos)
+	local dir = mesecon:piston_get_direction(node)
+	pos = {x=pos.x + dir.x, y=pos.y + dir.y, z=pos.z + dir.z} --move to first node being replaced
+
+	--ensure piston is extended
+	local checknode = minetest.env:get_node(pos)
+	if node.name == "mesecons_pistons:piston_up_normal" or node.name == "mesecons_pistons:piston_up_sticky" then --up piston
+		if checknode.name ~= "mesecons_pistons:piston_up_pusher_normal" and checknode.name ~= "mesecons_pistons:piston_up_pusher_sticky" then
+			return --piston is not extended
+		end
+	elseif node.name == "mesecons_pistons:piston_down_normal" or node.name == "mesecons_pistons:piston_down_sticky" then --down piston
+		if checknode.name ~= "mesecons_pistons:piston_down_pusher_normal" and checknode.name ~= "mesecons_pistons:piston_down_pusher_sticky" then
+			return --piston is not extended
+		end
+	else --horizontal piston
+		if checknode.name ~= "mesecons_pistons:piston_pusher_normal" and checknode.name ~= "mesecons_pistons:piston_pusher_sticky" then
+			return --piston is not extended
+		end
+		if checknode.param2 ~= node.param2 then --pusher is not facing the same direction as the piston
+			return --piston is not extended
+		end
+	end
+
+	--retract piston
+	minetest.env:remove_node(pos) --remove pusher
+	if minetest.registered_nodes[node.name].is_sticky_piston then --retract block if piston is sticky
+		local retractpos  = mesecon:addPosRule(pos, dir)  --move to the node to be retracted
+		local retractnode = minetest.env:get_node(retractpos)
+		if  minetest.registered_nodes[retractnode.name].liquidtype == "none"
+		and not mesecon:is_mvps_stopper(retractnode.name) then
+			mesecon:move_node(retractpos, pos)
+			mesecon.on_dignode(retractpos, retractnode)
+			mesecon.on_placenode(pos, retractnode)
+			nodeupdate(pos)
+		end
+	end
+end
+
+--push direction of a piston
+function mesecon:piston_get_direction(node)
+	if node.name == "mesecons_pistons:piston_up_normal" or node.name == "mesecons_pistons:piston_up_sticky" then
+		return {x=0, y=1, z=0}
+	elseif node.name == "mesecons_pistons:piston_down_normal" or node.name == "mesecons_pistons:piston_down_sticky" then
+		return {x=0, y=-1, z=0}
+	elseif node.param2 == 3 then
+		return {x=1, y=0, z=0}
+	elseif node.param2 == 2 then
+		return {x=0, y=0, z=1}
+	elseif node.param2 == 1 then
+		return {x=-1, y=0, z=0}
+	else --node.param2 == 0
+		return {x=0, y=0, z=-1}
+	end
+end
+
+--horizontal pistons
+minetest.register_node("mesecons_pistons:piston_normal", {
+	description = "Piston",
+	tiles = {"jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_side.png"},
+	groups = {cracky=3, mesecon=2},
+	paramtype2 = "facedir",
+	after_destruct = destruct,
+	on_timer = timer,
+	after_place_node = function(pos, placer)
+		if not placer then --not placed by player
+			return
+		end
+		local pitch = placer:get_look_pitch() * (180 / math.pi) --placer pitch in degrees
+		if pitch > 45 then --looking upwards
+			minetest.env:add_node(pos, {name="mesecons_pistons:piston_down_normal"})
+		elseif pitch < -45 then --looking downwards
+			minetest.env:add_node(pos, {name="mesecons_pistons:piston_up_normal"})
+		end
+	end,
+	mesecons = {effector={
+		action_change = update,
+		rules = piston_get_rules
+	}}
+})
+
+minetest.register_node("mesecons_pistons:piston_sticky", {
+	description = "Sticky Piston",
+	tiles = {"jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_sticky_side.png"},
+	groups = {cracky=3, mesecon=2},
+	paramtype2 = "facedir",
+	after_destruct = destruct,
+	on_timer = timer,
+	is_sticky_piston = true,
+	after_place_node = function(pos, placer)
+		if not placer then --not placed by player
+			return
+		end
+		local pitch = placer:get_look_pitch() * (180 / math.pi) --placer pitch in degrees
+		if pitch > 45 then --looking upwards
+			minetest.env:add_node(pos, {name="mesecons_pistons:piston_down_sticky"})
+		elseif pitch < -45 then --looking downwards
+			minetest.env:add_node(pos, {name="mesecons_pistons:piston_up_sticky"})
+		end
+	end,
+	mesecons = {effector={
+		action_change = update,
+		rules = piston_get_rules
+	}}
+})
+
+minetest.register_node("mesecons_pistons:piston_pusher_normal", {
+	drawtype = "nodebox",
+	tiles = {"jeija_piston_pusher_normal.png"},
+	paramtype = "light",
+	paramtype2 = "facedir",
+	diggable = false,
+	selection_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.2, -0.3, 0.2, 0.2, 0.5},
+			{-0.5, -0.5, -0.5, 0.5, 0.5, -0.3},
+		},
+	},
+	node_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.2, -0.3, 0.2, 0.2, 0.5},
+			{-0.5, -0.5, -0.5, 0.5, 0.5, -0.3},
+		},
+	},
+})
+
+minetest.register_node("mesecons_pistons:piston_pusher_sticky", {
+	drawtype = "nodebox",
+	tiles = {
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_sticky.png"
+		},
+	paramtype = "light",
+	paramtype2 = "facedir",
+	diggable = false,
+	selection_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.2, -0.3, 0.2, 0.2, 0.5},
+			{-0.5, -0.5, -0.5, 0.5, 0.5, -0.3},
+		},
+	},
+	node_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.2, -0.3, 0.2, 0.2, 0.5},
+			{-0.5, -0.5, -0.5, 0.5, 0.5, -0.3},
+		},
+	},
+})
+
+mesecon:register_mvps_stopper("mesecons_pistons:piston_pusher_normal")
+mesecon:register_mvps_stopper("mesecons_pistons:piston_pusher_sticky")
+
+-- up pistons
+minetest.register_node("mesecons_pistons:piston_up_normal", {
+	tiles = {"jeija_piston_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
+	groups = {cracky=3, mesecon=2},
+	after_destruct = destruct,
+	on_timer = timer,
+	mesecons = {effector={
+		action_change = update
+	}},
+	drop = "mesecons_pistons:piston_normal",
+})
+
+minetest.register_node("mesecons_pistons:piston_up_sticky", {
+	tiles = {"jeija_piston_sticky_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
+	groups = {cracky=3, mesecon=2},
+	after_destruct = destruct,
+	on_timer = timer,
+	is_sticky_piston = true,
+	mesecons = {effector={
+		action_change = update
+	}},
+	drop = "mesecons_pistons:piston_sticky",
+})
+
+minetest.register_node("mesecons_pistons:piston_up_pusher_normal", {
+	drawtype = "nodebox",
+	tiles = {"jeija_piston_pusher_normal.png"},
+	paramtype = "light",
+	diggable = false,
+	selection_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.5, -0.2, 0.2, 0.3, 0.2},
+			{-0.5, 0.3, -0.5, 0.5, 0.5, 0.5},
+		},
+	},
+	node_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.5, -0.2, 0.2, 0.3, 0.2},
+			{-0.5, 0.3, -0.5, 0.5, 0.5, 0.5},
+		},
+	},
+})
+
+minetest.register_node("mesecons_pistons:piston_up_pusher_sticky", {
+	drawtype = "nodebox",
+	tiles = {
+		"jeija_piston_pusher_sticky.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png"
+		},
+	paramtype = "light",
+	diggable = false,
+	selection_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.5, -0.2, 0.2, 0.3, 0.2},
+			{-0.5, 0.3, -0.5, 0.5, 0.5, 0.5},
+		},
+	},
+	node_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.5, -0.2, 0.2, 0.3, 0.2},
+			{-0.5, 0.3, -0.5, 0.5, 0.5, 0.5},
+		},
+	},
+})
+
+mesecon:register_mvps_stopper("mesecons_pistons:piston_up_pusher_normal")
+mesecon:register_mvps_stopper("mesecons_pistons:piston_up_pusher_sticky")
+
+--down pistons
+minetest.register_node("mesecons_pistons:piston_down_normal", {
+	tiles = {"jeija_piston_tb.png", "jeija_piston_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
+	groups = {cracky=3, mesecon=2},
+	after_destruct = destruct,
+	on_timer = timer,
+	mesecons = {effector={
+		action_change = update
+	}},
+	drop = "mesecons_pistons:piston_normal",
+})
+
+minetest.register_node("mesecons_pistons:piston_down_sticky", {
+	tiles = {"jeija_piston_tb.png", "jeija_piston_sticky_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
+	groups = {cracky=3, mesecon=2},
+	after_destruct = destruct,
+	on_timer = timer,
+	is_sticky_piston = true,
+	mesecons = {effector={
+		action_change = update
+	}},
+	drop = "mesecons_pistons:piston_sticky",
+})
+
+minetest.register_node("mesecons_pistons:piston_down_pusher_normal", {
+	drawtype = "nodebox",
+	tiles = {"jeija_piston_pusher_normal.png"},
+	paramtype = "light",
+	diggable = false,
+	selection_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.3, -0.2, 0.2, 0.5, 0.2},
+			{-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
+		},
+	},
+	node_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.3, -0.2, 0.2, 0.5, 0.2},
+			{-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
+		},
+	},
+})
+
+minetest.register_node("mesecons_pistons:piston_down_pusher_sticky", {
+	drawtype = "nodebox",
+	tiles = {
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_sticky.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png"
+		},
+	paramtype = "light",
+	diggable = false,
+	selection_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.3, -0.2, 0.2, 0.5, 0.2},
+			{-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
+		},
+	},
+	node_box = {
+		type = "fixed",
+		fixed = {
+			{-0.2, -0.3, -0.2, 0.2, 0.5, 0.2},
+			{-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
+		},
+	},
+})
+
+mesecon:register_mvps_stopper("mesecons_pistons:piston_down_pusher_normal")
+mesecon:register_mvps_stopper("mesecons_pistons:piston_down_pusher_sticky")
+
+--craft recipes
+minetest.register_craft({
+	output = '"mesecons_pistons:piston_normal" 2',
+	recipe = {
+		{"default:wood", "default:wood", "default:wood"},
+		{"default:cobble", "default:steel_ingot", "default:cobble"},
+		{"default:cobble", "group:mesecon_conductor_craftable", "default:cobble"},
+	}
+})
+
+minetest.register_craft({
+	output = "mesecons_pistons:piston_sticky",
+	recipe = {
+		{"mesecons_materials:glue"},
+		{"mesecons_pistons:piston_normal"},
+	}
+})
diff --git a/mesecons/internal.lua b/mesecons/internal.lua
index 3f2ceb3..c7efd1a 100644
--- a/mesecons/internal.lua
+++ b/mesecons/internal.lua
@@ -413,45 +413,45 @@ end
 
 --Rules rotation Functions:
 function mesecon:rotate_rules_right(rules)
-	local nr={};
+	local nr = {}
 	for i, rule in ipairs(rules) do
-		nr[i]={}
-		nr[i].z=rule.x
-		nr[i].x=-rule.z
-		nr[i].y=rule.y
+		table.insert(nr, {
+			x = -rule.z, 
+			y =  rule.y, 
+			z =  rule.x})
 	end
 	return nr
 end
 
 function mesecon:rotate_rules_left(rules)
-	local nr={};
+	local nr = {}
 	for i, rule in ipairs(rules) do
-		nr[i]={}
-		nr[i].z=-rules[i].x
-		nr[i].x=rules[i].z
-		nr[i].y=rules[i].y
+		table.insert(nr, {
+			x =  rule.z, 
+			y =  rule.y, 
+			z = -rule.x})
 	end
 	return nr
 end
 
 function mesecon:rotate_rules_down(rules)
-	local nr={};
+	local nr = {}
 	for i, rule in ipairs(rules) do
-		nr[i]={}
-		nr[i].y=rule.x
-		nr[i].x=-rule.y
-		nr[i].z=rule.z
+		table.insert(nr, {
+			x = -rule.y, 
+			y =  rule.x, 
+			z =  rule.z})
 	end
 	return nr
 end
 
 function mesecon:rotate_rules_up(rules)
-	local nr={};
+	local nr = {}
 	for i, rule in ipairs(rules) do
-		nr[i]={}
-		nr[i].y=-rule.x
-		nr[i].x=rule.y
-		nr[i].z=rule.z
+		table.insert(nr, {
+			x =  rule.y, 
+			y = -rule.x, 
+			z =  rule.z})
 	end
 	return nr
 end
diff --git a/mesecons/services.lua b/mesecons/services.lua
index ada9351..a3aab43 100644
--- a/mesecons/services.lua
+++ b/mesecons/services.lua
@@ -2,8 +2,9 @@ mesecon.on_placenode = function (pos, node)
 	if mesecon:is_receptor_on(node.name) then
 		mesecon:receptor_on(pos, mesecon:receptor_get_rules(node))
 	elseif mesecon:is_powered(pos) then
-		if mesecon:is_conductor_off(node.name) then
-			mesecon:turnon(pos, node)
+		if mesecon:is_conductor(node.name) then
+			mesecon:turnon (pos)
+			mesecon:receptor_on (pos, mesecon:conductor_get_rules(node))
 		else
 			mesecon:changesignal(pos, node)
 			mesecon:activate(pos, node)
@@ -17,7 +18,7 @@ end
 
 mesecon.on_dignode = function (pos, node)
 	if mesecon:is_conductor_on(node.name) then
-		mesecon:receptor_off(pos)
+		mesecon:receptor_off(pos, mesecon:conductor_get_rules(node))
 	elseif mesecon:is_receptor_on(node.name) then
 		mesecon:receptor_off(pos, mesecon:receptor_get_rules(node))
 	end
diff --git a/mesecons/settings.lua b/mesecons/settings.lua
index 398ee6d..20776ee 100644
--- a/mesecons/settings.lua
+++ b/mesecons/settings.lua
@@ -3,3 +3,4 @@ BLINKY_PLANT_INTERVAL = 3
 NEW_STYLE_WIRES  = true  -- true = new nodebox wires, false = old raillike wires
 PRESSURE_PLATE_INTERVAL = 0.1
 OBJECT_DETECTOR_RADIUS = 6
+PISTON_MAXIMUM_PUSH = 15
diff --git a/mesecons_lamp/init.lua b/mesecons_lamp/init.lua
index d20236b..175a22b 100644
--- a/mesecons_lamp/init.lua
+++ b/mesecons_lamp/init.lua
@@ -2,6 +2,13 @@
 -- A lamp is "is an electrical device used to create artificial light" (wikipedia)
 -- guess what?
 
+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},
+}
+
 minetest.register_node("mesecons_lamp:lamp_on", {
 	drawtype = "nodebox",
 	tiles = {"jeija_meselamp_on.png"},
@@ -11,18 +18,8 @@ minetest.register_node("mesecons_lamp:lamp_on", {
 	sunlight_propagates = true,
 	walkable = true,
 	light_source = LIGHT_MAX,
-	node_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},
-	},
-	selection_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},
-	},
+	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',
 	mesecons = {effector = {
@@ -41,18 +38,8 @@ minetest.register_node("mesecons_lamp:lamp_off", {
 	paramtype2 = "wallmounted",
 	sunlight_propagates = true,
 	walkable = true,
-	node_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},
-	},
-	selection_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},
-	},
+	node_box = mesecon_lamp_box,
+	selection_box = mesecon_lamp_box,
 	groups = {dig_immediate=3, mesecon_receptor_off = 1, mesecon_effector_off = 1},
     	description="Meselamp",
 	mesecons = {effector = {
diff --git a/mesecons_movestones/init.lua b/mesecons_movestones/init.lua
index a7e5f2c..91b57c7 100644
--- a/mesecons_movestones/init.lua
+++ b/mesecons_movestones/init.lua
@@ -70,7 +70,7 @@ minetest.register_node("mesecons_movestones:movestone", {
 			repeat -- Check if it collides with a stopper
 				collpos = mesecon:addPosRule(collpos, direction)
 				checknode=minetest.env:get_node(collpos)
-				if mesecon:is_mvps_stopper(checknode.name) then 
+				if mesecon:is_mvps_stopper(checknode.name, direction) then 
 					return
 				end
 			until checknode.name=="air"
@@ -97,7 +97,7 @@ minetest.register_entity("mesecons_movestones:movestone_entity", {
 
 	on_step = function(self, dtime)
 		local pos = self.object:getpos()
-		local direction=mesecon:get_movestone_direction(pos)
+		local direction = mesecon:get_movestone_direction(pos)
 
 		if not direction then
 			minetest.env:add_node(pos, {name="mesecons_movestones:movestone"})
@@ -105,9 +105,9 @@ minetest.register_entity("mesecons_movestones:movestone_entity", {
 			return
 		end
 
-		self.object:setvelocity({x=direction.x*3, y=direction.y*3, z=direction.z*3})
+		self.object:setvelocity({x=direction.x*2, y=direction.y*2, z=direction.z*2})
 
-		mesecon:mvps_push(pos, direction)
+		mesecon:mvps_push(pos, direction, 100)
 	end,
 })
 
@@ -140,7 +140,7 @@ minetest.register_node("mesecons_movestones:sticky_movestone", {
 			repeat -- Check if it collides with a stopper
 				collpos = mesecon:addPosRule(collpos, direction)
 				checknode=minetest.env:get_node(collpos)
-				if mesecon:is_mvps_stopper(checknode.name) then 
+				if mesecon:is_mvps_stopper(checknode.name, direction) then 
 					return 
 				end
 			until checknode.name=="air"
@@ -149,7 +149,7 @@ minetest.register_node("mesecons_movestones:sticky_movestone", {
 			repeat -- Check if it collides with a stopper (pull direction)
 				collpos={x=collpos.x-direction.x, y=collpos.y-direction.y, z=collpos.z-direction.z}
 				checknode=minetest.env:get_node(collpos)
-				if mesecon:is_mvps_stopper(checknode.name) then
+				if mesecon:is_mvps_stopper(checknode.name, direction) then
 					return 
 				end
 			until checknode.name=="air"
@@ -192,9 +192,9 @@ minetest.register_entity("mesecons_movestones:sticky_movestone_entity", {
 			return
 		end
 
-		self.object:setvelocity({x=direction.x*3, y=direction.y*3, z=direction.z*3})
+		self.object:setvelocity({x=direction.x*2, y=direction.y*2, z=direction.z*2})
 
-		mesecon:mvps_push(pos, direction)
+		mesecon:mvps_push(pos, direction, 100)
 
 		--STICKY
 		mesecon:mvps_pull_all(pos, direction)
diff --git a/mesecons_mvps/init.lua b/mesecons_mvps/init.lua
index 3903910..57c538d 100644
--- a/mesecons_mvps/init.lua
+++ b/mesecons_mvps/init.lua
@@ -2,42 +2,99 @@
 
 mesecon.mvps_stoppers={}
 
-function mesecon:is_mvps_stopper(nodename)
-	local i=1
-	repeat
-		i=i+1
-		if mesecon.mvps_stoppers[i]==nodename then return true end
-	until mesecon.mvps_stoppers[i]==nil
-	return false
+function mesecon:is_mvps_stopper(node, pushdir, stack, stackid)
+	local get_stopper = mesecon.mvps_stoppers[node.name]
+	if type (get_stopper) == "function" then
+		get_stopper = get_stopper(node, pushdir, stack, stackid)
+	end
+	return get_stopper
 end
 
-function mesecon:register_mvps_stopper(nodename)
-	local i=1
-	repeat
-		i=i+1
-		if mesecon.mvps_stoppers[i]==nil then break end
-	until false
-	mesecon.mvps_stoppers[i]=nodename
+function mesecon:register_mvps_stopper(nodename, get_stopper)
+	if get_stopper == nil then
+			get_stopper = true
+	end
+	mesecon.mvps_stoppers[nodename] = get_stopper
 end
 
-function mesecon:mvps_push(pos, direction) -- pos: pos of mvps; direction: direction of push
-		pos.x=pos.x+direction.x
-		pos.y=pos.y+direction.y
-		pos.z=pos.z+direction.z
+function mesecon:mvps_process_stack(stack)
+	-- update mesecons for placed nodes ( has to be done after all nodes have been added )
+	for _, n in ipairs(stack) do
+		mesecon.on_placenode(n.pos, n.node)
+		mesecon:update_autoconnect(n.pos)
+	end
+end
 
-		local lpos = {x=pos.x, y=pos.y, z=pos.z}
-		local lnode = minetest.env:get_node(lpos)
-		local newnode
-		minetest.env:remove_node(lpos)
-		while not(lnode.name == "ignore" or lnode.name == "air" or not(minetest.registered_nodes[lnode.name].liquidtype == "none")) do
-			lpos.x=lpos.x+direction.x
-			lpos.y=lpos.y+direction.y
-			lpos.z=lpos.z+direction.z
-			newnode = lnode
-			lnode = minetest.env:get_node(lpos)
-			minetest.env:add_node(lpos, newnode)
-			nodeupdate(lpos)
+function mesecon:mvps_push(pos, dir, maximum) -- pos: pos of mvps; dir: direction of push; maximum: maximum nodes to be pushed
+	np = {x = pos.x, y = pos.y, z = pos.z}
+
+	-- determine the number of nodes to be pushed
+	local nodes = {}
+	while true do
+		nn = minetest.env:get_node_or_nil(np)
+		if not nn or #nodes > maximum then
+			-- don't push at all, something is in the way (unloaded map or too many nodes)
+			return
+		end
+
+		if nn.name == "air"
+		or minetest.registered_nodes[nn.name].liquidtype ~= "none" then --is liquid
+			break
+		end
+
+		table.insert (nodes, {node = nn, pos = np})
+
+		np = mesecon:addPosRule(np, dir)
+	end
+
+	-- determine if one of the nodes blocks the push
+	for id, n in ipairs(nodes) do
+		if mesecon:is_mvps_stopper(n.node, dir, nodes, id) then
+			return
 		end
+	end
+
+	-- remove all nodes
+	for _, n in ipairs(nodes) do
+		minetest.env:remove_node(n.pos)
+		nodeupdate(n.pos)
+	end
+
+	-- update mesecons for removed nodes ( has to be done after all nodes have been removed )
+	for _, n in ipairs(nodes) do
+		mesecon.on_dignode(n.pos, n.node)
+		mesecon:update_autoconnect(n.pos)
+	end
+
+	-- add nodes
+	for _, n in ipairs(nodes) do
+		np = mesecon:addPosRule(n.pos, dir)
+		minetest.env:add_node(np, n.node)
+		nodeupdate(np)
+	end
+
+	for i in ipairs(nodes) do
+		nodes[i].pos = mesecon:addPosRule(nodes[i].pos, dir)
+	end
+
+	return true, nodes
+end
+
+function mesecon:mvps_pull_single(pos, dir) -- pos: pos of mvps; direction: direction of pull (matches push direction for sticky pistons)
+	np = mesecon:addPosRule(pos, dir)
+	nn = minetest.env:get_node(np)
+
+	if minetest.registered_nodes[nn.name].liquidtype == "none"
+	and not mesecon:is_mvps_stopper(nn, {x = -dir.x, y = -dir.y, z = -dir.z}, {{pos = np, node = nn}}, 1) then
+		minetest.env:remove_node(np)
+		minetest.env:add_node(pos, nn)
+
+		nodeupdate(np)
+		nodeupdate(pos)
+		mesecon.on_dignode(np, nn)
+		mesecon:update_autoconnect(np)
+	end
+	return {{pos = np, node = nn}}
 end
 
 function mesecon:mvps_pull_all(pos, direction) -- pos: pos of mvps; direction: direction of pull
diff --git a/mesecons_pistons/depends.txt b/mesecons_pistons/depends.txt
index a596cf8..01f085b 100644
--- a/mesecons_pistons/depends.txt
+++ b/mesecons_pistons/depends.txt
@@ -1,3 +1,2 @@
 mesecons
-mesecons_materials
 mesecons_mvps
diff --git a/mesecons_pistons/init.lua b/mesecons_pistons/init.lua
index 47956cc..75547d2 100644
--- a/mesecons_pistons/init.lua
+++ b/mesecons_pistons/init.lua
@@ -1,4 +1,7 @@
---PISTONS
+--
+--
+--
+--
 
 -- Get mesecon rules of pistons
 piston_rules =
@@ -20,242 +23,194 @@ local piston_get_rules = function (node)
 	return rules
 end
 
---starts the timer to make the piston update to its new state
-local update = function(pos, node)
-	local timer = minetest.env:get_node_timer(pos)
-	timer:stop()
-	timer:start(0)
-end
-
---on_destruct callback, removes the piston pusher if it is present
-local destruct = function(pos, oldnode)
-	local dir = mesecon:piston_get_direction(oldnode)
-	pos.x, pos.y, pos.z = pos.x + dir.x, pos.y + dir.y, pos.z + dir.z --move to first node to check
-
-	--ensure piston is extended
-	local checknode = minetest.env:get_node(pos)
-	if checknode.name == "mesecons_pistons:piston_pusher_normal"
-	or checknode.name == "mesecons_pistons:piston_pusher_sticky" then
-		if checknode.param2 == oldnode.param2 then --pusher is facing the same direction as the piston
-			minetest.env:remove_node(pos) --remove the pusher
-		end
-	elseif oldnode.name == "mesecons_pistons:piston_up_normal" or oldnode.name == "mesecons_pistons:piston_up_sticky" then
-		if checknode.name == "mesecons_pistons:piston_up_pusher_normal" or checknode.name == "mesecons_pistons:piston_up_pusher_sticky" then
-			minetest.env:remove_node(pos) --remove the pusher
-		end
-	elseif oldnode.name == "mesecons_pistons:piston_down_normal" or oldnode.name == "mesecons_pistons:piston_down_sticky" then
-		if checknode.name == "mesecons_pistons:piston_down_pusher_normal" or checknode.name == "mesecons_pistons:piston_down_pusher_sticky" then
-			minetest.env:remove_node(pos) --remove the pusher
-		end
+piston_facedir_direction = function (node)
+	local rules = {{x = 0, y = 0, z = -1}}
+	for i = 1, node.param2 do
+		rules = mesecon:rotate_rules_left(rules)
 	end
+	return rules[1]
 end
 
---node timer callback, pushes/pulls the piston depending on whether it is powered
-local timer = function(pos, elapsed)
-	if mesecon:is_powered(pos) then
-		mesecon:piston_push(pos)
+piston_get_direction = function (dir, node)
+	if type(dir) == "function" then
+		return dir(node)
 	else
-		mesecon:piston_pull(pos)
+		return dir
 	end
-	return false
 end
 
---piston push action
-function mesecon:piston_push(pos)
-	local node = minetest.env:get_node(pos)
-	local dir = mesecon:piston_get_direction(node)
-	pos = mesecon:addPosRule(pos, dir) --move to first node being pushed
-
-	--determine the number of nodes that need to be pushed
-	local count = 0
-	local checkpos = {x=pos.x, y=pos.y, z=pos.z} --first node being pushed
-
-	while true do
-		local checknode = minetest.env:get_node(checkpos)
-
-		--check for collision with stopper or bounds
-		if mesecon:is_mvps_stopper(checknode.name) or checknode.name == "ignore" then
-			return
-		end
-
-		--check for column end
-		if checknode.name == "air"
-		or not(minetest.registered_nodes[checknode.name].liquidtype == "none") then
-			break
-		end
-
-		--limit piston pushing capacity
-		count = count + 1
-		if count > 15 then
-			return
-		end
-
-		checkpos = mesecon:addPosRule(checkpos, dir)
-	end
+local piston_remove_pusher = function (pos, node)
+	pistonspec = minetest.registered_nodes[node.name].mesecons_piston
 
-	local thisnode = minetest.env:get_node(pos)
-	minetest.env:remove_node(pos)
-		mesecon.on_dignode(pos, thisnode)
-	local nextnode
-
-	--add pusher
-	if node.name == "mesecons_pistons:piston_normal" then
-		minetest.env:add_node(pos, {name="mesecons_pistons:piston_pusher_normal", param2=node.param2})
-	elseif node.name == "mesecons_pistons:piston_sticky" then
-		minetest.env:add_node(pos, {name="mesecons_pistons:piston_pusher_sticky", param2=node.param2})
-	elseif node.name == "mesecons_pistons:piston_up_normal" then
-		minetest.env:add_node(pos, {name="mesecons_pistons:piston_up_pusher_normal"})
-	elseif node.name == "mesecons_pistons:piston_up_sticky" then
-		minetest.env:add_node(pos, {name="mesecons_pistons:piston_up_pusher_sticky"})
-	elseif node.name == "mesecons_pistons:piston_down_normal" then
-		minetest.env:add_node(pos, {name="mesecons_pistons:piston_down_pusher_normal"})
-	elseif node.name == "mesecons_pistons:piston_down_sticky" then
-		minetest.env:add_node(pos, {name="mesecons_pistons:piston_down_pusher_sticky"})
-	end
+	dir = piston_get_direction(pistonspec.dir, node)
+	local pusherpos = mesecon:addPosRule(pos, dir)
+	minetest.env:remove_node(pusherpos)
+	nodeupdate(pusherpos)
+end
 
-	--move nodes forward
-	for i = 1, count do
-		pos = mesecon:addPosRule(pos, dir)  --move to the next node
-
-		nextnode = minetest.env:get_node(pos)
-		minetest.env:remove_node(pos)
-			mesecon.on_dignode(pos, thisnode)
-		minetest.env:add_node(pos, thisnode)
-			mesecon.on_placenode(pos, thisnode)
-		thisnode = nextnode
-		nodeupdate(pos)
+local piston_on = function (pos, node)
+	local pistonspec = minetest.registered_nodes[node.name].mesecons_piston
+
+	dir = piston_get_direction(pistonspec.dir, node)
+	local np = mesecon:addPosRule(pos, dir)
+	success, stack = mesecon:mvps_push(np, dir, PISTON_MAXIMUM_PUSH)
+	if success then
+		minetest.env:add_node(pos, {param2 = node.param2, name = pistonspec.onname})
+		minetest.env:add_node(np, {param2 = node.param2, name = pistonspec.pusher})
+		mesecon:mvps_process_stack(stack)
 	end
 end
 
---piston pull action
-function mesecon:piston_pull(pos)
-	local node = minetest.env:get_node(pos)
-	local dir = mesecon:piston_get_direction(node)
-	pos = {x=pos.x + dir.x, y=pos.y + dir.y, z=pos.z + dir.z} --move to first node being replaced
-
-	--ensure piston is extended
-	local checknode = minetest.env:get_node(pos)
-	if node.name == "mesecons_pistons:piston_up_normal" or node.name == "mesecons_pistons:piston_up_sticky" then --up piston
-		if checknode.name ~= "mesecons_pistons:piston_up_pusher_normal" and checknode.name ~= "mesecons_pistons:piston_up_pusher_sticky" then
-			return --piston is not extended
-		end
-	elseif node.name == "mesecons_pistons:piston_down_normal" or node.name == "mesecons_pistons:piston_down_sticky" then --down piston
-		if checknode.name ~= "mesecons_pistons:piston_down_pusher_normal" and checknode.name ~= "mesecons_pistons:piston_down_pusher_sticky" then
-			return --piston is not extended
-		end
-	else --horizontal piston
-		if checknode.name ~= "mesecons_pistons:piston_pusher_normal" and checknode.name ~= "mesecons_pistons:piston_pusher_sticky" then
-			return --piston is not extended
-		end
-		if checknode.param2 ~= node.param2 then --pusher is not facing the same direction as the piston
-			return --piston is not extended
-		end
-	end
+local piston_off = function (pos, node)
+	local pistonspec = minetest.registered_nodes[node.name].mesecons_piston
+	minetest.env:add_node(pos, {param2 = node.param2, name = pistonspec.offname})
+	piston_remove_pusher (pos, node)
 
-	--retract piston
-	minetest.env:remove_node(pos) --remove pusher
-	if minetest.registered_nodes[node.name].is_sticky_piston then --retract block if piston is sticky
-		local retractpos  = mesecon:addPosRule(pos, dir)  --move to the node to be retracted
-		local retractnode = minetest.env:get_node(retractpos)
-		if  minetest.registered_nodes[retractnode.name].liquidtype == "none"
-		and not mesecon:is_mvps_stopper(retractnode.name) then
-			mesecon:move_node(retractpos, pos)
-			mesecon.on_dignode(retractpos, retractnode)
-			mesecon.on_placenode(pos, retractnode)
-			nodeupdate(pos)
-		end
+	if pistonspec.sticky then
+		dir = piston_get_direction(pistonspec.dir, node)
+		pullpos = mesecon:addPosRule(pos, dir)
+		stack = mesecon:mvps_pull_single(pullpos, dir)
+		mesecon:mvps_process_stack(stack)
 	end
 end
 
---push direction of a piston
-function mesecon:piston_get_direction(node)
-	if node.name == "mesecons_pistons:piston_up_normal" or node.name == "mesecons_pistons:piston_up_sticky" then
-		return {x=0, y=1, z=0}
-	elseif node.name == "mesecons_pistons:piston_down_normal" or node.name == "mesecons_pistons:piston_down_sticky" then
-		return {x=0, y=-1, z=0}
-	elseif node.param2 == 3 then
-		return {x=1, y=0, z=0}
-	elseif node.param2 == 2 then
-		return {x=0, y=0, z=1}
-	elseif node.param2 == 1 then
-		return {x=-1, y=0, z=0}
-	else --node.param2 == 0
-		return {x=0, y=0, z=-1}
+local piston_orientate = function (pos, placer)
+	-- not placed by player
+	if not placer then return end
+
+	-- placer pitch in degrees
+	local pitch = placer:get_look_pitch() * (180 / math.pi)
+
+	local node = minetest.env:get_node(pos)
+	local pistonspec = minetest.registered_nodes[node.name].mesecons_piston
+	if pitch > 55 then --looking upwards
+		minetest.env:add_node(pos, {name=pistonspec.piston_down})
+	elseif pitch < -55 then --looking downwards
+		minetest.env:add_node(pos, {name=pistonspec.piston_up})
 	end
 end
 
---horizontal pistons
-minetest.register_node("mesecons_pistons:piston_normal", {
+
+-- Horizontal pistons
+
+local pt = 2/16 -- pusher thickness
+
+local piston_pusher_box = {
+	type = "fixed",
+	fixed = {
+		{-2/16, -2/16, -.5 + pt, 2/16, 2/16,  .5 + pt},
+		{-.5  , -.5  , -.5     , .5  , .5  , -.5 + pt},
+	}
+}
+
+local piston_on_box = {
+	type = "fixed",
+	fixed = {
+		{-.5, -.5, -.5 + pt, .5, .5, .5}
+	}
+}
+
+
+-- Normal (non-sticky) ones:
+
+local pistonspec_normal = {
+	offname = "mesecons_pistons:piston_normal_off",
+	onname = "mesecons_pistons:piston_normal_on",
+	dir = piston_facedir_direction,
+	pusher = "mesecons_pistons:piston_pusher_normal",
+	piston_down = "mesecons_pistons:piston_down_normal_off",
+	piston_up   = "mesecons_pistons:piston_up_normal_off",
+}
+
+-- offstate
+minetest.register_node("mesecons_pistons:piston_normal_off", {
 	description = "Piston",
 	tiles = {"jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_side.png"},
-	groups = {cracky=3, mesecon=2},
+	groups = {cracky = 3},
 	paramtype2 = "facedir",
-	after_destruct = destruct,
-	on_timer = timer,
-	after_place_node = function(pos, placer)
-		if not placer then --not placed by player
-			return
-		end
-		local pitch = placer:get_look_pitch() * (180 / math.pi) --placer pitch in degrees
-		if pitch > 45 then --looking upwards
-			minetest.env:add_node(pos, {name="mesecons_pistons:piston_down_normal"})
-		elseif pitch < -45 then --looking downwards
-			minetest.env:add_node(pos, {name="mesecons_pistons:piston_up_normal"})
-		end
-	end,
+	after_place_node = piston_orientate,
+	mesecons_piston = pistonspec_normal,
 	mesecons = {effector={
-		action_change = update,
+		action_on = piston_on,
 		rules = piston_get_rules
 	}}
 })
 
-minetest.register_node("mesecons_pistons:piston_sticky", {
-	description = "Sticky Piston",
-	tiles = {"jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_sticky_side.png"},
-	groups = {cracky=3, mesecon=2},
+-- onstate
+minetest.register_node("mesecons_pistons:piston_normal_on", {
+	drawtype = "nodebox",
+	tiles = {"jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype = "light",
 	paramtype2 = "facedir",
-	after_destruct = destruct,
-	on_timer = timer,
-	is_sticky_piston = true,
-	after_place_node = function(pos, placer)
-		if not placer then --not placed by player
-			return
-		end
-		local pitch = placer:get_look_pitch() * (180 / math.pi) --placer pitch in degrees
-		if pitch > 45 then --looking upwards
-			minetest.env:add_node(pos, {name="mesecons_pistons:piston_down_sticky"})
-		elseif pitch < -45 then --looking downwards
-			minetest.env:add_node(pos, {name="mesecons_pistons:piston_up_sticky"})
-		end
-	end,
+	drop = {"mesecons_pistons:piston_normal_off"},
+	after_dig_node = piston_remove_pusher,
+	node_box = piston_on_box,
+	selection_box = piston_on_box,
+	mesecons_piston = pistonspec_normal,
 	mesecons = {effector={
-		action_change = update,
+		action_off = piston_off,
 		rules = piston_get_rules
 	}}
 })
 
+-- pusher
 minetest.register_node("mesecons_pistons:piston_pusher_normal", {
 	drawtype = "nodebox",
 	tiles = {"jeija_piston_pusher_normal.png"},
 	paramtype = "light",
 	paramtype2 = "facedir",
 	diggable = false,
-	selection_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.2, -0.3, 0.2, 0.2, 0.5},
-			{-0.5, -0.5, -0.5, 0.5, 0.5, -0.3},
-		},
-	},
-	node_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.2, -0.3, 0.2, 0.2, 0.5},
-			{-0.5, -0.5, -0.5, 0.5, 0.5, -0.3},
-		},
-	},
+	corresponding_piston = "mesecons_pistons:piston_normal_on",
+	selection_box = piston_pusher_box,
+	node_box = piston_pusher_box,
 })
 
+-- Sticky ones
+
+local pistonspec_sticky = {
+	offname = "mesecons_pistons:piston_sticky_off",
+	onname = "mesecons_pistons:piston_sticky_on",
+	dir = piston_facedir_direction,
+	pusher = "mesecons_pistons:piston_pusher_sticky",
+	sticky = true,
+	piston_down = "mesecons_pistons:piston_down_sticky_off",
+	piston_up   = "mesecons_pistons:piston_up_sticky_off",
+}
+
+-- offstate
+minetest.register_node("mesecons_pistons:piston_sticky_off", {
+	description = "Sticky Piston",
+	tiles = {"jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_sticky_side.png"},
+	groups = {cracky = 3},
+	paramtype2 = "facedir",
+	after_place_node = piston_orientate,
+	mesecons_piston = pistonspec_sticky,
+	mesecons = {effector={
+		action_on = piston_on,
+		rules = piston_get_rules
+	}}
+})
+
+-- onstate
+minetest.register_node("mesecons_pistons:piston_sticky_on", {
+	drawtype = "nodebox",
+	tiles = {"jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype = "light",
+	paramtype2 = "facedir",
+	drop = {"mesecons_pistons:piston_normal_off"},
+	after_dig_node = piston_remove_pusher,
+	node_box = piston_on_box,
+	selection_box = piston_on_box,
+	mesecons_piston = pistonspec_sticky,
+	mesecons = {effector={
+		action_off = piston_off,
+		rules = piston_get_rules
+	}}
+})
+
+-- pusher
 minetest.register_node("mesecons_pistons:piston_pusher_sticky", {
 	drawtype = "nodebox",
 	tiles = {
@@ -269,70 +224,125 @@ minetest.register_node("mesecons_pistons:piston_pusher_sticky", {
 	paramtype = "light",
 	paramtype2 = "facedir",
 	diggable = false,
-	selection_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.2, -0.3, 0.2, 0.2, 0.5},
-			{-0.5, -0.5, -0.5, 0.5, 0.5, -0.3},
-		},
-	},
-	node_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.2, -0.3, 0.2, 0.2, 0.5},
-			{-0.5, -0.5, -0.5, 0.5, 0.5, -0.3},
-		},
-	},
+	corresponding_piston = "mesecons_pistons:piston_sticky_on",
+	selection_box = piston_pusher_box,
+	node_box = piston_pusher_box,
 })
 
-mesecon:register_mvps_stopper("mesecons_pistons:piston_pusher_normal")
-mesecon:register_mvps_stopper("mesecons_pistons:piston_pusher_sticky")
+--
+--
+-- UP
+--
+--
+
+local piston_up_pusher_box = {
+	type = "fixed",
+	fixed = {
+		{-2/16, -.5 - pt, -2/16, 2/16, .5, 2/16},
+		{-.5  ,  .5 - pt, -.5  , .5  , .5,   .5},
+	}
+}
+
+local piston_up_on_box = {
+	type = "fixed",
+	fixed = {
+		{-.5, -.5, -.5 , .5, .5-pt, .5}
+	}
+}
 
---up pistons
-minetest.register_node("mesecons_pistons:piston_up_normal", {
+-- Normal
+
+local pistonspec_normal_down = {
+	offname = "mesecons_pistons:piston_up_normal_off",
+	onname = "mesecons_pistons:piston_up_normal_on",
+	dir = {x = 0, y = 1, z = 0},
+	pusher = "mesecons_pistons:piston_up_pusher_normal"
+}
+
+-- offstate
+minetest.register_node("mesecons_pistons:piston_up_normal_off", {
 	tiles = {"jeija_piston_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
-	groups = {cracky=3, mesecon=2},
-	after_destruct = destruct,
-	on_timer = timer,
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype2 = "facedir",
+	drop = {"mesecons_pistons:piston_normal_off"},
+	mesecons_piston = pistonspec_normal_down,
 	mesecons = {effector={
-		action_change = update
-	}},
-	drop = "mesecons_pistons:piston_normal",
+		action_on = piston_on,
+	}}
 })
 
-minetest.register_node("mesecons_pistons:piston_up_sticky", {
-	tiles = {"jeija_piston_sticky_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
-	groups = {cracky=3, mesecon=2},
-	after_destruct = destruct,
-	on_timer = timer,
-	is_sticky_piston = true,
+-- onstate
+minetest.register_node("mesecons_pistons:piston_up_normal_on", {
+	drawtype = "nodebox",
+	tiles = {"jeija_piston_tb.png"},
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype = "light",
+	paramtype2 = "facedir",
+	drop = {"mesecons_pistons:piston_normal_off"},
+	after_dig_node = piston_remove_pusher,
+	node_box = piston_up_on_box,
+	selection_box = piston_up_on_box,
+	mesecons_piston = pistonspec_normal_down,
 	mesecons = {effector={
-		action_change = update
-	}},
-	drop = "mesecons_pistons:piston_sticky",
+		action_off = piston_off,
+	}}
 })
 
+-- pusher
 minetest.register_node("mesecons_pistons:piston_up_pusher_normal", {
 	drawtype = "nodebox",
 	tiles = {"jeija_piston_pusher_normal.png"},
 	paramtype = "light",
+	paramtype2 = "facedir",
 	diggable = false,
-	selection_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.5, -0.2, 0.2, 0.3, 0.2},
-			{-0.5, 0.3, -0.5, 0.5, 0.5, 0.5},
-		},
-	},
-	node_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.5, -0.2, 0.2, 0.3, 0.2},
-			{-0.5, 0.3, -0.5, 0.5, 0.5, 0.5},
-		},
-	},
+	corresponding_piston = "mesecons_pistons:piston_up_normal_on",
+	selection_box = piston_up_pusher_box,
+	node_box = piston_up_pusher_box,
 })
 
+
+
+-- Sticky
+
+
+local pistonspec_sticky_up = {
+	offname = "mesecons_pistons:piston_up_sticky_off",
+	onname = "mesecons_pistons:piston_up_sticky_on",
+	dir = {x = 0, y = 1, z = 0},
+	pusher = "mesecons_pistons:piston_up_pusher_sticky",
+	sticky = true
+}
+
+-- offstate
+minetest.register_node("mesecons_pistons:piston_up_sticky_off", {
+	tiles = {"jeija_piston_sticky_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype2 = "facedir",
+	drop = {"mesecons_pistons:piston_sticky_off"},
+	mesecons_piston = pistonspec_sticky_up,
+	mesecons = {effector={
+		action_on = piston_on,
+	}}
+})
+
+-- onstate
+minetest.register_node("mesecons_pistons:piston_up_sticky_on", {
+	drawtype = "nodebox",
+	tiles = {"jeija_piston_tb.png"},
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype = "light",
+	paramtype2 = "facedir",
+	drop = {"mesecons_pistons:piston_normal_off"},
+	after_dig_node = piston_remove_pusher,
+	node_box = piston_up_on_box,
+	selection_box = piston_up_on_box,
+	mesecons_piston = pistonspec_sticky_up,
+	mesecons = {effector={
+		action_off = piston_off,
+	}}
+})
+
+-- pusher
 minetest.register_node("mesecons_pistons:piston_up_pusher_sticky", {
 	drawtype = "nodebox",
 	tiles = {
@@ -344,71 +354,133 @@ minetest.register_node("mesecons_pistons:piston_up_pusher_sticky", {
 		"jeija_piston_pusher_normal.png"
 		},
 	paramtype = "light",
+	paramtype2 = "facedir",
 	diggable = false,
-	selection_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.5, -0.2, 0.2, 0.3, 0.2},
-			{-0.5, 0.3, -0.5, 0.5, 0.5, 0.5},
-		},
-	},
-	node_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.5, -0.2, 0.2, 0.3, 0.2},
-			{-0.5, 0.3, -0.5, 0.5, 0.5, 0.5},
-		},
-	},
+	corresponding_piston = "mesecons_pistons:piston_up_sticky_on",
+	selection_box = piston_up_pusher_box,
+	node_box = piston_up_pusher_box,
 })
 
-mesecon:register_mvps_stopper("mesecons_pistons:piston_up_pusher_normal")
-mesecon:register_mvps_stopper("mesecons_pistons:piston_up_pusher_sticky")
+--
+--
+-- DOWN
+--
+--
+
+local piston_down_pusher_box = {
+	type = "fixed",
+	fixed = {
+		{-2/16, -.5, -2/16, 2/16,  .5 + pt, 2/16},
+		{-.5  , -.5, -.5  , .5  , -.5 + pt,   .5},
+	}
+}
+
+local piston_down_on_box = {
+	type = "fixed",
+	fixed = {
+		{-.5, -.5+pt, -.5 , .5, .5, .5}
+	}
+}
+
 
---down pistons
-minetest.register_node("mesecons_pistons:piston_down_normal", {
+
+-- Normal
+
+local pistonspec_normal_down = {
+	offname = "mesecons_pistons:piston_down_normal_off",
+	onname = "mesecons_pistons:piston_down_normal_on",
+	dir = {x = 0, y = -1, z = 0},
+	pusher = "mesecons_pistons:piston_down_pusher_normal",
+}
+
+-- offstate
+minetest.register_node("mesecons_pistons:piston_down_normal_off", {
 	tiles = {"jeija_piston_tb.png", "jeija_piston_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
-	groups = {cracky=3, mesecon=2},
-	after_destruct = destruct,
-	on_timer = timer,
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype2 = "facedir",
+	drop = {"mesecons_pistons:piston_normal_off"},
+	mesecons_piston = pistonspec_normal_down,
 	mesecons = {effector={
-		action_change = update
-	}},
-	drop = "mesecons_pistons:piston_normal",
+		action_on = piston_on,
+	}}
 })
 
-minetest.register_node("mesecons_pistons:piston_down_sticky", {
-	tiles = {"jeija_piston_tb.png", "jeija_piston_sticky_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
-	groups = {cracky=3, mesecon=2},
-	after_destruct = destruct,
-	on_timer = timer,
-	is_sticky_piston = true,
+-- onstate
+minetest.register_node("mesecons_pistons:piston_down_normal_on", {
+	drawtype = "nodebox",
+	tiles = {"jeija_piston_tb.png"},
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype = "light",
+	paramtype2 = "facedir",
+	drop = {"mesecons_pistons:piston_normal_off"},
+	after_dig_node = piston_remove_pusher,
+	node_box = piston_down_on_box,
+	selection_box = piston_down_on_box,
+	mesecons_piston = pistonspec_normal_down,
 	mesecons = {effector={
-		action_change = update
-	}},
-	drop = "mesecons_pistons:piston_sticky",
+		action_off = piston_off,
+	}}
 })
 
+-- pusher
 minetest.register_node("mesecons_pistons:piston_down_pusher_normal", {
 	drawtype = "nodebox",
-	tiles = {"jeija_piston_pusher_normal.png"},
+	tiles = {
+		"jeija_piston_pusher_sticky.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png",
+		"jeija_piston_pusher_normal.png"
+		},
 	paramtype = "light",
+	paramtype2 = "facedir",
 	diggable = false,
-	selection_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.3, -0.2, 0.2, 0.5, 0.2},
-			{-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
-		},
-	},
-	node_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.3, -0.2, 0.2, 0.5, 0.2},
-			{-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
-		},
-	},
+	corresponding_piston = "mesecons_pistons:piston_down_normal_on",
+	selection_box = piston_down_pusher_box,
+	node_box = piston_down_pusher_box,
 })
 
+-- Sticky
+
+local pistonspec_sticky_down = {
+	onname = "mesecons_pistons:piston_down_sticky_on",
+	offname = "mesecons_pistons:piston_down_sticky_off",
+	dir = {x = 0, y = -1, z = 0},
+	pusher = "mesecons_pistons:piston_down_pusher_sticky",
+	sticky = true
+}
+
+-- offstate
+minetest.register_node("mesecons_pistons:piston_down_sticky_off", {
+	tiles = {"jeija_piston_tb.png", "jeija_piston_sticky_side.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png", "jeija_piston_tb.png"},
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype2 = "facedir",
+	drop = {"mesecons_pistons:piston_sticky_off"},
+	mesecons_piston = pistonspec_sticky_down,
+	mesecons = {effector={
+		action_on = piston_on,
+	}}
+})
+
+-- onstate
+minetest.register_node("mesecons_pistons:piston_down_sticky_on", {
+	drawtype = "nodebox",
+	tiles = {"jeija_piston_tb.png"},
+	groups = {cracky = 3, not_in_creative_inventory = 1},
+	paramtype = "light",
+	paramtype2 = "facedir",
+	drop = {"mesecons_pistons:piston_sticky_off"},
+	after_dig_node = piston_remove_pusher,
+	node_box = piston_down_on_box,
+	selection_box = piston_down_on_box,
+	mesecons_piston = pistonspec_sticky_down,
+	mesecons = {effector={
+		action_off = piston_off,
+	}}
+})
+
+-- pusher
 minetest.register_node("mesecons_pistons:piston_down_pusher_sticky", {
 	drawtype = "nodebox",
 	tiles = {
@@ -420,25 +492,78 @@ minetest.register_node("mesecons_pistons:piston_down_pusher_sticky", {
 		"jeija_piston_pusher_normal.png"
 		},
 	paramtype = "light",
+	paramtype2 = "facedir",
 	diggable = false,
-	selection_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.3, -0.2, 0.2, 0.5, 0.2},
-			{-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
-		},
-	},
-	node_box = {
-		type = "fixed",
-		fixed = {
-			{-0.2, -0.3, -0.2, 0.2, 0.5, 0.2},
-			{-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
-		},
-	},
+	corresponding_piston = "mesecons_pistons:piston_down_sticky_on",
+	selection_box = piston_down_pusher_box,
+	node_box = piston_down_pusher_box,
 })
 
-mesecon:register_mvps_stopper("mesecons_pistons:piston_down_pusher_normal")
-mesecon:register_mvps_stopper("mesecons_pistons:piston_down_pusher_sticky")
+
+-- Register pushers as stoppers if they would be seperated from the piston
+local piston_pusher_get_stopper = function (node, dir, stack, stackid)
+	if (stack[stackid + 1]
+	and stack[stackid + 1].node.name   == minetest.registered_nodes[node.name].corresponding_piston
+	and stack[stackid + 1].node.param2 == node.param2)
+	or (stack[stackid - 1]
+	and stack[stackid - 1].node.name   == minetest.registered_nodes[node.name].corresponding_piston
+	and stack[stackid - 1].node.param2 == node.param2) then
+		return false
+	end
+	return true
+end
+
+local piston_pusher_up_down_get_stopper = function (node, dir, stack, stackid)
+	if (stack[stackid + 1]
+	and stack[stackid + 1].node.name   == minetest.registered_nodes[node.name].corresponding_piston)
+	or (stack[stackid - 1]
+	and stack[stackid - 1].node.name   == minetest.registered_nodes[node.name].corresponding_piston) then
+		return false
+	end
+	return true
+end
+
+mesecon:register_mvps_stopper("mesecons_pistons:piston_pusher_normal", piston_pusher_get_stopper)
+mesecon:register_mvps_stopper("mesecons_pistons:piston_pusher_sticky", piston_pusher_get_stopper)
+
+mesecon:register_mvps_stopper("mesecons_pistons:piston_up_pusher_normal", piston_pusher_up_down_get_stopper)
+mesecon:register_mvps_stopper("mesecons_pistons:piston_up_pusher_sticky", piston_pusher_up_down_get_stopper)
+
+mesecon:register_mvps_stopper("mesecons_pistons:piston_down_pusher_normal", piston_pusher_up_down_get_stopper)
+mesecon:register_mvps_stopper("mesecons_pistons:piston_down_pusher_sticky", piston_pusher_up_down_get_stopper)
+
+
+-- Register pistons as stoppers if they would be seperated from the stopper
+local piston_up_down_get_stopper = function (node, dir, stack, stackid)
+	if (stack[stackid + 1]
+	and stack[stackid + 1].node.name   == minetest.registered_nodes[node.name].mesecons_piston.pusher)
+	or (stack[stackid - 1]
+	and stack[stackid - 1].node.name   == minetest.registered_nodes[node.name].mesecons_piston.pusher) then
+		return false
+	end
+	return true
+end
+
+local piston_get_stopper = function (node, dir, stack, stackid)
+	if (stack[stackid + 1]
+	and stack[stackid + 1].node.name   == minetest.registered_nodes[node.name].mesecons_piston.pusher
+	and stack[stackid + 1].node.param2 == node.param2)
+	or (stack[stackid - 1]
+	and stack[stackid - 1].node.name   == minetest.registered_nodes[node.name].mesecons_piston.pusher
+	and stack[stackid + 1].node.param2 == node.param2) then
+		return false
+	end
+	return true
+end
+
+mesecon:register_mvps_stopper("mesecons_pistons:piston_normal_on", piston_get_stopper)
+mesecon:register_mvps_stopper("mesecons_pistons:piston_sticky_on", piston_pusher_get_stopper)
+
+mesecon:register_mvps_stopper("mesecons_pistons:piston_normal_on", piston_up_down_get_stopper)
+mesecon:register_mvps_stopper("mesecons_pistons:piston_sticky_on", piston_up_down_get_stopper)
+
+mesecon:register_mvps_stopper("mesecons_pistons:piston_normal_on", piston_up_down_get_stopper)
+mesecon:register_mvps_stopper("mesecons_pistons:piston_sticky_on", piston_up_down_get_stopper)
 
 --craft recipes
 minetest.register_craft({
-- 
GitLab