diff --git a/mesecons/services.lua b/mesecons/services.lua
index 1abbc0c3fb4072f50c72391a2ffc241b2ab6ee87..1e12de08e6a4d6ff3b224f9ec344a8fd5f29e6dd 100644
--- a/mesecons/services.lua
+++ b/mesecons/services.lua
@@ -66,30 +66,63 @@ minetest.register_on_placenode(mesecon.on_placenode)
 minetest.register_on_dignode(mesecon.on_dignode)
 
 -- Overheating service for fast circuits
+local OVERHEAT_MAX = mesecon.setting("overheat_max", 20)
+local COOLDOWN_TIME = mesecon.setting("cooldown_time", 2.0)
+local COOLDOWN_STEP = mesecon.setting("cooldown_granularity", 0.5)
+local COOLDOWN_MULTIPLIER = OVERHEAT_MAX / COOLDOWN_TIME
+local cooldown_timer = 0.0
+local object_heat = {}
 
 -- returns true if heat is too high
-mesecon.do_overheat = function(pos)
-	local meta = minetest.get_meta(pos)
-	local heat = meta:get_int("heat") or 0
-
-	heat = heat + 1
-	meta:set_int("heat", heat)
-
-	if heat < mesecon.setting("overheat_max", 20) then
-		mesecon.queue:add_action(pos, "cooldown", {}, 1, nil, 0)
-	else
+function mesecon.do_overheat(pos)
+	local id = minetest.hash_node_position(pos)
+	local heat = (object_heat[id] or 0) + 1
+	object_heat[id] = heat
+	if heat >= OVERHEAT_MAX then
+		minetest.log("action", "Node overheats at " .. minetest.pos_to_string(pos))
+		object_heat[id] = nil
 		return true
 	end
-
 	return false
 end
 
+function mesecon.do_cooldown(pos)
+	local id = minetest.hash_node_position(pos)
+	object_heat[id] = nil
+end
 
-mesecon.queue:add_function("cooldown", function (pos)
-	local meta = minetest.get_meta(pos)
-	local heat = meta:get_int("heat")
+function mesecon.get_heat(pos)
+	local id = minetest.hash_node_position(pos)
+	return object_heat[id] or 0
+end
 
-	if (heat > 0) then
-		meta:set_int("heat", heat - 1)
+function mesecon.move_hot_nodes(moved_nodes)
+	local new_heat = {}
+	for _, n in ipairs(moved_nodes) do
+		local old_id = minetest.hash_node_position(n.oldpos)
+		local new_id = minetest.hash_node_position(n.pos)
+		new_heat[new_id] = object_heat[old_id]
+		object_heat[old_id] = nil
+	end
+	for id, heat in pairs(new_heat) do
+		object_heat[id] = heat
 	end
-end)
+end
+
+local function global_cooldown(dtime)
+	cooldown_timer = cooldown_timer + dtime
+	if cooldown_timer < COOLDOWN_STEP then
+		return -- don't overload the CPU
+	end
+	local cooldown = COOLDOWN_MULTIPLIER * cooldown_timer
+	cooldown_timer = 0
+	for id, heat in pairs(object_heat) do
+		heat = heat - cooldown
+		if heat <= 0 then
+			object_heat[id] = nil -- free some RAM
+		else
+			object_heat[id] = heat
+		end
+	end
+end
+minetest.register_globalstep(global_cooldown)
diff --git a/mesecons_gates/init.lua b/mesecons_gates/init.lua
index 13e583be5e5de231745c30ecf5771deadc933d81..3807d6f65e894aa3b27a08f35b96ab4d52da23a4 100644
--- a/mesecons_gates/init.lua
+++ b/mesecons_gates/init.lua
@@ -74,7 +74,8 @@ local function register_gate(name, inputnumber, assess, recipe, description)
 		assess = assess,
 		onstate = basename.."_on",
 		offstate = basename.."_off",
-		inputnumber = inputnumber
+		inputnumber = inputnumber,
+		after_dig_node = mesecon.do_cooldown,
 	},{
 		tiles = {"jeija_microcontroller_bottom.png^".."jeija_gate_off.png^"..
 			"jeija_gate_"..name..".png"},
diff --git a/mesecons_luacontroller/init.lua b/mesecons_luacontroller/init.lua
index c754eda788c0721155b6f99826058a388f04831f..66dc73b8de3e8d81a2c7c2b0113dfc44c25ca27c 100644
--- a/mesecons_luacontroller/init.lua
+++ b/mesecons_luacontroller/init.lua
@@ -315,7 +315,7 @@ local function create_environment(pos, mem, event)
 		port = vports_copy,
 		event = event,
 		mem = mem,
-		heat = minetest.get_meta(pos):get_int("heat"),
+		heat = mesecon.get_heat(pos),
 		heat_max = mesecon.setting("overheat_max", 20),
 		print = safe_print,
 		interrupt = get_interrupt(pos),
@@ -485,7 +485,6 @@ local function reset_meta(pos, code, errmsg)
 		"image_button[3.75,6;2.5,1;jeija_luac_runbutton.png;program;]"..
 		"image_button_exit[9.72,-0.25;0.425,0.4;jeija_close_window.png;exit;]"..
 		"label[0.1,5;"..errmsg.."]")
-	meta:set_int("heat", 0)
 	meta:set_int("luac_id", math.random(1, 65535))
 end
 
@@ -626,6 +625,7 @@ for d = 0, 1 do
 			d = d == 1,
 		},
 		after_dig_node = function (pos, node)
+			mesecon.do_cooldown(pos)
 			mesecon.receptor_off(pos, output_rules)
 		end,
 		is_luacontroller = true,
diff --git a/mesecons_mvps/init.lua b/mesecons_mvps/init.lua
index 2c6161b72304614d1cb6714a629341fb61cf1132..252acf7303b3dcf040ec28d36ccb4fb5fff2e711 100644
--- a/mesecons_mvps/init.lua
+++ b/mesecons_mvps/init.lua
@@ -246,3 +246,4 @@ mesecon.register_mvps_stopper("doors:door_steel_t_1")
 mesecon.register_mvps_stopper("doors:door_steel_b_2")
 mesecon.register_mvps_stopper("doors:door_steel_t_2")
 mesecon.register_mvps_stopper("default:chest_locked")
+mesecon.register_on_mvps_move(mesecon.move_hot_nodes)