mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-11-14 23:21:07 +01:00
Initial changes to spawn_check() and spawn_a_mob(), add profiling for mob spawning
This commit is contained in:
parent
77382d930e
commit
52f8814876
1 changed files with 191 additions and 156 deletions
|
@ -750,86 +750,111 @@ local function get_biome_name(pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local counts = {}
|
||||||
|
|
||||||
local function spawn_check(pos, spawn_def)
|
local function spawn_check(pos, spawn_def)
|
||||||
if not spawn_def or not pos then return end
|
local function log_fail(reason)
|
||||||
|
local count = (counts[reason] or 0) + 1
|
||||||
|
counts[reason] = count
|
||||||
|
mcl_log("Spawn check failed - "..reason.." ("..count..")")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if not spawn_def or not pos then return log_fail("missing pos or spawn_def") end
|
||||||
|
|
||||||
|
local gotten_node = get_node(pos).name
|
||||||
|
if not gotten_node then return log_fail("unable to get node") end
|
||||||
|
|
||||||
dbg_spawn_attempts = dbg_spawn_attempts + 1
|
dbg_spawn_attempts = dbg_spawn_attempts + 1
|
||||||
local dimension = mcl_worlds.pos_to_dimension(pos)
|
|
||||||
local mob_def = minetest.registered_entities[spawn_def.name]
|
|
||||||
local mob_type = mob_def.type
|
|
||||||
local gotten_node = get_node(pos).name
|
|
||||||
if not gotten_node then return end
|
|
||||||
|
|
||||||
|
-- Make sure the mob can spawn at this location
|
||||||
|
if pos.y < spawn_def.min_height or pos.y > spawn_def.max_height then return log_fail("incorrect height") end
|
||||||
|
|
||||||
|
-- Make the dimention is correct
|
||||||
|
local dimension = mcl_worlds.pos_to_dimension(pos)
|
||||||
|
if spawn_def.dimension ~= dimension then return log_fail("incorrect dimension") end
|
||||||
|
|
||||||
|
-- Make sure the biome is correct
|
||||||
local biome_name = get_biome_name(pos)
|
local biome_name = get_biome_name(pos)
|
||||||
if not biome_name then return end
|
if not biome_name then return end
|
||||||
|
if not biome_check(spawn_def.biomes, biome_name) then return log_fail("incorrect biome") end
|
||||||
|
|
||||||
|
-- Never spawn directly on bedrock
|
||||||
|
if gotten_node == "mcl_core:bedrock" then return log_fail("tried to spawn on bedrock") end
|
||||||
|
|
||||||
|
-- Spawning prohibited in protected areas
|
||||||
|
if spawn_protected and minetest.is_protected(pos, "") then return log_fail("tried to spawn in protected area") end
|
||||||
|
|
||||||
|
-- Ground mobs must spawn on solid nodes that are not leafes
|
||||||
local is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
|
local is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
|
||||||
if not is_ground then
|
if not is_ground then
|
||||||
|
mcl_log("Node "..gotten_node.." not solid, trying one block")
|
||||||
pos.y = pos.y - 1
|
pos.y = pos.y - 1
|
||||||
gotten_node = get_node(pos).name
|
gotten_node = get_node(pos).name
|
||||||
is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
|
is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
|
||||||
end
|
end
|
||||||
pos.y = pos.y + 1
|
pos.y = pos.y + 1
|
||||||
local is_water = get_item_group(gotten_node, "water") ~= 0
|
if spawn_def.type_of_spawning == "ground" and (not is_ground or get_item_group(gotten_node, "leaves") ~= 0) then
|
||||||
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
return log_fail("not ground node")
|
||||||
local is_leaf = get_item_group(gotten_node, "leaves") ~= 0
|
end
|
||||||
local is_bedrock = gotten_node == "mcl_core:bedrock"
|
|
||||||
local is_grass = minetest.get_item_group(gotten_node,"grass_block") ~= 0
|
|
||||||
|
|
||||||
if pos.y >= spawn_def.min_height
|
-- Water mobs must spawn in water
|
||||||
and pos.y <= spawn_def.max_height
|
if spawn_def.type_of_spawning == "water" and get_item_group(gotten_node, "water") == 0 then return log_fail("not water node") end
|
||||||
and spawn_def.dimension == dimension
|
|
||||||
and biome_check(spawn_def.biomes, biome_name) then
|
|
||||||
|
|
||||||
mcl_log("Spawn level 1 check - Passed")
|
-- Farm animals must spawn on grass
|
||||||
if (is_ground or spawn_def.type_of_spawning ~= "ground")
|
if is_farm_animal(spawn_def.name) and get_item_group(gotten_node, "grass_block") == 0 then return log_fail("not grass block") end
|
||||||
and (spawn_def.type_of_spawning ~= "ground" or not is_leaf)
|
|
||||||
and (not is_farm_animal(spawn_def.name) or is_grass)
|
-- Spawns require enough room for the mob
|
||||||
and (spawn_def.type_of_spawning ~= "water" or is_water)
|
local mob_def = minetest.registered_entities[spawn_def.name]
|
||||||
and not is_bedrock
|
if not has_room(mob_def,pos) then return log_fail("mob doesn't fit here") end
|
||||||
and has_room(mob_def,pos)
|
|
||||||
and (spawn_def.check_position and spawn_def.check_position(pos) or spawn_def.check_position == nil)
|
-- Don't spawn if the spawn definition has a custom check and that fails
|
||||||
and ( not spawn_protected or not minetest.is_protected(pos, "") ) then
|
if spawn_def.check_position and not spawn_def.check_position(pos) then return log_fail("custom position check failed") end
|
||||||
|
|
||||||
mcl_log("Spawn level 2 check - Passed")
|
|
||||||
local gotten_light = get_node_light(pos)
|
local gotten_light = get_node_light(pos)
|
||||||
|
|
||||||
if modern_lighting then
|
-- Legacy lighting
|
||||||
|
if not modern_lighting then
|
||||||
|
if gotten_light < spawn_def.min_light or gotten_light > spawn_def.max_light then
|
||||||
|
return log_fail("incorrect light level")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Modern lighting
|
||||||
local my_node = get_node(pos)
|
local my_node = get_node(pos)
|
||||||
local sky_light = minetest.get_natural_light(pos)
|
local sky_light = minetest.get_natural_light(pos)
|
||||||
local art_light = minetest.get_artificial_light(my_node.param1)
|
local art_light = minetest.get_artificial_light(my_node.param1)
|
||||||
|
|
||||||
if mob_def.spawn_check then
|
if mob_def.spawn_check then
|
||||||
return mob_def.spawn_check(pos, gotten_light, art_light, sky_light)
|
if not mob_def.spawn_check(pos, gotten_light, art_light, sky_light) then
|
||||||
elseif mob_type == "monster" then
|
return log_fail("mob_def.spawn_check failed")
|
||||||
|
end
|
||||||
|
elseif mob_def.type == "monster" then
|
||||||
if dimension == "nether" then
|
if dimension == "nether" then
|
||||||
if art_light <= nether_threshold then
|
if art_light > nether_threshold then
|
||||||
return true
|
return log_fail("artificial light too high")
|
||||||
end
|
end
|
||||||
elseif dimension == "end" then
|
elseif dimension == "end" then
|
||||||
if art_light <= end_threshold then
|
if art_light > end_threshold then
|
||||||
return true
|
return log_fail("artificial light too high")
|
||||||
end
|
end
|
||||||
elseif dimension == "overworld" then
|
elseif dimension == "overworld" then
|
||||||
if art_light <= overworld_threshold and sky_light <= overworld_sky_threshold then
|
if art_light > overworld_threshold then
|
||||||
return true
|
return log_fail("artificial light too high")
|
||||||
|
end
|
||||||
|
if sky_light > overworld_sky_threshold then
|
||||||
|
return log_fail("sky light too high")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- passive threshold is apparently the same in all dimensions ...
|
-- passive threshold is apparently the same in all dimensions ...
|
||||||
if gotten_light > overworld_passive_threshold then
|
if gotten_light < overworld_passive_threshold then
|
||||||
|
return log_fail("light too low")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
|
||||||
else
|
|
||||||
if gotten_light >= spawn_def.min_light and gotten_light <= spawn_def.max_light then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.spawn(pos,id)
|
function mcl_mobs.spawn(pos,id)
|
||||||
local def = minetest.registered_entities[id] or minetest.registered_entities["mobs_mc:"..id] or minetest.registered_entities["extra_mobs:"..id]
|
local def = minetest.registered_entities[id] or minetest.registered_entities["mobs_mc:"..id] or minetest.registered_entities["extra_mobs:"..id]
|
||||||
|
@ -1018,6 +1043,7 @@ if mobs_spawn then
|
||||||
if not mob_library_worker_table then
|
if not mob_library_worker_table then
|
||||||
mob_library_worker_table = table_copy(spawn_dictionary)
|
mob_library_worker_table = table_copy(spawn_dictionary)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not cumulative_chance then
|
if not cumulative_chance then
|
||||||
cumulative_chance = 0
|
cumulative_chance = 0
|
||||||
for k, v in pairs(mob_library_worker_table) do
|
for k, v in pairs(mob_library_worker_table) do
|
||||||
|
@ -1026,8 +1052,27 @@ if mobs_spawn then
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function spawn_a_mob(pos, cap_space_hostile, cap_space_non_hostile)
|
local function select_random_mob_def()
|
||||||
|
local mob_chance_offset = math_random(1, 1e6) / 1e6 * cumulative_chance
|
||||||
|
|
||||||
|
minetest.log("action", "mob_chance_offset = "..tostring(mob_chance_offset).."/"..tostring(cumulative_chance))
|
||||||
|
|
||||||
|
for i = 1,#mob_library_worker_table do
|
||||||
|
local mob_def = mob_library_worker_table[i]
|
||||||
|
local mob_chance = mob_def.chance
|
||||||
|
if mob_chance_offset <= mob_chance then
|
||||||
|
minetest.log(mob_def.name.." "..mob_chance)
|
||||||
|
return mob_def
|
||||||
|
end
|
||||||
|
|
||||||
|
mob_chance_offset = mob_chance_offset - mob_chance
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(not "failed")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Spawns one mob or one group of mobs
|
||||||
|
local function spawn_a_mob(pos, cap_space_hostile, cap_space_non_hostile)
|
||||||
local spawning_position = find_spawning_position(pos, FIND_SPAWN_POS_RETRIES)
|
local spawning_position = find_spawning_position(pos, FIND_SPAWN_POS_RETRIES)
|
||||||
if not spawning_position then
|
if not spawning_position then
|
||||||
minetest.log("action", "[Mobs spawn] Cannot find a valid spawn position after retries: " .. FIND_SPAWN_POS_RETRIES)
|
minetest.log("action", "[Mobs spawn] Cannot find a valid spawn position after retries: " .. FIND_SPAWN_POS_RETRIES)
|
||||||
|
@ -1044,43 +1089,38 @@ if mobs_spawn then
|
||||||
--repeat grabbing a mob to maintain existing spawn rates
|
--repeat grabbing a mob to maintain existing spawn rates
|
||||||
local spawn_loop_counter = #mob_library_worker_table
|
local spawn_loop_counter = #mob_library_worker_table
|
||||||
|
|
||||||
while spawn_loop_counter > 0 do
|
local spawn_check_cache = {}
|
||||||
table.shuffle(mob_library_worker_table)
|
local function inner_loop()
|
||||||
local mob_chance_offset = math_random(1, cumulative_chance)
|
local mob_def = select_random_mob_def()
|
||||||
local mob_index = 1
|
|
||||||
local mob_chance = mob_library_worker_table[mob_index].chance
|
|
||||||
local step_chance = mob_chance
|
|
||||||
while step_chance < mob_chance_offset do
|
|
||||||
mob_index = mob_index + 1
|
|
||||||
if mob_index <= #mob_library_worker_table then
|
|
||||||
mob_chance = mob_library_worker_table[mob_index].chance
|
|
||||||
step_chance = step_chance + mob_chance
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--minetest.log(mob_def.name.." "..step_chance.. " "..mob_chance)
|
|
||||||
|
|
||||||
local mob_def = mob_library_worker_table[mob_index]
|
|
||||||
if mob_def and mob_def.name and minetest.registered_entities[mob_def.name] then
|
|
||||||
|
|
||||||
|
if not mob_def or not mob_def.name then return end
|
||||||
local mob_def_ent = minetest.registered_entities[mob_def.name]
|
local mob_def_ent = minetest.registered_entities[mob_def.name]
|
||||||
|
if not mob_def_ent then return end
|
||||||
|
|
||||||
|
-- Check capacity
|
||||||
local mob_spawn_class = mob_def_ent.spawn_class
|
local mob_spawn_class = mob_def_ent.spawn_class
|
||||||
|
|
||||||
local cap_space_available = mob_cap_space(spawning_position, mob_spawn_class, mob_counts_close, mob_counts_wide, cap_space_hostile, cap_space_non_hostile)
|
local cap_space_available = mob_cap_space(spawning_position, mob_spawn_class, mob_counts_close, mob_counts_wide, cap_space_hostile, cap_space_non_hostile)
|
||||||
|
if cap_space_available == 0 then
|
||||||
if cap_space_available > 0 then
|
mcl_log("Cap space full")
|
||||||
--mcl_log("Cap space available")
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Spawn caps for animals and water creatures fill up rapidly. Need to throttle this somewhat
|
-- Spawn caps for animals and water creatures fill up rapidly. Need to throttle this somewhat
|
||||||
-- for performance and for early game challenge. We don't want to reduce hostiles though.
|
-- for performance and for early game challenge. We don't want to reduce hostiles though.
|
||||||
local spawn_hostile = (mob_spawn_class == "hostile")
|
local spawn_hostile = (mob_spawn_class == "hostile")
|
||||||
local spawn_passive = (mob_spawn_class ~= "hostile") and math.random(100) < peaceful_percentage_spawned
|
local spawn_passive = (mob_spawn_class ~= "hostile") and math.random(100) < peaceful_percentage_spawned
|
||||||
|
|
||||||
--mcl_log("Spawn_passive: " .. tostring(spawn_passive))
|
--mcl_log("Spawn_passive: " .. tostring(spawn_passive))
|
||||||
--mcl_log("Spawn_hostile: " .. tostring(spawn_hostile))
|
--mcl_log("Spawn_hostile: " .. tostring(spawn_hostile))
|
||||||
|
|
||||||
if (spawn_hostile or spawn_passive) and spawn_check(spawning_position,mob_def) then
|
-- Make sure we would be spawning a mob
|
||||||
|
if not (spawn_hostile or spawn_passive) then return end
|
||||||
|
if not (spawn_check_cache[mob_def.name] or spawn_check(spawning_position, mob_def)) then
|
||||||
|
mcl_log("Spawn check failed")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
spawn_check_cache[mob_def.name] = true
|
||||||
|
|
||||||
|
-- Water mob special case
|
||||||
if mob_def.type_of_spawning == "water" then
|
if mob_def.type_of_spawning == "water" then
|
||||||
spawning_position = get_water_spawn(spawning_position)
|
spawning_position = get_water_spawn(spawning_position)
|
||||||
if not spawning_position then
|
if not spawning_position then
|
||||||
|
@ -1088,6 +1128,7 @@ if mobs_spawn then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if mob_def_ent.can_spawn and not mob_def_ent.can_spawn(spawning_position) then
|
if mob_def_ent.can_spawn and not mob_def_ent.can_spawn(spawning_position) then
|
||||||
minetest.log("warning","[mcl_mobs] mob "..mob_def.name.." refused to spawn at "..minetest.pos_to_string(vector.round(spawning_position)))
|
minetest.log("warning","[mcl_mobs] mob "..mob_def.name.." refused to spawn at "..minetest.pos_to_string(vector.round(spawning_position)))
|
||||||
return
|
return
|
||||||
|
@ -1109,39 +1150,29 @@ if mobs_spawn then
|
||||||
|
|
||||||
local amount_to_spawn = math.random(group_min, spawn_in_group)
|
local amount_to_spawn = math.random(group_min, spawn_in_group)
|
||||||
mcl_log("Spawning quantity: " .. amount_to_spawn)
|
mcl_log("Spawning quantity: " .. amount_to_spawn)
|
||||||
amount_to_spawn = math_min(amount_to_spawn, cap_space_available)
|
amount_to_spawn = math.min(amount_to_spawn, cap_space_available)
|
||||||
mcl_log("throttled spawning quantity: " .. amount_to_spawn)
|
mcl_log("throttled spawning quantity: " .. amount_to_spawn)
|
||||||
|
|
||||||
if logging then
|
if logging then
|
||||||
minetest.log("action", "[mcl_mobs] A group of " ..amount_to_spawn .. " " .. mob_def.name .. " mob spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at " .. minetest.pos_to_string(spawning_position, 1))
|
minetest.log("action", "[mcl_mobs] A group of " ..amount_to_spawn .. " " .. mob_def.name ..
|
||||||
|
"mob spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name ..
|
||||||
|
" at " .. minetest.pos_to_string(spawning_position, 1)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
spawned = spawn_group(spawning_position,mob_def,{minetest.get_node(vector.offset(spawning_position,0,-1,0)).name}, amount_to_spawn)
|
return spawn_group(spawning_position,mob_def,{minetest.get_node(vector.offset(spawning_position,0,-1,0)).name}, amount_to_spawn)
|
||||||
else
|
else
|
||||||
if logging then
|
if logging then
|
||||||
minetest.log("action", "[mcl_mobs] Mob " .. mob_def.name .. " spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at ".. minetest.pos_to_string(spawning_position, 1))
|
minetest.log("action", "[mcl_mobs] Mob " .. mob_def.name .. " spawns on " ..
|
||||||
|
minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at "..
|
||||||
|
minetest.pos_to_string(spawning_position, 1)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
return mcl_mobs.spawn(spawning_position, mob_def.name)
|
||||||
end
|
end
|
||||||
spawned = mcl_mobs.spawn(spawning_position, mob_def.name)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if spawned then
|
while spawn_loop_counter > 0 do
|
||||||
--mcl_log("We have spawned")
|
if inner_loop() then return end
|
||||||
mob_counts_close, mob_counts_wide, total_mobs = count_mobs_all("spawn_class", pos)
|
|
||||||
local new_spawning_position = find_spawning_position(pos, FIND_SPAWN_POS_RETRIES_SUCCESS_RESPIN)
|
|
||||||
if new_spawning_position then
|
|
||||||
mcl_log("Setting new spawning position")
|
|
||||||
spawning_position = new_spawning_position
|
|
||||||
else
|
|
||||||
mcl_log("Cannot set new spawning position")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
--mcl_log("Spawn check failed")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
--mcl_log("Cap space full")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
spawn_loop_counter = spawn_loop_counter - 1
|
spawn_loop_counter = spawn_loop_counter - 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1157,6 +1188,8 @@ if mobs_spawn then
|
||||||
initialize_spawn_data()
|
initialize_spawn_data()
|
||||||
timer = 0
|
timer = 0
|
||||||
|
|
||||||
|
local start_time_us = minetest.get_us_time()
|
||||||
|
|
||||||
local players = get_connected_players()
|
local players = get_connected_players()
|
||||||
local total_mobs, total_non_hostile, total_hostile = count_mobs_total_cap()
|
local total_mobs, total_non_hostile, total_hostile = count_mobs_total_cap()
|
||||||
|
|
||||||
|
@ -1167,6 +1200,7 @@ if mobs_spawn then
|
||||||
|
|
||||||
if total_mobs > mob_cap.total or total_mobs > #players * mob_cap.player then
|
if total_mobs > mob_cap.total or total_mobs > #players * mob_cap.player then
|
||||||
minetest.log("action","[mcl_mobs] global mob cap reached. no cycle spawning.")
|
minetest.log("action","[mcl_mobs] global mob cap reached. no cycle spawning.")
|
||||||
|
minetest.log("action","[mcl_mobs] took "..(minetest.get_us_time() - start_time_us).." us")
|
||||||
return
|
return
|
||||||
end --mob cap per player
|
end --mob cap per player
|
||||||
|
|
||||||
|
@ -1178,6 +1212,7 @@ if mobs_spawn then
|
||||||
spawn_a_mob(pos, cap_space_hostile, cap_space_non_hostile)
|
spawn_a_mob(pos, cap_space_hostile, cap_space_non_hostile)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
minetest.log("action","[mcl_mobs] took "..(minetest.get_us_time() - start_time_us).." us")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue