Convert furnace ABM to node timer
authorAuke Kok <sofar@foo-projects.org>
Wed, 10 Feb 2016 06:03:40 +0000 (22:03 -0800)
committerparamat <mat.gregory@virginmedia.com>
Sat, 13 Feb 2016 03:47:38 +0000 (03:47 +0000)
Node timers are higher precision and a better guarantee
of happening at regular intervals, whereas ABM's may be
postponed, cancelled or missed if a player is too far.

The largest benefit is that once the furnace is done
cooking, no more ABM's are fired - the timer is stopped
instead and no more events are created until items
are put in the furnace.

This patch is larger due to the migration of the timer
function and indentation change as a result of the somewhat
reduced complexity. I've tested with several furnaces and
this works correctly and behavior is not affected, although
people may find that their furnaces now work more
regularly.

If you place several furnaces next to eachother, you will
still find all furnace timers firing exactly at the same
time. This is a bug in core that should not coalesce node
timers at second intervals.

mods/default/furnace.lua

index 6d89aae153128cf870bda3c1464d249a9d451b09..aa2be2125ae46ad28318236a2445f29a642c219f 100644 (file)
@@ -90,6 +90,137 @@ local function allow_metadata_inventory_take(pos, listname, index, stack, player
        return stack:get_count()
 end
 
+local function swap_node(pos, name)
+       local node = minetest.get_node(pos)
+       if node.name == name then
+               return
+       end
+       node.name = name
+       minetest.swap_node(pos, node)
+end
+
+local function furnace_node_timer(pos, elapsed)
+       --
+       -- Inizialize metadata
+       --
+       local meta = minetest.get_meta(pos)
+       local fuel_time = meta:get_float("fuel_time") or 0
+       local src_time = meta:get_float("src_time") or 0
+       local fuel_totaltime = meta:get_float("fuel_totaltime") or 0
+
+       local inv = meta:get_inventory()
+       local srclist = inv:get_list("src")
+       local fuellist = inv:get_list("fuel")
+       local dstlist = inv:get_list("dst")
+
+       --
+       -- Cooking
+       --
+
+       -- Check if we have cookable content
+       local cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
+       local cookable = true
+
+       if cooked.time == 0 then
+               cookable = false
+       end
+
+       -- Check if we have enough fuel to burn
+       if fuel_time < fuel_totaltime then
+               -- The furnace is currently active and has enough fuel
+               fuel_time = fuel_time + 1
+
+               -- If there is a cookable item then check if it is ready yet
+               if cookable then
+                       src_time = src_time + 1
+                       if src_time >= cooked.time then
+                               -- Place result in dst list if possible
+                               if inv:room_for_item("dst", cooked.item) then
+                                       inv:add_item("dst", cooked.item)
+                                       inv:set_stack("src", 1, aftercooked.items[1])
+                                       src_time = 0
+                               end
+                       end
+               end
+       else
+               -- Furnace ran out of fuel
+               if cookable then
+                       -- We need to get new fuel
+                       local fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
+
+                       if fuel.time == 0 then
+                               -- No valid fuel in fuel list
+                               fuel_totaltime = 0
+                               fuel_time = 0
+                               src_time = 0
+                       else
+                               -- Take fuel from fuel list
+                               inv:set_stack("fuel", 1, afterfuel.items[1])
+
+                               fuel_totaltime = fuel.time
+                               fuel_time = 0
+                       end
+               else
+                       -- We don't need to get new fuel since there is no cookable item
+                       fuel_totaltime = 0
+                       fuel_time = 0
+                       src_time = 0
+               end
+       end
+
+       --
+       -- Update formspec, infotext and node
+       --
+       local formspec = inactive_formspec
+       local item_state = ""
+       local item_percent = 0
+       if cookable then
+               item_percent = math.floor(src_time / cooked.time * 100)
+               item_state = item_percent .. "%"
+       else
+               if srclist[1]:is_empty() then
+                       item_state = "Empty"
+               else
+                       item_state = "Not cookable"
+               end
+       end
+
+       local fuel_state = "Empty"
+       local active = "inactive "
+       local result = false
+
+       if fuel_time <= fuel_totaltime and fuel_totaltime ~= 0 then
+               active = "active "
+               local fuel_percent = math.floor(fuel_time / fuel_totaltime * 100)
+               fuel_state = fuel_percent .. "%"
+               formspec = active_formspec(fuel_percent, item_percent)
+               swap_node(pos, "default:furnace_active")
+               -- make sure timer restarts automatically
+               result = true
+       else
+               if not fuellist[1]:is_empty() then
+                       fuel_state = "0%"
+               end
+               swap_node(pos, "default:furnace")
+               -- stop timer on the inactive furnace
+               local timer = minetest.get_node_timer(pos)
+               timer:stop()
+       end
+
+       local infotext = "Furnace " .. active .. "(Item: " .. item_state .. "; Fuel: " .. fuel_state .. ")"
+
+       --
+       -- Set meta values
+       --
+       meta:set_float("fuel_totaltime", fuel_totaltime)
+       meta:set_float("fuel_time", fuel_time)
+       meta:set_float("src_time", src_time)
+       meta:set_string("formspec", formspec)
+       meta:set_string("infotext", infotext)
+
+       return result
+end
+
 --
 -- Node definitions
 --
@@ -106,9 +237,26 @@ minetest.register_node("default:furnace", {
        legacy_facedir_simple = true,
        is_ground_content = false,
        sounds = default.node_sound_stone_defaults(),
-       
+
        can_dig = can_dig,
-       
+
+       on_timer = furnace_node_timer,
+
+       on_construct = function(pos)
+               local meta = minetest.get_meta(pos)
+               meta:set_string("formspec", inactive_formspec)
+               local inv = meta:get_inventory()
+               inv:set_size('src', 1)
+               inv:set_size('fuel', 1)
+               inv:set_size('dst', 4)
+       end,
+
+       on_metadata_inventory_put = function(pos)
+               -- start timer function, it will sort out whether furnace can burn or not.
+               local timer = minetest.get_node_timer(pos)
+               timer:start(1.0)
+       end,
+
        allow_metadata_inventory_put = allow_metadata_inventory_put,
        allow_metadata_inventory_move = allow_metadata_inventory_move,
        allow_metadata_inventory_take = allow_metadata_inventory_take,
@@ -138,154 +286,12 @@ minetest.register_node("default:furnace_active", {
        legacy_facedir_simple = true,
        is_ground_content = false,
        sounds = default.node_sound_stone_defaults(),
-       
+       on_timer = furnace_node_timer,
+
        can_dig = can_dig,
-       
+
        allow_metadata_inventory_put = allow_metadata_inventory_put,
        allow_metadata_inventory_move = allow_metadata_inventory_move,
        allow_metadata_inventory_take = allow_metadata_inventory_take,
 })
 
---
--- ABM
---
-
-local function swap_node(pos, name)
-       local node = minetest.get_node(pos)
-       if node.name == name then
-               return
-       end
-       node.name = name
-       minetest.swap_node(pos, node)
-end
-
-minetest.register_abm({
-       nodenames = {"default:furnace", "default:furnace_active"},
-       interval = 1.0,
-       chance = 1,
-       action = function(pos, node, active_object_count, active_object_count_wider)
-               --
-               -- Inizialize metadata
-               --
-               local meta = minetest.get_meta(pos)
-               local fuel_time = meta:get_float("fuel_time") or 0
-               local src_time = meta:get_float("src_time") or 0
-               local fuel_totaltime = meta:get_float("fuel_totaltime") or 0
-               
-               --
-               -- Inizialize inventory
-               --
-               local inv = meta:get_inventory()
-               for listname, size in pairs({
-                               src = 1,
-                               fuel = 1,
-                               dst = 4,
-               }) do
-                       if inv:get_size(listname) ~= size then
-                               inv:set_size(listname, size)
-                       end
-               end
-               local srclist = inv:get_list("src")
-               local fuellist = inv:get_list("fuel")
-               local dstlist = inv:get_list("dst")
-               
-               --
-               -- Cooking
-               --
-               
-               -- Check if we have cookable content
-               local cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
-               local cookable = true
-               
-               if cooked.time == 0 then
-                       cookable = false
-               end
-               
-               -- Check if we have enough fuel to burn
-               if fuel_time < fuel_totaltime then
-                       -- The furnace is currently active and has enough fuel
-                       fuel_time = fuel_time + 1
-                       
-                       -- If there is a cookable item then check if it is ready yet
-                       if cookable then
-                               src_time = src_time + 1
-                               if src_time >= cooked.time then
-                                       -- Place result in dst list if possible
-                                       if inv:room_for_item("dst", cooked.item) then
-                                               inv:add_item("dst", cooked.item)
-                                               inv:set_stack("src", 1, aftercooked.items[1])
-                                               src_time = 0
-                                       end
-                               end
-                       end
-               else
-                       -- Furnace ran out of fuel
-                       if cookable then
-                               -- We need to get new fuel
-                               local fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
-                               
-                               if fuel.time == 0 then
-                                       -- No valid fuel in fuel list
-                                       fuel_totaltime = 0
-                                       fuel_time = 0
-                                       src_time = 0
-                               else
-                                       -- Take fuel from fuel list
-                                       inv:set_stack("fuel", 1, afterfuel.items[1])
-                                       
-                                       fuel_totaltime = fuel.time
-                                       fuel_time = 0
-                                       
-                               end
-                       else
-                               -- We don't need to get new fuel since there is no cookable item
-                               fuel_totaltime = 0
-                               fuel_time = 0
-                               src_time = 0
-                       end
-               end
-               
-               --
-               -- Update formspec, infotext and node
-               --
-               local formspec = inactive_formspec
-               local item_state = ""
-               local item_percent = 0
-               if cookable then
-                       item_percent =  math.floor(src_time / cooked.time * 100)
-                       item_state = item_percent .. "%"
-               else
-                       if srclist[1]:is_empty() then
-                               item_state = "Empty"
-                       else
-                               item_state = "Not cookable"
-                       end
-               end
-               
-               local fuel_state = "Empty"
-               local active = "inactive "
-               if fuel_time <= fuel_totaltime and fuel_totaltime ~= 0 then
-                       active = "active "
-                       local fuel_percent = math.floor(fuel_time / fuel_totaltime * 100)
-                       fuel_state = fuel_percent .. "%"
-                       formspec = active_formspec(fuel_percent, item_percent)
-                       swap_node(pos, "default:furnace_active")
-               else
-                       if not fuellist[1]:is_empty() then
-                               fuel_state = "0%"
-                       end
-                       swap_node(pos, "default:furnace")
-               end
-               
-               local infotext =  "Furnace " .. active .. "(Item: " .. item_state .. "; Fuel: " .. fuel_state .. ")"
-               
-               --
-               -- Set meta values
-               --
-               meta:set_float("fuel_totaltime", fuel_totaltime)
-               meta:set_float("fuel_time", fuel_time)
-               meta:set_float("src_time", src_time)
-               meta:set_string("formspec", formspec)
-               meta:set_string("infotext", infotext)
-       end,
-})