Compare commits

...

1 Commits

1 changed files with 126 additions and 117 deletions

View File

@ -146,60 +146,87 @@ local function good_for_respawn(pos, player)
local def0 = minetest.registered_nodes[nn0] local def0 = minetest.registered_nodes[nn0]
local def1 = minetest.registered_nodes[nn1] local def1 = minetest.registered_nodes[nn1]
local def2 = minetest.registered_nodes[nn2] local def2 = minetest.registered_nodes[nn2]
-- Be safe around undefined nodes
if not def0 then return false end
if not def1 then return false end
if not def2 then return false end
return def0.walkable and (not def1.walkable) and (not def2.walkable) and return def0.walkable and (not def1.walkable) and (not def2.walkable) and
(def1.damage_per_second == nil or def2.damage_per_second <= 0) and (def1.damage_per_second == nil or def2.damage_per_second <= 0) and
(def1.damage_per_second == nil or def2.damage_per_second <= 0) (def1.damage_per_second == nil or def2.damage_per_second <= 0)
end end
local function can_find_tree(pos1, trees)
if not emerge_pos1 or not emerge_pos2 then return false end
local trees = trees or get_trees(pos1)
if not trees then return false end
if (attempts_to_find_trees * 3 < #trees) then local ADJACENT_OFFSETS = {
-- random search vector.new( 1,0, 0),
for i = 1, attempts_to_find_trees do vector.new(-1,0, 0),
local pos2 = trees[math.random(1,#trees)] vector.new( 0,0, 1),
if not minetest.is_protected(pos2, "") then vector.new( 0,0,-1),
if pos2.x < pos1.x then vector.new( 0,1, 0),
pos2.x = pos2.x + 1 }
elseif pos2.x > pos1.x then
pos2.x = pos2.x - 1 local function pathfind_next_to(target, pos1, distance)
end local closest_free_pos = nil
if pos2.z < pos1.z then local closest_free_dist = nil
pos2.z = pos2.z + 1 for j=1,4 do
elseif pos2.z > pos1.z then local test_pos = vector.add(target, ADJACENT_OFFSETS[j])
pos2.z = pos2.z - 1 if minetest.get_node(test_pos).name == "air" and minetest.get_node(vector.offset(test_pos,0,-1,0)).name ~= "air" then
end local new_dist = vector.distance(pos1, test_pos)
local way = minetest.find_path(pos1, pos2, res, 1, 3, "A*_noprefetch") if not closest_free_pos or new_dist < closest_free_dist then
if way then closest_free_pos = test_pos
return true closest_free_dist = new_dist
end
end end
end end
end
local way = false
if closest_free_pos then
return minetest.find_path(pos1, closest_free_pos, distance, 1, 3, "A*_noprefetch")
end
end
local function can_find_tree(pos1, trees)
local start_time = minetest.get_us_time()
if not emerge_pos1 or not emerge_pos2 then
minetest.log("verbose", "[mcl_spawn] Missing emerge position in can_find_tree()")
return false
end
local trees = trees or get_trees(pos1)
if not trees then
minetest.log("verbose", "[mcl_spawn] No trees in area in can_find_tree()")
return false return false
end end
for i, pos2 in ipairs(trees) do -- Search from closest to furthest from pos1
-- full search for i = 2,#trees do
if not minetest.is_protected(pos2, "") then for j = 1,(i-1) do
if pos2.x < pos1.x then if vector.distance(pos1,trees[j]) > vector.distance(pos1,trees[j+1]) then
pos2.x = pos2.x + 1 local tmp = trees[j+1]
elseif pos2.x > pos1.x then trees[j] = trees[j+1]
pos2.x = pos2.x - 1 trees[j+1] = tmp
end
if pos2.z < pos1.z then
pos2.z = pos2.z + 1
elseif pos2.z > pos1.z then
pos2.z = pos2.z - 1
end
local way = minetest.find_path(pos1, pos2, res, 1, 3, "A*_noprefetch")
if way then
return true
end end
end end
if i > attempts_to_find_trees then return false end
end end
for i = 1,#trees do
local possible_tree = trees[i]
local start_pathfind = minetest.get_us_time()
local way = pathfind_next_to(possible_tree, pos1, res)
local stop_pathfind = minetest.get_us_time()
if stop_pathfind > start_time + 0.25e6 then
minetest.log("info","[mcl_spawn] can_find_tree() timed out")
return false;
end
if way then
return true
end
end
return false return false
end end
@ -285,86 +312,52 @@ local function next_biome()
return false return false
end end
local function find_spawn_in_area(pos1, pos2)
local nodes = minetest.find_nodes_in_area(pos1, pos2, {"group:tree"})
if #nodes <= 0 then return nil end
for i = 1,#nodes do
local pos = nodes[i]
local candidates = minetest.find_nodes_in_area_under_air(
vector.offset(pos,-3,-2,-3),
vector.offset(pos, 3, 2, 3),
{"group:solid"}
)
for j = 1,#candidates do
local candidate = vector.offset(candidates[j],0,1,0)
local gfs = good_for_respawn(candidate)
local way = false
if vector.distance(candidate, pos) <= 1 then
way = true
else
local way = pathfind_next_to(candidate, pos, 5)
end
if gfs and way then
return candidate
end
end
end
return nil
end
local function ecb_search_continue(blockpos, action, calls_remaining, param) local function ecb_search_continue(blockpos, action, calls_remaining, param)
if calls_remaining <= 0 then if calls_remaining <= 0 then
emerge_pos1 = {x = wsp.x-half_res, y = alt_min, z = wsp.z-half_res} emerge_pos1 = {x = wsp.x-half_res, y = alt_min, z = wsp.z-half_res}
emerge_pos2 = {x = wsp.x+half_res, y = alt_max, z = wsp.z+half_res} emerge_pos2 = {x = wsp.x+half_res, y = alt_max, z = wsp.z+half_res}
local nodes = minetest.find_nodes_in_area_under_air(emerge_pos1, emerge_pos2, node_groups_white_list) local start_time = minetest.get_us_time()
minetest.log("verbose", "[mcl_spawn] Data emerge callback: "..minetest.pos_to_string(wsp).." - "..tostring(nodes and #nodes) .. " node(s) found under air")
if nodes then local spawn_pos = find_spawn_in_area(emerge_pos1, emerge_pos2)
if no_trees_area_counter >= 0 then if spawn_pos then
local trees = get_trees(emerge_pos1, emerge_pos2) wsp = spawn_pos
if trees and #trees > 0 then minetest.log("action", "[mcl_spawn] Dynamic world spawn randomly determined to be "..minetest.pos_to_string(wsp))
no_trees_area_counter = 0 searched = true
if attempts_to_find_pos * 3 < #nodes then success = true
-- random return
for i=1, attempts_to_find_pos do
wsp = nodes[math.random(1,#nodes)]
if wsp then
wsp.y = wsp.y + 1
if good_for_respawn(wsp) and can_find_tree(wsp, trees) then
minetest.log("action", "[mcl_spawn] Dynamic world spawn randomly determined to be "..minetest.pos_to_string(wsp))
searched = true
success = true
return
end
end
end
else
-- in a sequence
for i=1, math.min(#nodes, attempts_to_find_pos) do
wsp = nodes[i]
if wsp then
wsp.y = wsp.y + 1
if good_for_respawn(wsp) and can_find_tree(wsp, trees) then
minetest.log("action", "[mcl_spawn] Dynamic world spawn determined to be "..minetest.pos_to_string(wsp))
searched = true
success = true
return
end
end
end
end
else
no_trees_area_counter = no_trees_area_counter + 1
if no_trees_area_counter > 10 then
minetest.log("verbose", "[mcl_spawn] More than 10 times no trees at all! Won't search trees next 200 calls")
no_trees_area_counter = -200
end
end
else -- seems there are no trees but we'll check it later, after next 200 calls
no_trees_area_counter = no_trees_area_counter + 1
if attempts_to_find_pos * 3 < #nodes then
-- random
for i=1, attempts_to_find_pos do
wsp = nodes[math.random(1,#nodes)]
if wsp then
wsp.y = wsp.y + 1
if good_for_respawn(wsp) then
minetest.log("action", "[mcl_spawn] Dynamic world spawn randomly determined to be "..minetest.pos_to_string(wsp) .. " (no trees)")
searched = true
success = true
return
end
end
end
else
-- in a sequence
for i=1, math.min(#nodes, attempts_to_find_pos) do
wsp = nodes[i]
if wsp then
wsp.y = wsp.y + 1
if good_for_respawn(wsp) then
minetest.log("action", "[mcl_spawn] Dynamic world spawn determined to be "..minetest.pos_to_string(wsp) .. " (no trees)")
searched = true
success = true
return
end
end
end
end
end
end end
next_pos() next_pos()
mcl_spawn.search() mcl_spawn.search()
end end
@ -565,8 +558,24 @@ function mcl_spawn.shadow_worker()
if success then if success then
local wsp_node = minetest.get_node(wsp) local wsp_node = minetest.get_node(wsp)
if not (wsp_node and wsp_node.name == "ignore") local spawn_bad = false
and ((not good_for_respawn(wsp)) or ((no_trees_area_counter >= 0) and not can_find_tree(wsp))) then
if not spawn_bad and not wsp_node or wsp_node.name == "ignore" then
spawn_bad = true
minetest.log("verbose", "[mcl_spawn] World spawn point could not be checked or is 'ignore' wsp_node="..dump(wsp_node))
end
if not spawn_bad and not good_for_respawn(wsp) then
spawn_bad = true
minetest.log("verbose", "[mcl_spawn] World spawn point is not good for respawn")
end
if not spawn_bad and ( (no_trees_area_counter >= 0) and not can_find_tree(wsp) ) then
spawn_bad = true
minetest.log("verbose", "[mcl_spawn] No trees near spawn point")
end
if spawn_bad then
success = false success = false
minetest.log("action", "[mcl_spawn] World spawn position isn't safe anymore: "..minetest.pos_to_string(wsp)) minetest.log("action", "[mcl_spawn] World spawn position isn't safe anymore: "..minetest.pos_to_string(wsp))
mcl_spawn.search() mcl_spawn.search()