1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-09-15 18:57:08 +00:00

Add exclude_player to particle spawners

This commit is contained in:
sfan5 2025-09-05 16:42:57 +02:00
parent b6a23b1bcc
commit f714ac0611
5 changed files with 82 additions and 50 deletions

View file

@ -49,6 +49,7 @@ core.features = {
httpfetch_additional_methods = true, httpfetch_additional_methods = true,
object_guids = true, object_guids = true,
on_timer_four_args = true, on_timer_four_args = true,
particlespawner_exclude_player = true,
} }
function core.has_feature(arg) function core.has_feature(arg)

View file

@ -5843,6 +5843,8 @@ Utilities
object_guids = true, object_guids = true,
-- The NodeTimer `on_timer` callback is passed additional `node` and `timeout` args (5.14.0) -- The NodeTimer `on_timer` callback is passed additional `node` and `timeout` args (5.14.0)
on_timer_four_args = true, on_timer_four_args = true,
-- `ParticleSpawner` definition supports `exclude_player` field (5.14.0)
particlespawner_exclude_player = true,
} }
``` ```
@ -7471,6 +7473,7 @@ Particles
--------- ---------
* `core.add_particle(particle definition)` * `core.add_particle(particle definition)`
* Spawn a single particle
* Deprecated: `core.add_particle(pos, velocity, acceleration, * Deprecated: `core.add_particle(pos, velocity, acceleration,
expirationtime, size, collisiondetection, texture, playername)` expirationtime, size, collisiondetection, texture, playername)`
@ -11487,6 +11490,9 @@ Used by `core.add_particle`.
playername = "singleplayer", playername = "singleplayer",
-- Optional, if specified spawns particle only on the player's client -- Optional, if specified spawns particle only on the player's client
-- Note that `exclude_player` is not supported here. You can use a single-use
-- particlespawner if needed.
animation = {Tile Animation definition}, animation = {Tile Animation definition},
-- Optional, specifies how to animate the particle texture -- Optional, specifies how to animate the particle texture
@ -11550,6 +11556,9 @@ will be ignored.
-- If time is 0 spawner has infinite lifespan and spawns the `amount` on -- If time is 0 spawner has infinite lifespan and spawns the `amount` on
-- a per-second basis. -- a per-second basis.
size = 1,
-- Size of the particle.
collisiondetection = false, collisiondetection = false,
-- If true collide with `walkable` nodes and, depending on the -- If true collide with `walkable` nodes and, depending on the
-- `object_collision` field, objects too. -- `object_collision` field, objects too.
@ -11576,7 +11585,12 @@ will be ignored.
-- following section. -- following section.
playername = "singleplayer", playername = "singleplayer",
-- Optional, if specified spawns particles only on the player's client -- Optional, if specified spawns particles only for this player
-- Can't be used together with `exclude_player`.
exclude_player = "singleplayer",
-- Optional, if specified spawns particles not for this player
-- Added in v5.14.0. Can't be used together with `playername`.
animation = {Tile Animation definition}, animation = {Tile Animation definition},
-- Optional, specifies how to animate the particles' texture -- Optional, specifies how to animate the particles' texture

View file

@ -157,7 +157,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
// Get parameters // Get parameters
ParticleSpawnerParameters p; ParticleSpawnerParameters p;
ServerActiveObject *attached = NULL; ServerActiveObject *attached = NULL;
std::string playername; std::string playername, not_playername;
using namespace ParticleParamTypes; using namespace ParticleParamTypes;
if (lua_gettop(L) > 1) //deprecated if (lua_gettop(L) > 1) //deprecated
@ -257,7 +257,6 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
lua_pop(L, 1); lua_pop(L, 1);
p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); p.vertical = getboolfield_default(L, 1, "vertical", p.vertical);
playername = getstringfield_default(L, 1, "playername", "");
p.glow = getintfield_default(L, 1, "glow", p.glow); p.glow = getintfield_default(L, 1, "glow", p.glow);
lua_getfield(L, 1, "texpool"); lua_getfield(L, 1, "texpool");
@ -279,12 +278,16 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
lua_pop(L, 1); lua_pop(L, 1);
p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile);
// meta parameters
playername = getstringfield_default(L, 1, "playername", "");
not_playername = getstringfield_default(L, 1, "exclude_player", "");
} }
if (p.time < 0) if (p.time < 0)
throw LuaError("particle spawner 'time' must be >= 0"); throw LuaError("particle spawner 'time' must be >= 0");
u32 id = getServer(L)->addParticleSpawner(p, attached, playername); u32 id = getServer(L)->addParticleSpawner(p, attached, playername, not_playername);
lua_pushnumber(L, id); lua_pushnumber(L, id);
return 1; return 1;

View file

@ -1646,45 +1646,61 @@ void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
Send(&pkt); Send(&pkt);
} }
// Adds a ParticleSpawner on peer with peer_id void Server::SendAddParticleSpawner(const std::string &to_player,
void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, const std::string &exclude_player,
const ParticleSpawnerParameters &p, u16 attached_id, u32 id) const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
{ {
static thread_local const float radius = static thread_local const float radius =
g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS; g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
const float radius_sq = radius * radius;
if (peer_id == PEER_ID_INEXISTENT) { // Average position where particles would spawn (approximate)
std::vector<session_t> clients = m_clients.getClientIDs();
const v3f pos = ( const v3f pos = (
p.pos.start.min.val + p.pos.start.min.val +
p.pos.start.max.val + p.pos.start.max.val +
p.pos.end.min.val + p.pos.end.min.val +
p.pos.end.max.val p.pos.end.max.val
) / 4.0f * BS; ) / 4.0f * BS;
const float radius_sq = radius * radius;
/* Don't send short-lived spawners to distant players. /* Don't send short-lived spawners to distant players.
* This could be replaced with proper tracking at some point. * This could be replaced with proper tracking at some point.
* A lifetime of 0 means that the spawner exists forever.*/ * A lifetime of 0 means that the spawner exists forever. */
const bool distance_check = !attached_id && p.time <= 1.0f && p.time != 0.0f; const bool distance_check = !attached_id && p.time <= 1.0f && p.time != 0.0f;
const auto &consider_player = [&] (RemotePlayer *player) {
if (distance_check) {
PlayerSAO *sao = player->getPlayerSAO();
if (!sao)
return;
if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
return;
}
SendAddParticleSpawner(player->getPeerId(), player->protocol_version,
p, attached_id, id);
};
// Send to one -or- all (except one)
if (!to_player.empty()) {
RemotePlayer *player = m_env->getPlayer(to_player);
if (player)
consider_player(player);
return;
}
std::vector<session_t> clients = m_clients.getClientIDs();
for (const session_t client_id : clients) { for (const session_t client_id : clients) {
RemotePlayer *player = m_env->getPlayer(client_id); RemotePlayer *player = m_env->getPlayer(client_id);
if (!player) if (!player)
continue; continue;
if (!exclude_player.empty() && exclude_player == player->getName())
if (distance_check) {
PlayerSAO *sao = player->getPlayerSAO();
if (!sao)
continue;
if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
continue; continue;
consider_player(player);
} }
}
SendAddParticleSpawner(client_id, player->protocol_version, void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
p, attached_id, id); const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
} {
return; assert(peer_id != PEER_ID_INEXISTENT);
}
assert(protocol_version != 0); assert(protocol_version != 0);
NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id); NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
@ -3619,22 +3635,12 @@ void Server::spawnParticle(const std::string &playername,
} }
u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p, u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
ServerActiveObject *attached, const std::string &playername) ServerActiveObject *attached, const std::string &to_player,
const std::string &exclude_player)
{ {
// m_env will be NULL if the server is initializing
if (!m_env) if (!m_env)
return -1; return -1;
session_t peer_id = PEER_ID_INEXISTENT;
u16 proto_ver = 0;
if (!playername.empty()) {
RemotePlayer *player = m_env->getPlayer(playername.c_str());
if (!player)
return -1;
peer_id = player->getPeerId();
proto_ver = player->protocol_version;
}
u16 attached_id = attached ? attached->getId() : 0; u16 attached_id = attached ? attached->getId() : 0;
u32 id; u32 id;
@ -3643,13 +3649,12 @@ u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
else else
id = m_env->addParticleSpawner(p.time, attached_id); id = m_env->addParticleSpawner(p.time, attached_id);
SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id); SendAddParticleSpawner(to_player, exclude_player, p, attached_id, id);
return id; return id;
} }
void Server::deleteParticleSpawner(const std::string &playername, u32 id) void Server::deleteParticleSpawner(const std::string &playername, u32 id)
{ {
// m_env will be NULL if the server is initializing
if (!m_env) if (!m_env)
throw ServerError("Can't delete particle spawners during initialisation!"); throw ServerError("Can't delete particle spawners during initialisation!");
@ -3661,7 +3666,11 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id)
peer_id = player->getPeerId(); peer_id = player->getPeerId();
} }
// FIXME: we don't track which client still knows about this spawner, so
// just deleting it entirely is problematic!
// We also don't check if the ID is even in use. FAIL!
m_env->deleteParticleSpawner(id); m_env->deleteParticleSpawner(id);
SendDeleteParticleSpawner(peer_id, id); SendDeleteParticleSpawner(peer_id, id);
} }

View file

@ -291,7 +291,8 @@ public:
const ParticleParameters &p); const ParticleParameters &p);
u32 addParticleSpawner(const ParticleSpawnerParameters &p, u32 addParticleSpawner(const ParticleSpawnerParameters &p,
ServerActiveObject *attached, const std::string &playername); ServerActiveObject *attached, const std::string &to_player,
const std::string &exclude_player);
void deleteParticleSpawner(const std::string &playername, u32 id); void deleteParticleSpawner(const std::string &playername, u32 id);
@ -593,7 +594,11 @@ private:
const std::unordered_set<std::string> &tosend); const std::unordered_set<std::string> &tosend);
void stepPendingDynMediaCallbacks(float dtime); void stepPendingDynMediaCallbacks(float dtime);
// Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) /// @brief send particle spawner to a selection of clients
void SendAddParticleSpawner(const std::string &to_player,
const std::string &exclude_player,
const ParticleSpawnerParameters &p, u16 attached_id, u32 id);
/// @brief send particle spawner to one client (internal)
void SendAddParticleSpawner(session_t peer_id, u16 protocol_version, void SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
const ParticleSpawnerParameters &p, u16 attached_id, u32 id); const ParticleSpawnerParameters &p, u16 attached_id, u32 id);