From c60d8e4da0d55014c06ee78b366be23152da35e0 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 10 Apr 2024 20:27:57 +0200 Subject: [PATCH] Add API to control respawn logic and behavior --- game_api.txt | 28 ++++++++++++ mods/beds/functions.lua | 7 ++- mods/beds/mod.conf | 2 +- mods/spawn/api.lua | 49 +++++++++++++++++++++ mods/spawn/init.lua | 94 +++++++++++++++-------------------------- mods/spawn/mod.conf | 1 - 6 files changed, 115 insertions(+), 66 deletions(-) create mode 100644 mods/spawn/api.lua diff --git a/game_api.txt b/game_api.txt index 9a7ef8fb..b3f6efc6 100644 --- a/game_api.txt +++ b/game_api.txt @@ -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 --------- diff --git a/mods/beds/functions.lua b/mods/beds/functions.lua index 0668976e..c3e2a2c9 100644 --- a/mods/beds/functions.lua +++ b/mods/beds/functions.lua @@ -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 diff --git a/mods/beds/mod.conf b/mods/beds/mod.conf index 450ec138..ef88ad18 100644 --- a/mods/beds/mod.conf +++ b/mods/beds/mod.conf @@ -1,3 +1,3 @@ name = beds description = Minetest Game mod: beds -depends = default, wool +depends = default, wool, spawn diff --git a/mods/spawn/api.lua b/mods/spawn/api.lua new file mode 100644 index 00000000..bffe32a3 --- /dev/null +++ b/mods/spawn/api.lua @@ -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) diff --git a/mods/spawn/init.lua b/mods/spawn/init.lua index 12c957f9..df30a25e 100644 --- a/mods/spawn/init.lua +++ b/mods/spawn/init.lua @@ -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), } @@ -67,8 +72,8 @@ local chunksize = tonumber(minetest.get_mapgen_setting("chunksize")) local spawn_limit = math.max(mapgen_limit - (chunksize + 1) * 16, 0) ---Functions ------------ +-- Functions +------------ -- Get next position on square search spiral @@ -98,15 +103,11 @@ 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 - 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} - return true - end + 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 = vector.new(pos.x, spawn_y, pos.z) + return true end end @@ -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) - end - return success + return success and spawn_pos 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) diff --git a/mods/spawn/mod.conf b/mods/spawn/mod.conf index ec3d5648..7a09d4a5 100644 --- a/mods/spawn/mod.conf +++ b/mods/spawn/mod.conf @@ -1,4 +1,3 @@ name = spawn description = Minetest Game mod: spawn depends = default -optional_depends = beds