Improve core.sound_play with ephemeral sounds and player exclusion
authorsfan5 <sfan5@live.de>
Sat, 25 Jan 2020 20:19:29 +0000 (21:19 +0100)
committersfan5 <sfan5@live.de>
Sat, 1 Feb 2020 19:31:41 +0000 (20:31 +0100)
doc/lua_api.txt
src/client/client.cpp
src/client/client.h
src/network/clientpackethandler.cpp
src/network/networkprotocol.h
src/script/common/c_content.cpp
src/script/lua_api/l_server.cpp
src/server.cpp
src/server.h

index 6b8dcb5fcc4af2d0f4e8300a3e9f1c957d45f932..051ca97cf11b16d05dc6b40aa30f8d95379459f8 100644 (file)
@@ -826,7 +826,7 @@ Examples of sound parameter tables:
         gain = 1.0,  -- default
         loop = true,
     }
-    -- Play in a location
+    -- Play at a location
     {
         pos = {x = 1, y = 2, z = 3},
         gain = 1.0,  -- default
@@ -839,13 +839,22 @@ Examples of sound parameter tables:
         max_hear_distance = 32,  -- default, uses an euclidean metric
         loop = true,
     }
+    -- Play at a location, heard by anyone *but* the given player
+    {
+        pos = {x = 32, y = 0, z = 100},
+        max_hear_distance = 40,
+        exclude_player = name,
+    }
 
 Looped sounds must either be connected to an object or played locationless to
-one player using `to_player = name,`.
+one player using `to_player = name`.
 
 A positional sound will only be heard by players that are within
 `max_hear_distance` of the sound position, at the start of the sound.
 
+`exclude_player = name` can be applied to locationless, positional and object-
+bound sounds to exclude a single player from hearing them.
+
 `SimpleSoundSpec`
 -----------------
 
@@ -4929,10 +4938,15 @@ Defaults for the `on_punch` and `on_dig` node definition callbacks
 Sounds
 ------
 
-* `minetest.sound_play(spec, parameters)`: returns a handle
+* `minetest.sound_play(spec, parameters, [ephemeral])`: returns a handle
     * `spec` is a `SimpleSoundSpec`
     * `parameters` is a sound parameter table
+    * `ephemeral` is a boolean (default: false)
+      Ephemeral sounds will not return a handle and can't be stopped or faded.
+      It is recommend to use this for short sounds that happen in response to
+      player actions (e.g. door closing).
 * `minetest.sound_stop(handle)`
+    * `handle` is a handle returned by `minetest.sound_play`
 * `minetest.sound_fade(handle, step, gain)`
     * `handle` is a handle returned by `minetest.sound_play`
     * `step` determines how fast a sound will fade.
index 94b483802c5d184ee822faf593593b926610b9ee..6016f940ec8cbd39ba200f235978827e5d43e405 100644 (file)
@@ -1106,7 +1106,7 @@ void Client::sendRemovedSounds(std::vector<s32> &soundList)
 
        pkt << (u16) (server_ids & 0xFFFF);
 
-       for (int sound_id : soundList)
+       for (s32 sound_id : soundList)
                pkt << sound_id;
 
        Send(&pkt);
index 10608ccf92d1888f333d498c30b3919e3f38a022..b9807a11ed723e3125d6d4f3ab0032c7d2d39c40 100644 (file)
@@ -561,7 +561,7 @@ private:
        std::unordered_map<s32, int> m_sounds_server_to_client;
        // And the other way!
        std::unordered_map<int, s32> m_sounds_client_to_server;
-       // And relations to objects
+       // Relation of client id to object id
        std::unordered_map<int, u16> m_sounds_to_objects;
 
        // Map server hud ids to client hud ids
index 79f2b95babfc913688a117063254c8f056f847ab..ceb4b3b17e2f02cd4da71fae02d563236499772e 100644 (file)
@@ -778,6 +778,7 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
                [25 + len] bool loop
                [26 + len] f32 fade
                [30 + len] f32 pitch
+               [34 + len] bool ephemeral
        */
 
        s32 server_id;
@@ -790,12 +791,14 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
        bool loop;
        float fade = 0.0f;
        float pitch = 1.0f;
+       bool ephemeral = false;
 
        *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
 
        try {
                *pkt >> fade;
                *pkt >> pitch;
+               *pkt >> ephemeral;
        } catch (PacketError &e) {};
 
        // Start playing
@@ -813,7 +816,6 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
                        if (cao)
                                pos = cao->getPosition();
                        client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
-                       // TODO: Set up sound to move with object
                        break;
                }
                default:
@@ -821,8 +823,11 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
        }
 
        if (client_id != -1) {
-               m_sounds_server_to_client[server_id] = client_id;
-               m_sounds_client_to_server[client_id] = server_id;
+               // for ephemeral sounds, server_id is not meaningful
+               if (!ephemeral) {
+                       m_sounds_server_to_client[server_id] = client_id;
+                       m_sounds_client_to_server[client_id] = server_id;
+               }
                if (object_id != 0)
                        m_sounds_to_objects[client_id] = object_id;
        }
index 2ade030c4036e5ae5ac6d9777938097f7cbeec50..3be4110eee6d3de930f75e47acf4332e44938901 100644 (file)
@@ -200,6 +200,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
                Unknown inventory serialization fields no longer throw an error
                Mod-specific formspec version
                Player FOV override API
+               "ephemeral" added to TOCLIENT_PLAY_SOUND
 */
 
 #define LATEST_PROTOCOL_VERSION 38
@@ -450,6 +451,7 @@ enum ToClientCommand
                s32[3] pos_nodes*10000
                u16 object_id
                u8 loop (bool)
+               u8 ephemeral (bool)
        */
 
        TOCLIENT_STOP_SOUND = 0x40,
index ca061c45466f1384a1dabc69363d4fad5b4b2ab6..accbb1a875465a14aff9d11e81da2543d34061fc 100644 (file)
@@ -1019,6 +1019,7 @@ void read_server_sound_params(lua_State *L, int index,
                params.max_hear_distance = BS*getfloatfield_default(L, index,
                                "max_hear_distance", params.max_hear_distance/BS);
                getboolfield(L, index, "loop", params.loop);
+               getstringfield(L, index, "exclude_player", params.exclude_player);
        }
 }
 
index 7c083e65296d0ee7b47893a2d3e907e5f512c1c7..00e849cdf9b614bd4efc1c76dfed4be0a47862ac 100644 (file)
@@ -429,7 +429,7 @@ int ModApiServer::l_get_worldpath(lua_State *L)
        return 1;
 }
 
-// sound_play(spec, parameters)
+// sound_play(spec, parameters, [ephemeral])
 int ModApiServer::l_sound_play(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
@@ -437,8 +437,14 @@ int ModApiServer::l_sound_play(lua_State *L)
        read_soundspec(L, 1, spec);
        ServerSoundParams params;
        read_server_sound_params(L, 2, params);
-       s32 handle = getServer(L)->playSound(spec, params);
-       lua_pushinteger(L, handle);
+       bool ephemeral = lua_gettop(L) > 2 && readParam<bool>(L, 3);
+       if (ephemeral) {
+               getServer(L)->playSound(spec, params, true);
+               lua_pushnil(L);
+       } else {
+               s32 handle = getServer(L)->playSound(spec, params);
+               lua_pushinteger(L, handle);
+       }
        return 1;
 }
 
@@ -446,7 +452,7 @@ int ModApiServer::l_sound_play(lua_State *L)
 int ModApiServer::l_sound_stop(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
-       int handle = luaL_checkinteger(L, 1);
+       s32 handle = luaL_checkinteger(L, 1);
        getServer(L)->stopSound(handle);
        return 0;
 }
index b74bba258c0dd79368985ca10aa80f61707c382a..f1613cffe4bc11f1d564b1e42b594f3bc0da2a56 100644 (file)
@@ -2013,8 +2013,18 @@ void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
        Send(&pkt);
 }
 
+inline s32 Server::nextSoundId()
+{
+       s32 ret = m_next_sound_id;
+       if (m_next_sound_id == INT32_MAX)
+               m_next_sound_id = 0; // signed overflow is undefined
+       else
+               m_next_sound_id++;
+       return ret;
+}
+
 s32 Server::playSound(const SimpleSoundSpec &spec,
-               const ServerSoundParams &params)
+               const ServerSoundParams &params, bool ephemeral)
 {
        // Find out initial position of sound
        bool pos_exists = false;
@@ -2025,7 +2035,7 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
 
        // Filter destination clients
        std::vector<session_t> dst_clients;
-       if(!params.to_player.empty()) {
+       if (!params.to_player.empty()) {
                RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
                if(!player){
                        infostream<<"Server::playSound: Player \""<<params.to_player
@@ -2045,6 +2055,9 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
                        RemotePlayer *player = m_env->getPlayer(client_id);
                        if (!player)
                                continue;
+                       if (!params.exclude_player.empty() &&
+                                       params.exclude_player == player->getName())
+                               continue;
 
                        PlayerSAO *sao = player->getPlayerSAO();
                        if (!sao)
@@ -2063,27 +2076,32 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
                return -1;
 
        // Create the sound
-       s32 id = m_next_sound_id++;
-       // The sound will exist as a reference in m_playing_sounds
-       m_playing_sounds[id] = ServerPlayingSound();
-       ServerPlayingSound &psound = m_playing_sounds[id];
-       psound.params = params;
-       psound.spec = spec;
+       s32 id;
+       ServerPlayingSound *psound = nullptr;
+       if (ephemeral) {
+               id = -1; // old clients will still use this, so pick a reserved ID
+       } else {
+               id = nextSoundId();
+               // The sound will exist as a reference in m_playing_sounds
+               m_playing_sounds[id] = ServerPlayingSound();
+               psound = &m_playing_sounds[id];
+               psound->params = params;
+               psound->spec = spec;
+       }
 
        float gain = params.gain * spec.gain;
        NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
        pkt << id << spec.name << gain
                        << (u8) params.type << pos << params.object
-                       << params.loop << params.fade << params.pitch;
+                       << params.loop << params.fade << params.pitch
+                       << ephemeral;
 
-       // Backwards compability
-       bool play_sound = gain > 0;
+       bool as_reliable = !ephemeral;
 
        for (const u16 dst_client : dst_clients) {
-               if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
-                       psound.clients.insert(dst_client);
-                       m_clients.send(dst_client, 0, &pkt, true);
-               }
+               if (psound)
+                       psound->clients.insert(dst_client);
+               m_clients.send(dst_client, 0, &pkt, as_reliable);
        }
        return id;
 }
index d61840871b933d6afa9b1cf84f45d4be52f58909..28d9c8fa1e9d5d5c9837051e092b73c30fd66aff 100644 (file)
@@ -98,6 +98,7 @@ struct ServerSoundParams
        v3f pos;
        u16 object = 0;
        std::string to_player = "";
+       std::string exclude_player = "";
 
        v3f getPos(ServerEnvironment *env, bool *pos_exists) const;
 };
@@ -209,7 +210,8 @@ public:
 
        // Returns -1 if failed, sound handle on success
        // Envlock
-       s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params);
+       s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params,
+                       bool ephemeral=false);
        void stopSound(s32 handle);
        void fadeSound(s32 handle, float step, float gain);
 
@@ -646,7 +648,8 @@ private:
                Sounds
        */
        std::unordered_map<s32, ServerPlayingSound> m_playing_sounds;
-       s32 m_next_sound_id = 0;
+       s32 m_next_sound_id = 0; // positive values only
+       s32 nextSoundId();
 
        /*
                Detached inventories (behind m_env_mutex)