2018-12-28 03:55:00 +00:00
|
|
|
rspawn = {}
|
2018-12-28 23:31:33 +00:00
|
|
|
rspawn.playerspawns = {}
|
2018-12-28 03:55:00 +00:00
|
|
|
|
|
|
|
local mpath = minetest.get_modpath("rspawn")
|
|
|
|
|
2018-12-28 23:31:33 +00:00
|
|
|
-- Water level, plus one to ensure we are above the sea.
|
|
|
|
local water_level = tonumber(minetest.settings:get("water_level", "1") )+1
|
2018-12-28 03:55:00 +00:00
|
|
|
local radial_step = 16
|
|
|
|
|
2018-12-28 23:31:33 +00:00
|
|
|
local static_spawnpoint = minetest.setting_get_pos("static_spawnpoint") or {x=0, y=50, z=0}
|
2018-12-28 03:55:00 +00:00
|
|
|
|
2018-12-28 23:31:33 +00:00
|
|
|
rspawn.adminname = minetest.settings:get("name", "singleplayer")
|
|
|
|
rspawn.spawnanywhere = minetest.settings:get_bool("spawn_anywhere", true)
|
|
|
|
rspawn.bedspawn = minetest.setting_getbool("enable_bed_respawn", true)
|
2016-07-09 11:22:49 -04:00
|
|
|
|
2018-12-28 23:31:33 +00:00
|
|
|
dofile(mpath.."/src/data.lua")
|
|
|
|
dofile(mpath.."/src/commands.lua")
|
2017-01-31 18:08:53 +00:00
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
rspawn:spawnload()
|
2017-01-13 16:07:17 +00:00
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
local function forceload_operate(pos1, pos2, handler)
|
|
|
|
local i,j,k
|
|
|
|
|
|
|
|
for i=pos1.x,pos2.x,16 do
|
|
|
|
for j=pos1.y,pos2.y,16 do
|
|
|
|
for k=pos1.z,pos2.z,16 do
|
2018-12-28 23:31:33 +00:00
|
|
|
handler({x=i,y=j,z=k})
|
2018-12-28 03:55:00 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-01-31 18:20:24 +00:00
|
|
|
end
|
2017-01-14 00:11:59 +00:00
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
local function forceload_blocks_in(pos1, pos2)
|
|
|
|
forceload_operate(pos1, pos2, minetest.forceload_block)
|
2017-01-31 18:08:53 +00:00
|
|
|
end
|
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
local function forceload_free_blocks_in(pos1, pos2)
|
|
|
|
forceload_operate(pos1, pos2, minetest.forceload_free_block)
|
|
|
|
end
|
2017-01-31 18:08:53 +00:00
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
local function daylight_above(min_daylight, pos)
|
2018-12-28 04:12:50 +00:00
|
|
|
local level = minetest.get_node_light(pos, 0.5)
|
|
|
|
return min_daylight <= level
|
2017-01-13 23:00:48 +00:00
|
|
|
end
|
2017-01-13 22:15:45 +00:00
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
function rspawn:newspawn(pos, radius)
|
|
|
|
-- Given a seed position and a radius, find an exact spawn location
|
|
|
|
-- that is walkable and with 2 air nodes above it
|
|
|
|
|
|
|
|
if not radius then
|
|
|
|
radius = radial_step
|
|
|
|
end
|
|
|
|
|
|
|
|
if radius > 4*radial_step then
|
2018-12-28 23:31:33 +00:00
|
|
|
minetest.debug("__ No valid spawnable location around "..minetest.pos_to_string(pos))
|
2018-12-28 03:55:00 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
minetest.debug("Trying somewhere around "..minetest.pos_to_string(pos))
|
|
|
|
|
|
|
|
local breadth = radius/2
|
|
|
|
local altitude = radius*2
|
|
|
|
|
2018-12-28 23:31:33 +00:00
|
|
|
local pos1 = {x=pos.x-breadth, y=pos.y, z=pos.z-breadth}
|
2018-12-28 03:55:00 +00:00
|
|
|
local pos2 = {x=pos.x+breadth, y=pos.y+altitude, z=pos.z+breadth}
|
|
|
|
|
|
|
|
minetest.debug("Searching "..minetest.pos_to_string(pos1).." to "..minetest.pos_to_string(pos2))
|
|
|
|
|
|
|
|
minetest.emerge_area(pos1, pos2)
|
|
|
|
forceload_blocks_in(pos1, pos2)
|
|
|
|
|
|
|
|
local airnodes = minetest.find_nodes_in_area(pos1, pos2, {"air"})
|
|
|
|
local validnodes = {}
|
|
|
|
|
|
|
|
minetest.debug("Found "..tostring(#airnodes).." air nodes within "..tostring(radius))
|
|
|
|
for _,anode in pairs(airnodes) do
|
|
|
|
local under = minetest.get_node( {x=anode.x, y=anode.y-1, z=anode.z} ).name
|
|
|
|
local over = minetest.get_node( {x=anode.x, y=anode.y+1, z=anode.z} ).name
|
|
|
|
under = minetest.registered_nodes[under]
|
|
|
|
over = minetest.registered_nodes[over]
|
|
|
|
|
|
|
|
if under.walkable
|
|
|
|
and not over.walkable
|
|
|
|
and not minetest.is_protected(anode, rspawn.adminname)
|
|
|
|
and daylight_above(7, anode) then
|
|
|
|
validnodes[#validnodes+1] = anode
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if #validnodes > 0 then
|
|
|
|
minetest.log("info", "New spawn point found with radius "..tostring(radius))
|
|
|
|
forceload_free_blocks_in(pos1, pos2)
|
|
|
|
return validnodes[math.random(1,#validnodes)]
|
|
|
|
end
|
|
|
|
|
|
|
|
local pos = rspawn:newspawn(pos, radius+radial_step)
|
|
|
|
if not pos then
|
|
|
|
-- Nothing found, do cleanup with this largest forceloaded area
|
|
|
|
forceload_free_blocks_in(pos1, pos2)
|
|
|
|
end
|
|
|
|
return pos
|
2017-01-13 23:00:48 +00:00
|
|
|
end
|
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
function rspawn:genpos()
|
|
|
|
-- Generate a random position, and derive a new spawn position
|
2018-12-28 23:31:33 +00:00
|
|
|
local pos = static_spawnpoint
|
2018-12-28 03:55:00 +00:00
|
|
|
|
|
|
|
if rspawn.spawnanywhere then
|
|
|
|
pos = {
|
|
|
|
x = math.random(-30000,30000),
|
2018-12-28 23:31:33 +00:00
|
|
|
y = math.random(water_level, water_level+10),
|
2018-12-28 03:55:00 +00:00
|
|
|
z = math.random(-30000,30000),
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
return pos
|
2017-01-13 23:00:48 +00:00
|
|
|
end
|
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
function rspawn:set_new_playerspawn(player, args)
|
|
|
|
local newpos
|
|
|
|
if args == "here" then
|
|
|
|
newpos = player:get_pos()
|
|
|
|
elseif args then
|
|
|
|
newpos = minetest.string_to_pos(args)
|
|
|
|
end
|
|
|
|
|
|
|
|
if not newpos then
|
|
|
|
newpos = rspawn:genpos()
|
|
|
|
end
|
|
|
|
|
|
|
|
local spawnpos = rspawn:newspawn(newpos)
|
|
|
|
local name = player:get_player_name()
|
|
|
|
|
|
|
|
if spawnpos then
|
|
|
|
rspawn.playerspawns[name] = spawnpos
|
|
|
|
rspawn:spawnsave()
|
|
|
|
return spawnpos
|
|
|
|
end
|
|
|
|
end
|
2017-01-13 22:03:36 +00:00
|
|
|
|
2018-12-28 23:31:33 +00:00
|
|
|
local function confirm_new_spawn(name, newpos)
|
|
|
|
minetest.chat_send_player(name, "New spawn set at "..minetest.pos_to_string(newpos))
|
|
|
|
minetest.get_player_by_name(name):setpos(rspawn.playerspawns[name])
|
|
|
|
end
|
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
function rspawn:double_set_new_playerspawn(player, attempts)
|
|
|
|
local cpos = minetest.pos_to_string(rspawn:genpos())
|
|
|
|
local name = player:get_player_name()
|
|
|
|
attempts = attempts or 1
|
|
|
|
|
|
|
|
minetest.chat_send_player(name, tostring(attempts)..": Searching for a suitable spawn around "..cpos)
|
|
|
|
|
|
|
|
local newpos = rspawn:set_new_playerspawn(player, cpos)
|
2018-12-28 23:31:33 +00:00
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
if not newpos then
|
2018-12-28 23:31:33 +00:00
|
|
|
-- Repeat only after some time: give the server time to get through previous emerge calls
|
2018-12-28 03:55:00 +00:00
|
|
|
minetest.after(4,function()
|
2018-12-28 23:31:33 +00:00
|
|
|
-- Second attempt at the same location - emerge calls should have yielded
|
|
|
|
-- map data to work with
|
2018-12-28 03:55:00 +00:00
|
|
|
newpos = rspawn:set_new_playerspawn(player, cpos)
|
2018-12-28 23:31:33 +00:00
|
|
|
|
2018-12-28 03:55:00 +00:00
|
|
|
if not newpos then
|
|
|
|
if attempts > 0 then
|
2018-12-28 23:31:33 +00:00
|
|
|
-- Repeat the process at a new location
|
2018-12-28 03:55:00 +00:00
|
|
|
rspawn:double_set_new_playerspawn(player, attempts - 1)
|
|
|
|
else
|
|
|
|
minetest.chat_send_player(name, "! Could not identify suitable spawn location (try again?)")
|
|
|
|
end
|
|
|
|
else
|
2018-12-28 23:31:33 +00:00
|
|
|
confirm_new_spawn(name, newpos)
|
2018-12-28 03:55:00 +00:00
|
|
|
end
|
|
|
|
end)
|
2018-12-28 23:31:33 +00:00
|
|
|
else
|
|
|
|
confirm_new_spawn(name, newpos)
|
2018-12-28 03:55:00 +00:00
|
|
|
end
|
|
|
|
end
|
2017-01-13 16:07:17 +00:00
|
|
|
|
2017-01-13 23:00:48 +00:00
|
|
|
minetest.register_on_joinplayer(function(player)
|
2018-12-28 03:55:00 +00:00
|
|
|
-- Use the recursive mode - it is not acceptable for a player
|
|
|
|
-- not to receive a randomized spawn
|
2018-12-28 05:17:16 +00:00
|
|
|
minetest.after(1,function()
|
|
|
|
if not rspawn.playerspawns[player:get_player_name()] then
|
|
|
|
rspawn:double_set_new_playerspawn(player, 10)
|
|
|
|
end
|
|
|
|
end)
|
2017-01-13 23:00:48 +00:00
|
|
|
end)
|
2017-01-13 23:16:29 +00:00
|
|
|
|
|
|
|
minetest.register_on_respawnplayer(function(player)
|
2018-12-28 03:55:00 +00:00
|
|
|
local name = player:get_player_name()
|
|
|
|
if rspawn.bedspawn == true then
|
|
|
|
local pos = beds.spawn[name]
|
|
|
|
if pos then
|
|
|
|
player:setpos(pos)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- And if no bed, nor bed spwawning not active:
|
|
|
|
player:setpos(rspawn.playerspawns[name])
|
|
|
|
return true
|
2017-01-13 23:16:29 +00:00
|
|
|
end)
|