diff options
author | Perttu Ahola <celeron55@gmail.com> | 2012-03-24 19:01:26 +0200 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2012-03-24 19:01:26 +0200 |
commit | 601d1936c9ab4787d43f55d67900ed7c46fd3452 (patch) | |
tree | b328ea7a66ee7dc37fffddd0ee063a0c7b1ea10f /src | |
parent | 06e93f8d9508a23e86751dfae2f6a7e550d8e1af (diff) | |
download | minetest-601d1936c9ab4787d43f55d67900ed7c46fd3452.tar.gz minetest-601d1936c9ab4787d43f55d67900ed7c46fd3452.tar.bz2 minetest-601d1936c9ab4787d43f55d67900ed7c46fd3452.zip |
Lua API for playing sounds
Diffstat (limited to 'src')
-rw-r--r-- | src/client.cpp | 111 | ||||
-rw-r--r-- | src/client.h | 9 | ||||
-rw-r--r-- | src/clientserver.h | 39 | ||||
-rw-r--r-- | src/content_cao.cpp | 20 | ||||
-rw-r--r-- | src/scriptapi.cpp | 71 | ||||
-rw-r--r-- | src/server.cpp | 134 | ||||
-rw-r--r-- | src/server.h | 64 | ||||
-rw-r--r-- | src/sound.h | 4 | ||||
-rw-r--r-- | src/sound_openal.cpp | 18 |
9 files changed, 455 insertions, 15 deletions
diff --git a/src/client.cpp b/src/client.cpp index d8fb4eb77..89070d66b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -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 " diff --git a/src/client.h b/src/client.h index 13b36106c..3a47a08f6 100644 --- a/src/client.h +++ b/src/client.h @@ -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 diff --git a/src/clientserver.h b/src/clientserver.h index b4e9ccfc8..9cbb7a685 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -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 + } */ }; diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 3aba4c7cb..fc1df377a 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -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) diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 5ce5f3b29..3c7b9bb3e 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -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 ¶ms) +{ + 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} }; diff --git a/src/server.cpp b/src/server.cpp index e781f1284..745e55f83 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -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 ¶ms) +{ + // 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)); diff --git a/src/server.h b/src/server.h index 3baeb433d..ae50af15b 100644 --- a/src/server.h +++ b/src/server.h @@ -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 ¶ms); + 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; }; /* diff --git a/src/sound.h b/src/sound.h index 7f6e4141e..f9c1ea0ce 100644 --- a/src/sound.h +++ b/src/sound.h @@ -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 diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index edcb9e8d4..26ad6fa4c 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -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) |