Lua API for playing sounds
authorPerttu Ahola <celeron55@gmail.com>
Sat, 24 Mar 2012 17:01:26 +0000 (19:01 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Sat, 24 Mar 2012 17:01:26 +0000 (19:01 +0200)
doc/lua_api.txt
games/mesetint/mods/experimental/init.lua
src/client.cpp
src/client.h
src/clientserver.h
src/content_cao.cpp
src/scriptapi.cpp
src/server.cpp
src/server.h
src/sound.h
src/sound_openal.cpp

index 53857ee3c535505e4b0f3a2dbcd2a6e2818076aa..43f49dde6620fe86a6f0a6196acf73b05d368ba4 100644 (file)
@@ -363,8 +363,40 @@ dump2(obj, name="_", dumped={})
 dump(obj, dumped={})
 ^ Return object serialized as a string
 
+Sounds
+-------
+Examples of sound parameter tables:
+-- Play locationless on all clients
+{
+       gain = 1.0, -- default
+}
+-- Play locationless to a player
+{
+       to_player = name,
+       gain = 1.0, -- default
+}
+-- Play in a location
+{
+       pos = {x=1,y=2,z=3},
+       gain = 1.0, -- default
+       max_hear_distance = 32, -- default
+}
+-- Play connected to an object, looped
+{
+    object = <an ObjectRef>,
+       gain = 1.0, -- default
+       max_hear_distance = 32, -- default
+    loop = true, -- only sounds connected to objects can be looped
+}
+
 minetest namespace reference
 -----------------------------
+minetest.get_current_modname() -> string
+minetest.get_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname"
+^ Useful for loading additional .lua modules or static data from mod
+minetest.get_worldpath(modname) -> eg. "/home/user/.minetest/world"
+^ Useful for storing custom data
+
 minetest.register_entity(name, prototype table)
 minetest.register_abm(abm definition)
 minetest.register_node(name, node definition)
@@ -372,6 +404,7 @@ minetest.register_tool(name, item definition)
 minetest.register_craftitem(name, item definition)
 minetest.register_alias(name, convert_to)
 minetest.register_craft(recipe)
+
 minetest.register_globalstep(func(dtime))
 minetest.register_on_placenode(func(pos, newnode, placer))
 minetest.register_on_dignode(func(pos, oldnode, digger))
@@ -383,20 +416,22 @@ minetest.register_on_respawnplayer(func(ObjectRef))
 ^ return true in func to disable regular player placement
 ^ currently called _before_ repositioning of player occurs
 minetest.register_on_chat_message(func(name, message))
+
 minetest.add_to_creative_inventory(itemstring)
 minetest.setting_get(name) -> string or nil
 minetest.setting_getbool(name) -> boolean value or nil
+
 minetest.chat_send_all(text)
 minetest.chat_send_player(name, text)
 minetest.get_player_privs(name) -> set of privs
 minetest.get_inventory(location) -> InvRef
 ^ location = eg. {type="player", name="celeron55"}
                  {type="node", pos={x=, y=, z=}}
-minetest.get_current_modname() -> string
-minetest.get_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname"
-^ Useful for loading additional .lua modules or static data from mod
-minetest.get_worldpath(modname) -> eg. "/home/user/.minetest/world"
-^ Useful for storing custom data
+
+minetest.sound_play(spec, parameters) -> handle
+^ spec = SimpleSoundSpec
+^ parameters = sound parameter table
+minetest.sound_stop(handle)
 
 minetest.debug(line)
 ^ Goes to dstream
@@ -681,6 +716,7 @@ Node definition (register_node)
     legacy_wallmounted = false, -- Support maps made in and before January 2012
     sounds = {
         footstep = <SimpleSoundSpec>,
+        dig = <SimpleSoundSpec>, -- "__group" = group-based sound (default)
         dug = <SimpleSoundSpec>,
     },
 }
index 364eeb10c12f93379f1ef479e1453669fc4a1f33..e11086274b90d9594895d007165329fc0d49e69c 100644 (file)
@@ -6,6 +6,42 @@
 
 experimental = {}
 
+timers_to_add = {}
+timers = {}
+minetest.register_globalstep(function(dtime)
+  for indes, timer in ipairs(timers_to_add) do
+    table.insert(timers, timer)
+  end
+  timers_to_add = {}
+  for index, timer in ipairs(timers) do
+    timer.time = timer.time - dtime
+    if timer.time <= 0 then
+      timer.func()
+      timers[index] = nil
+    end
+  end
+end)
+
+after = function(time, func)
+  table.insert(timers_to_add, {time=time, func=func})
+end
+
+--[[
+stepsound = -1
+function test_sound()
+       print("test_sound")
+       stepsound = minetest.sound_play("default_grass_footstep", {gain=1.0})
+       after(2.0, test_sound)
+       --after(0.1, test_sound_stop)
+end
+function test_sound_stop()
+       print("test_sound_stop")
+       minetest.sound_stop(stepsound)
+       after(2.0, test_sound)
+end
+test_sound()
+--]]
+
 function on_step(dtime)
        -- print("experimental on_step")
        --[[
index d8fb4eb772e23dc8932151280ed700ac62496550..89070d66bf481f03fd8b3a9bc33ebbea0edc1bf3 100644 (file)
@@ -261,7 +261,8 @@ Client::Client(
        m_nodedef_received(false),
        m_time_of_day_set(false),
        m_last_time_of_day_f(-1),
-       m_time_of_day_update_timer(0)
+       m_time_of_day_update_timer(0),
+       m_removed_sounds_check_timer(0)
 {
        m_packetcounter_timer = 0.0;
        //m_delete_unused_sectors_timer = 0.0;
@@ -733,6 +734,63 @@ void Client::step(float dtime)
                        m_inventory_updated = true;
                }
        }
+
+       /*
+               Update positions of sounds attached to objects
+       */
+       {
+               for(std::map<int, u16>::iterator
+                               i = m_sounds_to_objects.begin();
+                               i != m_sounds_to_objects.end(); i++)
+               {
+                       int client_id = i->first;
+                       u16 object_id = i->second;
+                       ClientActiveObject *cao = m_env.getActiveObject(object_id);
+                       if(!cao)
+                               continue;
+                       v3f pos = cao->getPosition();
+                       m_sound->updateSoundPosition(client_id, pos);
+               }
+       }
+       
+       /*
+               Handle removed remotely initiated sounds
+       */
+       m_removed_sounds_check_timer += dtime;
+       if(m_removed_sounds_check_timer >= 2.32)
+       {
+               m_removed_sounds_check_timer = 0;
+               // Find removed sounds and clear references to them
+               std::set<s32> removed_server_ids;
+               for(std::map<s32, int>::iterator
+                               i = m_sounds_server_to_client.begin();
+                               i != m_sounds_server_to_client.end();)
+               {
+                       s32 server_id = i->first;
+                       int client_id = i->second;
+                       i++;
+                       if(!m_sound->soundExists(client_id)){
+                               m_sounds_server_to_client.erase(server_id);
+                               m_sounds_client_to_server.erase(client_id);
+                               m_sounds_to_objects.erase(client_id);
+                               removed_server_ids.insert(server_id);
+                       }
+               }
+               // Sync to server
+               if(removed_server_ids.size() != 0)
+               {
+                       std::ostringstream os(std::ios_base::binary);
+                       writeU16(os, TOSERVER_REMOVED_SOUNDS);
+                       writeU16(os, removed_server_ids.size());
+                       for(std::set<s32>::iterator i = removed_server_ids.begin();
+                                       i != removed_server_ids.end(); i++)
+                               writeS32(os, *i);
+                       std::string s = os.str();
+                       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+                       // Send as reliable
+                       Send(0, data, true);
+               }
+       }
 }
 
 // Virtual methods from con::PeerHandler
@@ -1610,6 +1668,57 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                m_itemdef->deSerialize(tmp_is2);
                m_itemdef_received = true;
        }
+       else if(command == TOCLIENT_PLAY_SOUND)
+       {
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               s32 server_id = readS32(is);
+               std::string name = deSerializeString(is);
+               float gain = readF1000(is);
+               int type = readU8(is); // 0=local, 1=positional, 2=object
+               v3f pos = readV3F1000(is);
+               u16 object_id = readU16(is);
+               bool loop = readU8(is);
+               // Start playing
+               int client_id = -1;
+               switch(type){
+               case 0: // local
+                       client_id = m_sound->playSound(name, false, gain);
+                       break;
+               case 1: // positional
+                       client_id = m_sound->playSoundAt(name, false, gain, pos);
+                       break;
+               case 2: { // object
+                       ClientActiveObject *cao = m_env.getActiveObject(object_id);
+                       if(cao)
+                               pos = cao->getPosition();
+                       client_id = m_sound->playSoundAt(name, loop, gain, pos);
+                       // TODO: Set up sound to move with object
+                       break; }
+               default:
+                       break;
+               }
+               if(client_id != -1){
+                       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;
+               }
+       }
+       else if(command == TOCLIENT_STOP_SOUND)
+       {
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               s32 server_id = readS32(is);
+               std::map<s32, int>::iterator i =
+                               m_sounds_server_to_client.find(server_id);
+               if(i != m_sounds_server_to_client.end()){
+                       int client_id = i->second;
+                       m_sound->stopSound(client_id);
+               }
+       }
        else
        {
                infostream<<"Client: Ignoring unknown command "
index 13b36106c9f18e606874a34d9fc4580ad5a16b09..3a47a08f6810c792776b5461381932c44a7c7c1c 100644 (file)
@@ -376,6 +376,15 @@ private:
        bool m_time_of_day_set;
        float m_last_time_of_day_f;
        float m_time_of_day_update_timer;
+
+       // Sounds
+       float m_removed_sounds_check_timer;
+       // Mapping from server sound ids to our sound ids
+       std::map<s32, int> m_sounds_server_to_client;
+       // And the other way!
+       std::map<int, s32> m_sounds_client_to_server;
+       // And relations to objects
+       std::map<int, u16> m_sounds_to_objects;
 };
 
 #endif // !SERVER
index b4e9ccfc8931fc3e4ced1132eaf7db859ef481d7..9cbb7a68593532a9c0cba7496aa8ad603f345afe 100644 (file)
@@ -46,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
                Compress the contents of TOCLIENT_ITEMDEF and TOCLIENT_NODEDEF
        PROTOCOL_VERSION 8:
                Digging based on item groups
+               Many things
 */
 
 #define PROTOCOL_VERSION 8
@@ -268,7 +269,25 @@ enum ToClientCommand
                u32 length of next item
                serialized ItemDefManager
        */
+       
+       TOCLIENT_PLAY_SOUND = 0x3f,
+       /*
+               u16 command
+               s32 sound_id
+               u16 len
+               u8[len] sound name
+               s32 gain*1000
+               u8 type (0=local, 1=positional, 2=object)
+               s32[3] pos_nodes*10000
+               u16 object_id
+               u8 loop (bool)
+       */
 
+       TOCLIENT_STOP_SOUND = 0x40,
+       /*
+               u16 command
+               s32 sound_id
+       */
 };
 
 enum ToServerCommand
@@ -442,15 +461,21 @@ enum ToServerCommand
                (Obsoletes TOSERVER_GROUND_ACTION and TOSERVER_CLICK_ACTIVEOBJECT.)
        */
        
-       TOSERVER_REQUEST_TEXTURES = 0x40,
+       TOSERVER_REMOVED_SOUNDS = 0x3a,
+       /*
+               u16 command
+               u16 len
+               s32[len] sound_id
+       */
 
+       TOSERVER_REQUEST_TEXTURES = 0x40,
        /*
-                       u16 command
-                       u16 number of textures requested
-                       for each texture {
-                               u16 length of name
-                               string name
-                       }
+               u16 command
+               u16 number of textures requested
+               for each texture {
+                       u16 length of name
+                       string name
+               }
         */
 
 };
index 3aba4c7cb2cd94afb45a7fed76d4e8ac8c2f95f0..fc1df377a7875624a56156ca2df98741f78b5f34 100644 (file)
@@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "itemdef.h"
 #include "tool.h"
 #include "content_cso.h"
+#include "sound.h"
+#include "nodedef.h"
 class Settings;
 struct ToolCapabilities;
 
@@ -1008,6 +1010,7 @@ private:
        LocalPlayer *m_local_player;
        float m_damage_visual_timer;
        bool m_dead;
+       float m_step_distance_counter;
 
 public:
        PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
@@ -1020,7 +1023,8 @@ public:
                m_is_local_player(false),
                m_local_player(NULL),
                m_damage_visual_timer(0),
-               m_dead(false)
+               m_dead(false),
+               m_step_distance_counter(0)
        {
                if(gamedef == NULL)
                        ClientActiveObject::registerType(getType(), create);
@@ -1202,7 +1206,9 @@ public:
 
        void step(float dtime, ClientEnvironment *env)
        {
+               v3f lastpos = pos_translator.vect_show;
                pos_translator.translate(dtime);
+               float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
                updateVisibility();
                updateNodePos();
 
@@ -1212,6 +1218,18 @@ public:
                                updateTextures("");
                        }
                }
+               
+               m_step_distance_counter += moved;
+               if(m_step_distance_counter > 1.5*BS){
+                       m_step_distance_counter = 0;
+                       if(!m_is_local_player){
+                               INodeDefManager *ndef = m_gamedef->ndef();
+                               v3s16 p = floatToInt(getPosition()+v3f(0,-0.5*BS, 0), BS);
+                               MapNode n = m_env->getMap().getNodeNoEx(p);
+                               SimpleSoundSpec spec = ndef->get(n).sound_footstep;
+                               m_gamedef->sound()->playSoundAt(spec, false, getPosition());
+                       }
+               }
        }
 
        void processMessage(const std::string &data)
index 5ce5f3b2953ec969321b461b0026a2c6c836f504..3c7b9bb3eed1c4c88938051a763a0c77a29bd87a 100644 (file)
@@ -2222,7 +2222,7 @@ private:
 
        static const char className[];
        static const luaL_reg methods[];
-
+public:
        static ObjectRef *checkobject(lua_State *L, int narg)
        {
                luaL_checktype(L, narg, LUA_TUSERDATA);
@@ -2236,7 +2236,7 @@ private:
                ServerActiveObject *co = ref->m_object;
                return co;
        }
-       
+private:
        static LuaEntitySAO* getluaobject(ObjectRef *ref)
        {
                ServerActiveObject *obj = getobject(ref);
@@ -3134,10 +3134,6 @@ const luaL_reg EnvRef::methods[] = {
        {0,0}
 };
 
-/*
-       Global functions
-*/
-
 class LuaABM : public ActiveBlockModifier
 {
 private:
@@ -3211,6 +3207,47 @@ public:
        }
 };
 
+/*
+       ServerSoundParams
+*/
+
+static void read_server_sound_params(lua_State *L, int index,
+               ServerSoundParams &params)
+{
+       if(index < 0)
+               index = lua_gettop(L) + 1 + index;
+       // Clear
+       params = ServerSoundParams();
+       if(lua_istable(L, index)){
+               getfloatfield(L, index, "gain", params.gain);
+               getstringfield(L, index, "to_player", params.to_player);
+               lua_getfield(L, index, "pos");
+               if(!lua_isnil(L, -1)){
+                       v3f p = read_v3f(L, -1)*BS;
+                       params.pos = p;
+                       params.type = ServerSoundParams::SSP_POSITIONAL;
+               }
+               lua_pop(L, 1);
+               lua_getfield(L, index, "object");
+               if(!lua_isnil(L, -1)){
+                       ObjectRef *ref = ObjectRef::checkobject(L, -1);
+                       ServerActiveObject *sao = ObjectRef::getobject(ref);
+                       if(sao){
+                               params.object = sao->getId();
+                               params.type = ServerSoundParams::SSP_OBJECT;
+                       }
+               }
+               lua_pop(L, 1);
+               params.max_hear_distance = BS*getfloatfield_default(L, index,
+                               "max_hear_distance", params.max_hear_distance/BS);
+               getboolfield(L, index, "loop", params.loop);
+       }
+}
+
+/*
+       Global functions
+*/
+
 // debug(text)
 // Writes a line to dstream
 static int l_debug(lua_State *L)
@@ -3674,6 +3711,26 @@ static int l_get_worldpath(lua_State *L)
        return 1;
 }
 
+// sound_play(spec, parameters)
+static int l_sound_play(lua_State *L)
+{
+       SimpleSoundSpec spec;
+       read_soundspec(L, 1, spec);
+       ServerSoundParams params;
+       read_server_sound_params(L, 2, params);
+       s32 handle = get_server(L)->playSound(spec, params);
+       lua_pushinteger(L, handle);
+       return 1;
+}
+
+// sound_stop(handle)
+static int l_sound_stop(lua_State *L)
+{
+       int handle = luaL_checkinteger(L, 1);
+       get_server(L)->stopSound(handle);
+       return 0;
+}
+
 static const struct luaL_Reg minetest_f [] = {
        {"debug", l_debug},
        {"log", l_log},
@@ -3691,6 +3748,8 @@ static const struct luaL_Reg minetest_f [] = {
        {"get_current_modname", l_get_current_modname},
        {"get_modpath", l_get_modpath},
        {"get_worldpath", l_get_worldpath},
+       {"sound_play", l_sound_play},
+       {"sound_stop", l_sound_stop},
        {NULL, NULL}
 };
 
index e781f1284984c6151edef2ddb51695d0ec60d45d..745e55f831cf3231d90d789c202c2194c8d04993 100644 (file)
@@ -3126,6 +3126,24 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        <<action<<std::endl;
                }
        }
+       else if(command == TOSERVER_REMOVED_SOUNDS)
+       {
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               int num = readU16(is);
+               for(int k=0; k<num; k++){
+                       s32 id = readS32(is);
+                       std::map<s32, ServerPlayingSound>::iterator i =
+                                       m_playing_sounds.find(id);
+                       if(i == m_playing_sounds.end())
+                               continue;
+                       ServerPlayingSound &psound = i->second;
+                       psound.clients.erase(peer_id);
+                       if(psound.clients.size() == 0)
+                               m_playing_sounds.erase(i++);
+               }
+       }
        else
        {
                infostream<<"Server::ProcessData(): Ignoring "
@@ -3575,6 +3593,107 @@ void Server::SendMovePlayer(Player *player)
        m_con.Send(player->peer_id, 0, data, true);
 }
 
+s32 Server::playSound(const SimpleSoundSpec &spec,
+               const ServerSoundParams &params)
+{
+       // Find out initial position of sound
+       bool pos_exists = false;
+       v3f pos = params.getPos(m_env, &pos_exists);
+       // If position is not found while it should be, cancel sound
+       if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
+               return -1;
+       // Filter destination clients
+       std::set<RemoteClient*> dst_clients;
+       if(params.to_player != "")
+       {
+               Player *player = m_env->getPlayer(params.to_player.c_str());
+               if(!player){
+                       infostream<<"Server::playSound: Player \""<<params.to_player
+                                       <<"\" not found"<<std::endl;
+                       return -1;
+               }
+               if(player->peer_id == PEER_ID_INEXISTENT){
+                       infostream<<"Server::playSound: Player \""<<params.to_player
+                                       <<"\" not connected"<<std::endl;
+                       return -1;
+               }
+               RemoteClient *client = getClient(player->peer_id);
+               dst_clients.insert(client);
+       }
+       else
+       {
+               for(core::map<u16, RemoteClient*>::Iterator
+                               i = m_clients.getIterator(); i.atEnd() == false; i++)
+               {
+                       RemoteClient *client = i.getNode()->getValue();
+                       Player *player = m_env->getPlayer(client->peer_id);
+                       if(!player)
+                               continue;
+                       if(pos_exists){
+                               if(player->getPosition().getDistanceFrom(pos) >
+                                               params.max_hear_distance)
+                                       continue;
+                       }
+                       dst_clients.insert(client);
+               }
+       }
+       if(dst_clients.size() == 0)
+               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;
+       for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
+                       i != dst_clients.end(); i++)
+               psound.clients.insert((*i)->peer_id);
+       // Create packet
+       std::ostringstream os(std::ios_base::binary);
+       writeU16(os, TOCLIENT_PLAY_SOUND);
+       writeS32(os, id);
+       os<<serializeString(spec.name);
+       writeF1000(os, spec.gain * params.gain);
+       writeU8(os, params.type);
+       writeV3F1000(os, pos);
+       writeU16(os, params.object);
+       writeU8(os, params.loop);
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send
+       for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
+                       i != dst_clients.end(); i++){
+               // Send as reliable
+               m_con.Send((*i)->peer_id, 0, data, true);
+       }
+       return id;
+}
+void Server::stopSound(s32 handle)
+{
+       // Get sound reference
+       std::map<s32, ServerPlayingSound>::iterator i =
+                       m_playing_sounds.find(handle);
+       if(i == m_playing_sounds.end())
+               return;
+       ServerPlayingSound &psound = i->second;
+       // Create packet
+       std::ostringstream os(std::ios_base::binary);
+       writeU16(os, TOCLIENT_STOP_SOUND);
+       writeS32(os, handle);
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send
+       for(std::set<u16>::iterator i = psound.clients.begin();
+                       i != psound.clients.end(); i++){
+               // Send as reliable
+               m_con.Send(*i, 0, data, true);
+       }
+       // Remove sound reference
+       m_playing_sounds.erase(i);
+}
+
 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
        core::list<u16> *far_players, float far_d_nodes)
 {
@@ -4511,6 +4630,21 @@ void Server::handlePeerChange(PeerChange &c)
                                obj->m_known_by_count--;
                }
 
+               /*
+                       Clear references to playing sounds
+               */
+               for(std::map<s32, ServerPlayingSound>::iterator
+                               i = m_playing_sounds.begin();
+                               i != m_playing_sounds.end();)
+               {
+                       ServerPlayingSound &psound = i->second;
+                       psound.clients.erase(c.peer_id);
+                       if(psound.clients.size() == 0)
+                               m_playing_sounds.erase(i++);
+                       else
+                               i++;
+               }
+
                ServerRemotePlayer* player =
                                static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
 
index 3baeb433df261079ab811f5c3b909789085bf523..ae50af15b7ce1906bfc28dd39ff7ced52743a81a 100644 (file)
@@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mods.h"
 #include "inventorymanager.h"
 #include "subgame.h"
+#include "sound.h"
 struct LuaState;
 typedef struct lua_State lua_State;
 class IWritableItemDefManager;
@@ -274,6 +275,58 @@ struct TextureInformation
        }
 };
 
+struct ServerSoundParams
+{
+       float gain;
+       std::string to_player;
+       enum Type{
+               SSP_LOCAL=0,
+               SSP_POSITIONAL=1,
+               SSP_OBJECT=2
+       } type;
+       v3f pos;
+       u16 object;
+       float max_hear_distance;
+       bool loop;
+
+       ServerSoundParams():
+               gain(1.0),
+               to_player(""),
+               type(SSP_LOCAL),
+               pos(0,0,0),
+               object(0),
+               max_hear_distance(32*BS),
+               loop(false)
+       {}
+       
+       v3f getPos(ServerEnvironment *env, bool *pos_exists) const
+       {
+               if(pos_exists) *pos_exists = false;
+               switch(type){
+               case SSP_LOCAL:
+                       return v3f(0,0,0);
+               case SSP_POSITIONAL:
+                       if(pos_exists) *pos_exists = true;
+                       return pos;
+               case SSP_OBJECT: {
+                       if(object == 0)
+                               return v3f(0,0,0);
+                       ServerActiveObject *sao = env->getActiveObject(object);
+                       if(!sao)
+                               return v3f(0,0,0);
+                       if(pos_exists) *pos_exists = true;
+                       return sao->getBasePosition(); }
+               }
+               return v3f(0,0,0);
+       }
+};
+
+struct ServerPlayingSound
+{
+       ServerSoundParams params;
+       std::set<u16> clients; // peer ids
+};
+
 class RemoteClient
 {
 public:
@@ -464,6 +517,11 @@ public:
        // Envlock and conlock should be locked when calling this
        void SendMovePlayer(Player *player);
        
+       // Returns -1 if failed, sound handle on success
+       // Envlock + conlock
+       s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params);
+       void stopSound(s32 handle);
+       
        // Thread-safe
        u64 getPlayerAuthPrivs(const std::string &name);
        void setPlayerAuthPrivs(const std::string &name, u64 privs);
@@ -775,6 +833,12 @@ private:
        friend class RemoteClient;
 
        std::map<std::string,TextureInformation> m_Textures;
+
+       /*
+               Sounds
+       */
+       std::map<s32, ServerPlayingSound> m_playing_sounds;
+       s32 m_next_sound_id;
 };
 
 /*
index 7f6e4141ef68e6b6c593fb6139b464f04e4ed21f..f9c1ea0ce948a9c844e69a706846ec6965385d68 100644 (file)
@@ -67,6 +67,8 @@ public:
        virtual int playSoundAt(const std::string &name, bool loop,
                        float volume, v3f pos) = 0;
        virtual void stopSound(int sound) = 0;
+       virtual bool soundExists(int sound) = 0;
+       virtual void updateSoundPosition(int sound, v3f pos) = 0;
 
        int playSound(const SimpleSoundSpec &spec, bool loop)
                { return playSound(spec.name, loop, spec.gain); }
@@ -87,6 +89,8 @@ public:
        int playSoundAt(const std::string &name, bool loop,
                        float volume, v3f pos) {return 0;}
        void stopSound(int sound) {}
+       bool soundExists(int sound) {return false;}
+       void updateSoundPosition(int sound, v3f pos) {}
 };
 
 // Global DummySoundManager singleton
index edcb9e8d4e91ea1e644487d45fe543dc817862b3..26ad6fa4ce2ffb12d995b1a2be11e75aaae180a1 100644 (file)
@@ -482,6 +482,24 @@ public:
                maintain();
                deleteSound(sound);
        }
+       bool soundExists(int sound)
+       {
+               maintain();
+               return (m_sounds_playing.count(sound) != 0);
+       }
+       void updateSoundPosition(int id, v3f pos)
+       {
+               std::map<int, PlayingSound*>::iterator i =
+                               m_sounds_playing.find(id);
+               if(i == m_sounds_playing.end())
+                       return;
+               PlayingSound *sound = i->second;
+
+               alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
+               alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
+               alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
+               alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
+       }
 };
 
 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)