aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2012-03-24 19:01:26 +0200
committerPerttu Ahola <celeron55@gmail.com>2012-03-24 19:01:26 +0200
commit601d1936c9ab4787d43f55d67900ed7c46fd3452 (patch)
treeb328ea7a66ee7dc37fffddd0ee063a0c7b1ea10f /src
parent06e93f8d9508a23e86751dfae2f6a7e550d8e1af (diff)
downloadminetest-601d1936c9ab4787d43f55d67900ed7c46fd3452.tar.gz
minetest-601d1936c9ab4787d43f55d67900ed7c46fd3452.tar.bz2
minetest-601d1936c9ab4787d43f55d67900ed7c46fd3452.zip
Lua API for playing sounds
Diffstat (limited to 'src')
-rw-r--r--src/client.cpp111
-rw-r--r--src/client.h9
-rw-r--r--src/clientserver.h39
-rw-r--r--src/content_cao.cpp20
-rw-r--r--src/scriptapi.cpp71
-rw-r--r--src/server.cpp134
-rw-r--r--src/server.h64
-rw-r--r--src/sound.h4
-rw-r--r--src/sound_openal.cpp18
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 &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}
};
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 &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));
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 &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;
};
/*
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)