From 8e8ccddb68434e4dc6f4047ab9215a052c259a8e Mon Sep 17 00:00:00 2001 From: cora Date: Fri, 14 Jan 2022 22:15:44 +0100 Subject: [PATCH 01/13] Replace MineClone2 fire with Minetest Game ABMs ABMs have better performance than the laggy timer-based implementation. --- mods/ITEMS/mcl_fire/init.lua | 185 ++++++++++++----------------------- 1 file changed, 63 insertions(+), 122 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 50303e3b..b29e60ef 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -124,82 +124,6 @@ minetest.register_node("mcl_fire:fire", { minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true) end end, - on_timer = function(pos) - local node = minetest.get_node(pos) - -- Age is a number from 0 to 15 and is increased every timer step. - -- "old" fire is more likely to be extinguished - local age = node.param2 - local flammables = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y+4, z=pos.z+1}, {"group:flammable"}) - local below = minetest.get_node({x=pos.x, y=pos.z-1, z=pos.z}) - local below_is_flammable = minetest.get_item_group(below.name, "flammable") > 0 - -- Extinguish fire - if (not fire_enabled) and (math.random(1,3) == 1) then - minetest.remove_node(pos) - return - end - if age == 15 and not below_is_flammable then - minetest.remove_node(pos) - return - elseif age > 3 and #flammables == 0 and not below_is_flammable and math.random(1,4) == 1 then - minetest.remove_node(pos) - return - end - local age_add = 1 - -- If fire spread is disabled, we have to skip the "destructive" code - if (not fire_enabled) then - if age + age_add <= 15 then - node.param2 = age + age_add - minetest.set_node(pos, node) - end - -- Restart timer - fire_timer(pos) - return - end - -- Spawn fire to nearby flammable nodes - local is_next_to_flammable = minetest.find_node_near(pos, 2, {"group:flammable"}) ~= nil - if is_next_to_flammable and math.random(1,2) == 1 then - -- The fire we spawn copies the age of this fire. - -- This prevents fire from spreading infinitely far as the fire fire dies off - -- quicker the further it has spreaded. - local age_next = math.min(15, age + math.random(0, 1)) - -- Select random type of fire spread - local burntype = math.random(1,2) - if burntype == 1 then - -- Spawn fire in air - local nodes = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y+4, z=pos.z+1}, {"air"}) - while #nodes > 0 do - local r = math.random(1, #nodes) - if minetest.find_node_near(nodes[r], 1, {"group:flammable"}) then - spawn_fire(nodes[r], age_next) - break - else - table.remove(nodes, r) - end - end - else - -- Burn flammable block - local nodes = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y+4, z=pos.z+1}, {"group:flammable"}) - if #nodes > 0 then - local r = math.random(1, #nodes) - local nn = minetest.get_node(nodes[r]).name - local ndef = minetest.registered_nodes[nn] - local fgroup = minetest.get_item_group(nn, "flammable") - if ndef and ndef._on_burn then - ndef._on_burn(nodes[r]) - elseif fgroup ~= -1 then - spawn_fire(nodes[r], age_next) - end - end - end - end - -- Regular age increase - if age + age_add <= 15 then - node.param2 = age + age_add - minetest.set_node(pos, node) - end - -- Restart timer - fire_timer(pos) - end, drop = "", sounds = {}, -- Turn into eternal fire on special blocks, light Nether portal (if possible), start burning timer @@ -255,29 +179,7 @@ minetest.register_node("mcl_fire:eternal_fire", { minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true) end end, - on_timer = function(pos) - if fire_enabled then - local airs = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y+4, z=pos.z+1}, {"air"}) - while #airs > 0 do - local r = math.random(1, #airs) - if minetest.find_node_near(airs[r], 1, {"group:flammable"}) then - local node = minetest.get_node(airs[r]) - local age = node.param2 - local age_next = math.min(15, age + math.random(0, 1)) - spawn_fire(airs[r], age_next) - break - else - table.remove(airs, r) - end - end - end - -- Restart timer - fire_timer(pos) - end, - -- Start burning timer and light Nether portal (if possible) on_construct = function(pos) - fire_timer(pos) - if minetest.get_modpath("mcl_portals") then mcl_portals.light_nether_portal(pos) end @@ -402,6 +304,31 @@ if flame_sound then end +-- Set pointed_thing on (normal) fire. +-- * pointed_thing: Pointed thing to ignite +-- * player: Player who sets fire or nil if nobody +-- * allow_on_fire: If false, can't ignite fire on fire (default: true) +mcl_fire.set_fire = function(pointed_thing, player, allow_on_fire) + local pname + if player == nil then + pname = "" + else + pname = player:get_player_name() + end + local n = minetest.get_node(pointed_thing.above) + local nu = minetest.get_node(pointed_thing.under) + if allow_on_fire == false and minetest.get_item_group(nu.name, "fire") ~= 0 then + return + end + if minetest.is_protected(pointed_thing.above, pname) then + minetest.record_protection_violation(pointed_thing.above, pname) + return + end + if n.name == "air" then + minetest.add_node(pointed_thing.above, {name="mcl_fire:fire"}) + end +end + -- -- ABMs -- @@ -508,31 +435,45 @@ else -- Fire enabled end, }) -end + -- Ignite neighboring nodes, add basic flames + minetest.register_abm({ + label = "Ignite flame", + nodenames = {"group:flammable"}, + neighbors = {"mcl_fire:fire","mcl_fire:eternal_fire","mcl_core:lava_source","mcl_core:lava_flowing","mcl_nether:nether_lava_source","mcl_nether:nether_lava_flowing",}, + interval = 7, + chance = 12, + catch_up = false, + action = function(pos) + local p = minetest.find_node_near(pos, 1, {"air"}) + if p then + minetest.set_node(p, {name = "mcl_fire:fire"}) + end + end + }) --- Set pointed_thing on (normal) fire. --- * pointed_thing: Pointed thing to ignite --- * player: Player who sets fire or nil if nobody --- * allow_on_fire: If false, can't ignite fire on fire (default: true) -mcl_fire.set_fire = function(pointed_thing, player, allow_on_fire) - local pname - if player == nil then - pname = "" - else - pname = player:get_player_name() - end - local n = minetest.get_node(pointed_thing.above) - local nu = minetest.get_node(pointed_thing.under) - if allow_on_fire == false and minetest.get_item_group(nu.name, "fire") ~= 0 then - return - end - if minetest.is_protected(pointed_thing.above, pname) then - minetest.record_protection_violation(pointed_thing.above, pname) - return - end - if n.name == "air" then - minetest.add_node(pointed_thing.above, {name="mcl_fire:fire"}) - end + -- Remove flammable nodes around basic flame + minetest.register_abm({ + label = "Remove flammable nodes", + nodenames = {"mc_fire:fire"}, + neighbors = "group:flammable", + interval = 5, + chance = 18, + catch_up = false, + action = function(pos) + local p = minetest.find_node_near(pos, 1, {"group:flammable"}) + if not p then + return + end + local flammable_node = minetest.get_node(p) + local def = minetest.registered_nodes[flammable_node.name] + if def.on_burn then + def.on_burn(p) + else + minetest.remove_node(p) + minetest.check_for_falling(p) + end + end + }) end minetest.register_lbm({ From df4ea94dbcc92bcef4cf34dd2d80b997b39a0b41 Mon Sep 17 00:00:00 2001 From: cora Date: Fri, 14 Jan 2022 23:01:12 +0100 Subject: [PATCH 02/13] Add node timer back in so fires burn out --- mods/ITEMS/mcl_fire/init.lua | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index b29e60ef..0e02a2ab 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -85,10 +85,6 @@ local fire_death_messages = { N("@1 died in a fire."), } -local fire_timer = function(pos) - minetest.get_node_timer(pos):start(math.random(3, 7)) -end - local spawn_fire = function(pos, age) minetest.set_node(pos, {name="mcl_fire:fire", param2 = age}) minetest.check_single_for_falling({x=pos.x, y=pos.y+1, z=pos.z}) @@ -126,6 +122,14 @@ minetest.register_node("mcl_fire:fire", { end, drop = "", sounds = {}, + on_timer= function(pos) + if not minetest.find_node_near(pos, 1, {"group:flammable"}) then + minetest.remove_node(pos) + return + end + -- Restart timer + return true + end, -- Turn into eternal fire on special blocks, light Nether portal (if possible), start burning timer on_construct = function(pos) local bpos = {x=pos.x, y=pos.y-1, z=pos.z} @@ -139,8 +143,7 @@ minetest.register_node("mcl_fire:fire", { if minetest.get_modpath("mcl_portals") then mcl_portals.light_nether_portal(pos) end - - fire_timer(pos) + minetest.get_node_timer(pos):start(math.random(30, 60)) spawn_smoke(pos) end, on_destruct = function(pos) From 4deca628dcd89456c742e416f3b20f8c79ddeb01 Mon Sep 17 00:00:00 2001 From: cora Date: Fri, 14 Jan 2022 23:50:36 +0100 Subject: [PATCH 03/13] Make fire actually remove nodes --- mods/ITEMS/mcl_fire/init.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 0e02a2ab..c8c9669e 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -90,6 +90,10 @@ local spawn_fire = function(pos, age) minetest.check_single_for_falling({x=pos.x, y=pos.y+1, z=pos.z}) end +local function fire_timer(pos) + return minetest.get_node_timer(pos):start(math.random(30, 60)) +end + minetest.register_node("mcl_fire:fire", { description = S("Fire"), _doc_items_longdesc = fire_help, @@ -143,8 +147,8 @@ minetest.register_node("mcl_fire:fire", { if minetest.get_modpath("mcl_portals") then mcl_portals.light_nether_portal(pos) end - minetest.get_node_timer(pos):start(math.random(30, 60)) spawn_smoke(pos) + fire_timer(pos) end, on_destruct = function(pos) mcl_particles.delete_node_particlespawners(pos) @@ -457,8 +461,8 @@ else -- Fire enabled -- Remove flammable nodes around basic flame minetest.register_abm({ label = "Remove flammable nodes", - nodenames = {"mc_fire:fire"}, - neighbors = "group:flammable", + nodenames = {"mcl_fire:fire","mcl_fire:eternal_fire"}, + neighbors = {"group:flammable"}, interval = 5, chance = 18, catch_up = false, @@ -472,7 +476,8 @@ else -- Fire enabled if def.on_burn then def.on_burn(p) else - minetest.remove_node(p) + minetest.swap_node(p, {name = "mcl_fire:fire"}) + fire_timer(p) minetest.check_for_falling(p) end end From c12076e74d52729ad74f240e1f1f077ce07d4b68 Mon Sep 17 00:00:00 2001 From: cora Date: Sat, 15 Jan 2022 03:31:15 +0100 Subject: [PATCH 04/13] Reimplement basic Minecraft-like fire spread --- mods/ITEMS/mcl_fire/init.lua | 130 +++++++++++------------------------ 1 file changed, 39 insertions(+), 91 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index c8c9669e..95056488 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -5,29 +5,6 @@ mcl_fire = {} local S = minetest.get_translator("mcl_fire") local N = function(s) return s end --- inverse pyramid pattern above lava source, floor 1 of 2: -local lava_fire= -{ - { x =-1, y = 1, z =-1}, - { x =-1, y = 1, z = 0}, - { x =-1, y = 1, z = 1}, - { x = 0, y = 1, z =-1}, - { x = 0, y = 1, z = 0}, - { x = 0, y = 1, z = 1}, - { x = 1, y = 1, z =-1}, - { x = 1, y = 1, z = 0}, - { x = 1, y = 1, z = 1} -} -local alldirs= -{ - { 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} -} - local spawn_smoke = function(pos) mcl_particles.add_node_particlespawner(pos, { amount = 0.1, @@ -359,16 +336,28 @@ minetest.register_abm({ -- Enable the following ABMs according to 'enable fire' setting +-- [...]a fire that is not adjacent to any flammable block does not spread, even to another flammable block within the normal range. +-- https://minecraft.fandom.com/wiki/Fire#Spread + local function has_flammable(pos) - local npos, node - for n, v in ipairs(alldirs) do - npos = vector.add(pos, v) - node = minetest.get_node_or_nil(npos) - if node and node.name and minetest.get_item_group(node.name, "flammable") ~= 0 then - return npos - end + return minetest.find_node_near(pos, 1, {"group:flammable"}) +end + +local function check_aircube(p1,p2) + local nds=minetest.find_nodes_in_area(p1,p2,{"air"}) + for k,v in pairs(nds) do + if has_flammable(v) then return v end end - return false +end + + +-- [...] a fire block can turn any air block that is adjacent to a flammable block into a fire block. This can happen at a distance of up to one block downward, one block sideways (including diagonals), and four blocks upward of the original fire block (not the block the fire is on/next to). +local function get_ignitable(pos) + return check_aircube(vector.add(pos,vector.new(-1,-1,-1)),vector.add(pos,vector.new(1,4,1))) +end +-- Fire spreads from a still lava block similarly: any air block one above and up to one block sideways (including diagonals) or two above and two blocks sideways (including diagonals) that is adjacent to a flammable block may be turned into a fire block. +local function get_ignitable_by_lava(pos) + return check_aircube(vector.add(pos,vector.new(-1,1,-1)),vector.add(pos,vector.new(1,1,1))) or check_aircube(vector.add(pos,vector.new(-2,2,-2)),vector.add(pos,vector.new(2,2,2))) or nil end if not fire_enabled then @@ -386,78 +375,37 @@ if not fire_enabled then else -- Fire enabled - -- Set fire to air nodes - minetest.register_abm({ - label = "Ignite fire by lava", - nodenames = {"group:lava"}, - neighbors = {"air"}, - interval = 7, - chance = 3, - catch_up = false, - action = function(pos) - local i, dir, target, node, i2, f - i = math.random(1,9) - dir = lava_fire[i] - target = {x=pos.x+dir.x, y=pos.y+dir.y, z=pos.z+dir.z} - node = minetest.get_node(target) - if not node or node.name ~= "air" then - i = ((i + math.random(0,7)) % 9) + 1 - dir = lava_fire[i] - target = {x=pos.x+dir.x, y=pos.y+dir.y, z=pos.z+dir.z} - node = minetest.get_node(target) - if not node or node.name ~= "air" then - return - end - end - i2 = math.random(1,15) - if i2 < 10 then - local dir2, target2, node2 - dir2 = lava_fire[i2] - target2 = {x=target.x+dir2.x, y=target.y+dir2.y, z=target.z+dir2.z} - node2 = minetest.get_node(target2) - if node2 and node2.name == "air" then - f = has_flammable(target2) - if f then - minetest.after(1, spawn_fire, {x=target2.x, y=target2.y, z=target2.z}) - minetest.add_particle({ - pos = vector.new({x=pos.x, y=pos.y+0.5, z=pos.z}), - velocity={x=f.x-pos.x, y=math.max(f.y-pos.y,0.7), z=f.z-pos.z}, - expirationtime=1, size=1.5, collisiondetection=false, - glow=minetest.LIGHT_MAX, texture="mcl_particles_flame.png" - }) - return - end - end - end - f = has_flammable(target) - if f then - minetest.after(1, spawn_fire, {x=target.x, y=target.y, z=target.z}) - minetest.add_particle({ - pos = vector.new({x=pos.x, y=pos.y+0.5, z=pos.z}), - velocity={x=f.x-pos.x, y=math.max(f.y-pos.y,0.25), z=f.z-pos.z}, - expirationtime=1, size=1, collisiondetection=false, - glow=minetest.LIGHT_MAX, texture="mcl_particles_flame.png" - }) - end - end, - }) - - -- Ignite neighboring nodes, add basic flames + -- Fire Spread minetest.register_abm({ label = "Ignite flame", - nodenames = {"group:flammable"}, - neighbors = {"mcl_fire:fire","mcl_fire:eternal_fire","mcl_core:lava_source","mcl_core:lava_flowing","mcl_nether:nether_lava_source","mcl_nether:nether_lava_flowing",}, + nodenames ={"mcl_fire:fire","mcl_fire:eternal_fire"}, interval = 7, chance = 12, catch_up = false, action = function(pos) - local p = minetest.find_node_near(pos, 1, {"air"}) + local p = get_ignitable(pos) if p then minetest.set_node(p, {name = "mcl_fire:fire"}) end end }) + --lava fire spread + minetest.register_abm({ + label = "Ignite fire by lava", + nodenames = {"mcl_core:lava_source","mcl_nether:nether_lava_source"}, + neighbors = {"air","group:flammable"}, + interval = 7, + chance = 3, + catch_up = false, + action = function(pos) + local p=get_ignitable_by_lava(pos) + if p then + minetest.set_node(p, {name = "mcl_fire:fire"}) + end + end, + }) + -- Remove flammable nodes around basic flame minetest.register_abm({ label = "Remove flammable nodes", From 02fa2c9e07e7eca2e6e974e9cfa1ad8934affaad Mon Sep 17 00:00:00 2001 From: cora Date: Sat, 15 Jan 2022 23:06:00 +0100 Subject: [PATCH 05/13] Fix nodes that should not burn up burning up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nodes with the group “flammable = -1” (e.g. crafting tables) must be able to catch fire, but must not burn up. This patch adds checks for this group. --- mods/ITEMS/mcl_fire/init.lua | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 95056488..b10ddfb4 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -27,6 +27,10 @@ local spawn_smoke = function(pos) }, "high") end +local function has_flammable(pos) + return minetest.find_node_near(pos, 1, {"group:flammable"}) +end + -- -- Items -- @@ -104,7 +108,8 @@ minetest.register_node("mcl_fire:fire", { drop = "", sounds = {}, on_timer= function(pos) - if not minetest.find_node_near(pos, 1, {"group:flammable"}) then + local p=has_flammable(pos) + if not p or minetest.get_item_group(minetest.get_node(p).name, "flammable") == -1 then minetest.remove_node(pos) return end @@ -339,10 +344,6 @@ minetest.register_abm({ -- [...]a fire that is not adjacent to any flammable block does not spread, even to another flammable block within the normal range. -- https://minecraft.fandom.com/wiki/Fire#Spread -local function has_flammable(pos) - return minetest.find_node_near(pos, 1, {"group:flammable"}) -end - local function check_aircube(p1,p2) local nds=minetest.find_nodes_in_area(p1,p2,{"air"}) for k,v in pairs(nds) do @@ -415,15 +416,18 @@ else -- Fire enabled chance = 18, catch_up = false, action = function(pos) - local p = minetest.find_node_near(pos, 1, {"group:flammable"}) + local p = has_flammable(pos) if not p then return end - local flammable_node = minetest.get_node(p) - local def = minetest.registered_nodes[flammable_node.name] - if def.on_burn then - def.on_burn(p) - else + + local nn = minetest.get_node(p).name + local def = minetest.registered_nodes[nn] + local fgroup = minetest.get_item_group(nn, "flammable") + + if def and def._on_burn then + def._on_burn(p) + elseif fgroup ~= -1 then minetest.swap_node(p, {name = "mcl_fire:fire"}) fire_timer(p) minetest.check_for_falling(p) From 1b89c15193f4458813b7c39a6b02c1b94d2d294e Mon Sep 17 00:00:00 2001 From: cora Date: Sun, 16 Jan 2022 12:26:50 +0100 Subject: [PATCH 06/13] Fix double nodes not being properly removed --- mods/ITEMS/mcl_fire/init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index b10ddfb4..99a636f3 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -123,7 +123,7 @@ minetest.register_node("mcl_fire:fire", { local dim = mcl_worlds.pos_to_dimension(bpos) if under == "mcl_nether:magma" or under == "mcl_nether:netherrack" or (under == "mcl_core:bedrock" and dim == "end") then - minetest.swap_node(pos, {name = "mcl_fire:eternal_fire"}) + spawn_fire(pos) end if minetest.get_modpath("mcl_portals") then @@ -386,7 +386,7 @@ else -- Fire enabled action = function(pos) local p = get_ignitable(pos) if p then - minetest.set_node(p, {name = "mcl_fire:fire"}) + spawn_fire(p) end end }) @@ -402,7 +402,7 @@ else -- Fire enabled action = function(pos) local p=get_ignitable_by_lava(pos) if p then - minetest.set_node(p, {name = "mcl_fire:fire"}) + spawn_fire(p) end end, }) @@ -428,7 +428,7 @@ else -- Fire enabled if def and def._on_burn then def._on_burn(p) elseif fgroup ~= -1 then - minetest.swap_node(p, {name = "mcl_fire:fire"}) + spawn_fire(p) fire_timer(p) minetest.check_for_falling(p) end From db8fbdc5dde6bc79330578e7b82e3734c3255fdb Mon Sep 17 00:00:00 2001 From: cora Date: Sun, 16 Jan 2022 13:04:52 +0100 Subject: [PATCH 07/13] Fix beds burning away and dropping --- mods/ITEMS/mcl_beds/api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/ITEMS/mcl_beds/api.lua b/mods/ITEMS/mcl_beds/api.lua index c274a29a..f72b20be 100644 --- a/mods/ITEMS/mcl_beds/api.lua +++ b/mods/ITEMS/mcl_beds/api.lua @@ -81,7 +81,7 @@ function mcl_beds.register_bed(name, def) paramtype2 = "facedir", is_ground_content = false, stack_max = 1, - groups = {handy=1, flammable = 3, bed = 1, dig_by_piston=1, bouncy=66, fall_damage_add_percent=-50, deco_block = 1, flammable=-1}, + groups = {handy=1, bed = 1, dig_by_piston=1, bouncy=66, fall_damage_add_percent=-50, deco_block = 1, flammable=-1}, _mcl_hardness = 0.2, _mcl_blast_resistance = 1, sounds = def.sounds or default_sounds, @@ -204,7 +204,7 @@ function mcl_beds.register_bed(name, def) paramtype2 = "facedir", is_ground_content = false, -- FIXME: Should be bouncy=66, but this would be a higher bounciness than slime blocks! - groups = {handy = 1, flammable = 3, bed = 2, dig_by_piston=1, bouncy=33, fall_damage_add_percent=-50, not_in_creative_inventory = 1}, + groups = {handy = 1, flammable = -1, bed = 2, dig_by_piston=1, bouncy=33, fall_damage_add_percent=-50, not_in_creative_inventory = 1}, _mcl_hardness = 0.2, _mcl_blast_resistance = 1, sounds = def.sounds or default_sounds, From 052c9fcbcf68d6b96665213c1b9a19eff4a4f764 Mon Sep 17 00:00:00 2001 From: cora Date: Sun, 23 Jan 2022 03:17:51 +0100 Subject: [PATCH 08/13] Fix diagonal fire-spread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Minecraft fire spread logic, “adjacent” does not mean “diagonal”. --- mods/ITEMS/mcl_fire/init.lua | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 99a636f3..8ef03fc7 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -27,8 +27,30 @@ local spawn_smoke = function(pos) }, "high") end +local adjacents = { + { 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 }, +} + +local function shuffle_adjacents() + for i = #adjacents, 1, -1 do + local r = math.random(i) + adjacents[i], adjacents[r] = adjacents[r], adjacents[i] + end +end + local function has_flammable(pos) - return minetest.find_node_near(pos, 1, {"group:flammable"}) + for k,v in pairs(adjacents) do + local p=vector.add(pos,v) + local n=minetest.get_node_or_nil(p) + if n and minetest.get_item_group(n.name, "flammable") > 0 then + return p + end + end end -- @@ -387,6 +409,7 @@ else -- Fire enabled local p = get_ignitable(pos) if p then spawn_fire(p) + shuffle_adjacents() end end }) From 6c2fb98160d5ac8292d806649d3a4219a469c5da Mon Sep 17 00:00:00 2001 From: cora Date: Sun, 23 Jan 2022 11:19:55 +0100 Subject: [PATCH 09/13] =?UTF-8?q?Fix=20nodes=20with=20=E2=80=9Cflammable?= =?UTF-8?q?=20=3D=20-1=E2=80=9D=20not=20catching=20fire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mods/ITEMS/mcl_fire/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 8ef03fc7..05091554 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -47,7 +47,7 @@ local function has_flammable(pos) for k,v in pairs(adjacents) do local p=vector.add(pos,v) local n=minetest.get_node_or_nil(p) - if n and minetest.get_item_group(n.name, "flammable") > 0 then + if n and minetest.get_item_group(n.name, "flammable") ~= 0 then return p end end From ef7370550f69e8f3834604aa02dddf63653c7f6c Mon Sep 17 00:00:00 2001 From: cora Date: Tue, 8 Feb 2022 01:45:54 +0100 Subject: [PATCH 10/13] Make fire spread direction truly random This patch initializes the random number generator used in mcl_fire with the current Unix timestamp. It also corrects two biases in fire spread that were caused by nodes being iterated over in a predictable way. --- mods/ITEMS/mcl_fire/init.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 05091554..8e2383da 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -36,12 +36,14 @@ local adjacents = { { x = 0, y = 0, z = 1 }, } -local function shuffle_adjacents() - for i = #adjacents, 1, -1 do +math.randomseed(os.time()) +local function shuffle_table(t) + for i = #t, 1, -1 do local r = math.random(i) - adjacents[i], adjacents[r] = adjacents[r], adjacents[i] + t[i], t[r] = t[r], t[i] end end +shuffle_table(adjacents) local function has_flammable(pos) for k,v in pairs(adjacents) do @@ -368,6 +370,7 @@ minetest.register_abm({ local function check_aircube(p1,p2) local nds=minetest.find_nodes_in_area(p1,p2,{"air"}) + shuffle_table(nds) for k,v in pairs(nds) do if has_flammable(v) then return v end end @@ -409,7 +412,7 @@ else -- Fire enabled local p = get_ignitable(pos) if p then spawn_fire(p) - shuffle_adjacents() + shuffle_table(adjacents) end end }) From 5d09ec311c314b195d23c24f599328d26727512a Mon Sep 17 00:00:00 2001 From: cora Date: Thu, 10 Feb 2022 15:05:35 +0100 Subject: [PATCH 11/13] Extinguish fire using an ABM instead of timers Even the mitigated timers seem to have lead to slow memory leaks. Once Minetest has used up all the RAM, it will free some, then quickly use memory up again, then repeat it ad nauseum, requiring 100% CPU. On a PC with 2GB of RAM this could be reliably triggered by having a fire burn a forest for 20 to 30 minutes. This patch removes fire node timers completely and instead extinguishes fire using an ABM. --- mods/ITEMS/mcl_fire/init.lua | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 8e2383da..3926513e 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -95,10 +95,6 @@ local spawn_fire = function(pos, age) minetest.check_single_for_falling({x=pos.x, y=pos.y+1, z=pos.z}) end -local function fire_timer(pos) - return minetest.get_node_timer(pos):start(math.random(30, 60)) -end - minetest.register_node("mcl_fire:fire", { description = S("Fire"), _doc_items_longdesc = fire_help, @@ -131,15 +127,6 @@ minetest.register_node("mcl_fire:fire", { end, drop = "", sounds = {}, - on_timer= function(pos) - local p=has_flammable(pos) - if not p or minetest.get_item_group(minetest.get_node(p).name, "flammable") == -1 then - minetest.remove_node(pos) - return - end - -- Restart timer - return true - end, -- Turn into eternal fire on special blocks, light Nether portal (if possible), start burning timer on_construct = function(pos) local bpos = {x=pos.x, y=pos.y-1, z=pos.z} @@ -154,7 +141,6 @@ minetest.register_node("mcl_fire:fire", { mcl_portals.light_nether_portal(pos) end spawn_smoke(pos) - fire_timer(pos) end, on_destruct = function(pos) mcl_particles.delete_node_particlespawners(pos) @@ -433,6 +419,25 @@ else -- Fire enabled end, }) + minetest.register_abm({ + label = "Remove fires", + nodenames = {"mcl_fire:fire"}, + interval = 12, + chance = 4, + catch_up = false, + action = function(pos) + local p=has_flammable(pos) + if p then + local n=minetest.get_node_or_nil(p) + if n and minetest.get_item_group(n.name, "flammable") < 1 then + minetest.remove_node(pos) + end + else + minetest.remove_node(pos) + end + end, + }) + -- Remove flammable nodes around basic flame minetest.register_abm({ label = "Remove flammable nodes", @@ -455,7 +460,6 @@ else -- Fire enabled def._on_burn(p) elseif fgroup ~= -1 then spawn_fire(p) - fire_timer(p) minetest.check_for_falling(p) end end From 464771094591ee27e078d6800f6179d0d1aa8db2 Mon Sep 17 00:00:00 2001 From: cora Date: Thu, 10 Feb 2022 17:52:11 +0100 Subject: [PATCH 12/13] =?UTF-8?q?Extinguish=20fire=20without=20nearby=20?= =?UTF-8?q?=E2=80=9Cfuel=E2=80=9D=20faster?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adjusts how often the ABM runs that removes fire nodes with no adjacent nodes that can burn up. This makes fire linger less in air. --- mods/ITEMS/mcl_fire/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 3926513e..8b606561 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -422,8 +422,8 @@ else -- Fire enabled minetest.register_abm({ label = "Remove fires", nodenames = {"mcl_fire:fire"}, - interval = 12, - chance = 4, + interval = 7, + chance = 3, catch_up = false, action = function(pos) local p=has_flammable(pos) From f5ba6f564925ebf5ce0de3c8c905ef24a8123ab5 Mon Sep 17 00:00:00 2001 From: cora Date: Fri, 11 Feb 2022 13:27:07 +0100 Subject: [PATCH 13/13] Fix memory leak & C stack overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this patch, when placing a fire above a node that would turn it into eternal fire (e.g. Netherrack or Magma) the spawn_fire() function would call itself infinitely via the on_construct() handler of eternal fire – because the latter called spawn_fire() itself. On an x86 machine, this caused a memory leak, hanging Minetest. On an x86_64 machine though, Minetest crashed immediately, showing an error message about a stack overflow. --- mods/ITEMS/mcl_fire/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 8b606561..2eed0843 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -134,7 +134,7 @@ minetest.register_node("mcl_fire:fire", { local dim = mcl_worlds.pos_to_dimension(bpos) if under == "mcl_nether:magma" or under == "mcl_nether:netherrack" or (under == "mcl_core:bedrock" and dim == "end") then - spawn_fire(pos) + minetest.set_node(pos, {name="mcl_fire:eternal_fire"}) end if minetest.get_modpath("mcl_portals") then