Add API to control respawn logic and behavior

This commit is contained in:
sfan5 2024-04-10 20:27:57 +02:00
parent d1ba7c3db3
commit c60d8e4da0
6 changed files with 115 additions and 66 deletions

View File

@ -632,6 +632,34 @@ set a players home position and teleport a player to home position.
* `name` Player you wish to teleport to their home position * `name` Player you wish to teleport to their home position
* return value: false if player cannot be sent home, otherwise true * return value: false if player cannot be sent home, otherwise true
Spawn API
---------
The spawn mod takes care of deciding the position of new and respawning players
in the world and has an API to modify its behavior.
`spawn.get_default_pos()`
* Gets the default spawn position as decided by a biome-dependent algorithm.
* This is not influenced by settings like "static_spawnpoint" or "engine_spawn".
* return value: a vector or `nil` on failure
`spawn.add_suitable_biome(biome)`:
* Adds a biome to the list of allowed biomes for the above algorithm.
* `biome`: Name of a registered biome
`spawn.register_on_spawn(func)`:
* Registers a callback to be called when a player (re-)spawns. This can be used
to intercept the normal logic to e.g. respawn a player at his bed.
* `func`: `function(player, is_new)` with arguments
- `player`: ObjectRef
- `is_new`: true if the player is joining the server for the first time
- return value: true to skip all other spawn logic, false or nil otherwise
When a player (re-)spawns the following order is executed:
1. All spawn callbacks in order of registration.
2. If no result, teleport player to `spawn.get_default_pos()`.
3. If that fails, spawning is left up to engine.
Sfinv API Sfinv API
--------- ---------

View File

@ -244,10 +244,9 @@ end
-- Callbacks -- Callbacks
-- Only register respawn callback if respawn enabled -- Only register respawn callback if respawn enabled
if enable_respawn then if enable_respawn then
-- respawn player at bed if enabled and valid position is found -- Respawn player at bed if valid position is found
minetest.register_on_respawnplayer(function(player) spawn.register_on_spawn(function(player, is_new)
local name = player:get_player_name() local pos = beds.spawn[player:get_player_name()]
local pos = beds.spawn[name]
if pos then if pos then
player:set_pos(pos) player:set_pos(pos)
return true return true

View File

@ -1,3 +1,3 @@
name = beds name = beds
description = Minetest Game mod: beds description = Minetest Game mod: beds
depends = default, wool depends = default, wool, spawn

49
mods/spawn/api.lua Normal file
View File

@ -0,0 +1,49 @@
spawn = {}
-- provide empty default implementations
function spawn.get_default_pos()
return nil
end
function spawn.add_suitable_biome(biome)
end
-- Callback registration
spawn.registered_on_spawn = {}
function spawn.register_on_spawn(func)
table.insert(spawn.registered_on_spawn, func)
end
-- Logic run on spawn
local use_engine_spawn = minetest.settings:get("static_spawnpoint") or
minetest.settings:get_bool("engine_spawn")
local function on_spawn(player, is_new)
-- Ask all callbacks first
for _, cb in ipairs(spawn.registered_on_spawn) do
if cb(player, is_new) then
return true
end
end
-- Fall back to default spawn
if not use_engine_spawn then
local pos = spawn.get_default_pos()
if pos then
player:set_pos(pos)
return true
end
end
return false
end
minetest.register_on_newplayer(function(player)
on_spawn(player, true)
end)
minetest.register_on_respawnplayer(function(player)
return on_spawn(player, false)
end)

View File

@ -1,12 +1,12 @@
-- spawn/init.lua -- Always load the API
----------------------
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/api.lua")
-- Disable by mapgen, setting or if 'static_spawnpoint' is set -- Disable biome-search implementation on unsuitable mapgens
-------------------------------------------------------------- ------------------------------------------------------------
local mg_name = minetest.get_mapgen_setting("mg_name") local mg_name = minetest.get_mapgen_setting("mg_name")
if mg_name == "v6" or mg_name == "singlenode" or if mg_name == "v6" or mg_name == "singlenode" then
minetest.settings:get("static_spawnpoint") or
minetest.settings:get_bool("engine_spawn") then
return return
end end
@ -23,27 +23,32 @@ local checks = 128 * 128
local pos = {x = 0, y = 8, z = 0} local pos = {x = 0, y = 8, z = 0}
-- Table of suitable biomes -- Table of suitable biomes and matching API function
local biome_ids = { local biome_ids = {}
minetest.get_biome_id("taiga"),
minetest.get_biome_id("coniferous_forest"), function spawn.add_suitable_biome(biome)
minetest.get_biome_id("deciduous_forest"), local id = minetest.get_biome_id(biome)
minetest.get_biome_id("grassland"), assert(id ~= nil)
minetest.get_biome_id("savanna"), biome_ids[id] = true
} end
for _, name in ipairs({
"taiga", "coniferous_forest", "deciduous_forest", "grassland", "savanna"
}) do
spawn.add_suitable_biome(name)
end
-- End of parameters -- End of parameters
-------------------- --------------------
-- Direction table -- Direction table
local dirs = { local dirs = {
{x = 0, y = 0, z = 1}, vector.new(0, 0, 1),
{x = -1, y = 0, z = 0}, vector.new(-1, 0, 0),
{x = 0, y = 0, z = -1}, vector.new(0, 0, -1),
{x = 1, y = 0, z = 0}, vector.new(1, 0, 0),
} }
@ -67,8 +72,8 @@ local chunksize = tonumber(minetest.get_mapgen_setting("chunksize"))
local spawn_limit = math.max(mapgen_limit - (chunksize + 1) * 16, 0) local spawn_limit = math.max(mapgen_limit - (chunksize + 1) * 16, 0)
--Functions -- Functions
----------- ------------
-- Get next position on square search spiral -- Get next position on square search spiral
@ -98,17 +103,13 @@ local function search()
for iter = 1, checks do for iter = 1, checks do
local biome_data = minetest.get_biome_data(pos) local biome_data = minetest.get_biome_data(pos)
-- Sometimes biome_data is nil -- Sometimes biome_data is nil
local biome = biome_data and biome_data.biome if biome_data and biome_ids[biome_data.biome] then
for id_ind = 1, #biome_ids do
local biome_id = biome_ids[id_ind]
if biome == biome_id then
local spawn_y = minetest.get_spawn_level(pos.x, pos.z) local spawn_y = minetest.get_spawn_level(pos.x, pos.z)
if spawn_y then if spawn_y then
spawn_pos = {x = pos.x, y = spawn_y, z = pos.z} spawn_pos = vector.new(pos.x, spawn_y, pos.z)
return true return true
end end
end end
end
pos = next_pos() pos = next_pos()
-- Check for position being outside world edge -- Check for position being outside world edge
@ -121,38 +122,11 @@ local function search()
end end
-- On new player spawn and player respawn function spawn.get_default_pos()
-- Search for spawn position once per server session
-- Search for spawn position once per server session. If successful, store
-- position and reposition players, otherwise leave them at engine spawn
-- position.
local function on_spawn(player)
if not searched then if not searched then
success = search() success = search()
searched = true searched = true
end end
if success then return success and spawn_pos
player:set_pos(spawn_pos)
end
return success
end end
minetest.register_on_newplayer(function(player)
on_spawn(player)
end)
local enable_bed_respawn = minetest.settings:get_bool("enable_bed_respawn")
if enable_bed_respawn == nil then
enable_bed_respawn = true
end
minetest.register_on_respawnplayer(function(player)
-- Avoid respawn conflict with beds mod
if beds and enable_bed_respawn and
beds.spawn[player:get_player_name()] then
return
end
return on_spawn(player)
end)

View File

@ -1,4 +1,3 @@
name = spawn name = spawn
description = Minetest Game mod: spawn description = Minetest Game mod: spawn
depends = default depends = default
optional_depends = beds