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

@ -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
* 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
---------

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

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

49
mods/spawn/api.lua Normal 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)

@ -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")
if mg_name == "v6" or mg_name == "singlenode" or
minetest.settings:get("static_spawnpoint") or
minetest.settings:get_bool("engine_spawn") then
if mg_name == "v6" or mg_name == "singlenode" then
return
end
@ -23,27 +23,32 @@ local checks = 128 * 128
local pos = {x = 0, y = 8, z = 0}
-- Table of suitable biomes
-- Table of suitable biomes and matching API function
local biome_ids = {
minetest.get_biome_id("taiga"),
minetest.get_biome_id("coniferous_forest"),
minetest.get_biome_id("deciduous_forest"),
minetest.get_biome_id("grassland"),
minetest.get_biome_id("savanna"),
}
local biome_ids = {}
function spawn.add_suitable_biome(biome)
local id = minetest.get_biome_id(biome)
assert(id ~= nil)
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
--------------------
-- Direction table
local dirs = {
{x = 0, y = 0, z = 1},
{x = -1, y = 0, z = 0},
{x = 0, y = 0, z = -1},
{x = 1, y = 0, z = 0},
vector.new(0, 0, 1),
vector.new(-1, 0, 0),
vector.new(0, 0, -1),
vector.new(1, 0, 0),
}
@ -68,7 +73,7 @@ local spawn_limit = math.max(mapgen_limit - (chunksize + 1) * 16, 0)
-- Functions
-----------
------------
-- Get next position on square search spiral
@ -98,17 +103,13 @@ local function search()
for iter = 1, checks do
local biome_data = minetest.get_biome_data(pos)
-- Sometimes biome_data is nil
local biome = biome_data and biome_data.biome
for id_ind = 1, #biome_ids do
local biome_id = biome_ids[id_ind]
if biome == biome_id then
if biome_data and biome_ids[biome_data.biome] then
local spawn_y = minetest.get_spawn_level(pos.x, pos.z)
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
end
end
end
pos = next_pos()
-- Check for position being outside world edge
@ -121,38 +122,11 @@ local function search()
end
-- On new player spawn and player respawn
-- 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)
function spawn.get_default_pos()
-- Search for spawn position once per server session
if not searched then
success = search()
searched = true
end
if success then
player:set_pos(spawn_pos)
return success and spawn_pos
end
return success
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)

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