Lua API for playing sounds
parent
06e93f8d95
commit
601d1936c9
|
@ -363,8 +363,40 @@ dump2(obj, name="_", dumped={})
|
||||||
dump(obj, dumped={})
|
dump(obj, dumped={})
|
||||||
^ Return object serialized as a string
|
^ 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 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_entity(name, prototype table)
|
||||||
minetest.register_abm(abm definition)
|
minetest.register_abm(abm definition)
|
||||||
minetest.register_node(name, node 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_craftitem(name, item definition)
|
||||||
minetest.register_alias(name, convert_to)
|
minetest.register_alias(name, convert_to)
|
||||||
minetest.register_craft(recipe)
|
minetest.register_craft(recipe)
|
||||||
|
|
||||||
minetest.register_globalstep(func(dtime))
|
minetest.register_globalstep(func(dtime))
|
||||||
minetest.register_on_placenode(func(pos, newnode, placer))
|
minetest.register_on_placenode(func(pos, newnode, placer))
|
||||||
minetest.register_on_dignode(func(pos, oldnode, digger))
|
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
|
^ return true in func to disable regular player placement
|
||||||
^ currently called _before_ repositioning of player occurs
|
^ currently called _before_ repositioning of player occurs
|
||||||
minetest.register_on_chat_message(func(name, message))
|
minetest.register_on_chat_message(func(name, message))
|
||||||
|
|
||||||
minetest.add_to_creative_inventory(itemstring)
|
minetest.add_to_creative_inventory(itemstring)
|
||||||
minetest.setting_get(name) -> string or nil
|
minetest.setting_get(name) -> string or nil
|
||||||
minetest.setting_getbool(name) -> boolean value or nil
|
minetest.setting_getbool(name) -> boolean value or nil
|
||||||
|
|
||||||
minetest.chat_send_all(text)
|
minetest.chat_send_all(text)
|
||||||
minetest.chat_send_player(name, text)
|
minetest.chat_send_player(name, text)
|
||||||
minetest.get_player_privs(name) -> set of privs
|
minetest.get_player_privs(name) -> set of privs
|
||||||
minetest.get_inventory(location) -> InvRef
|
minetest.get_inventory(location) -> InvRef
|
||||||
^ location = eg. {type="player", name="celeron55"}
|
^ location = eg. {type="player", name="celeron55"}
|
||||||
{type="node", pos={x=, y=, z=}}
|
{type="node", pos={x=, y=, z=}}
|
||||||
minetest.get_current_modname() -> string
|
|
||||||
minetest.get_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname"
|
minetest.sound_play(spec, parameters) -> handle
|
||||||
^ Useful for loading additional .lua modules or static data from mod
|
^ spec = SimpleSoundSpec
|
||||||
minetest.get_worldpath(modname) -> eg. "/home/user/.minetest/world"
|
^ parameters = sound parameter table
|
||||||
^ Useful for storing custom data
|
minetest.sound_stop(handle)
|
||||||
|
|
||||||
minetest.debug(line)
|
minetest.debug(line)
|
||||||
^ Goes to dstream
|
^ Goes to dstream
|
||||||
|
@ -681,6 +716,7 @@ Node definition (register_node)
|
||||||
legacy_wallmounted = false, -- Support maps made in and before January 2012
|
legacy_wallmounted = false, -- Support maps made in and before January 2012
|
||||||
sounds = {
|
sounds = {
|
||||||
footstep = <SimpleSoundSpec>,
|
footstep = <SimpleSoundSpec>,
|
||||||
|
dig = <SimpleSoundSpec>, -- "__group" = group-based sound (default)
|
||||||
dug = <SimpleSoundSpec>,
|
dug = <SimpleSoundSpec>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,42 @@
|
||||||
|
|
||||||
experimental = {}
|
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)
|
function on_step(dtime)
|
||||||
-- print("experimental on_step")
|
-- print("experimental on_step")
|
||||||
--[[
|
--[[
|
||||||
|
|
111
src/client.cpp
111
src/client.cpp
|
@ -261,7 +261,8 @@ Client::Client(
|
||||||
m_nodedef_received(false),
|
m_nodedef_received(false),
|
||||||
m_time_of_day_set(false),
|
m_time_of_day_set(false),
|
||||||
m_last_time_of_day_f(-1),
|
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_packetcounter_timer = 0.0;
|
||||||
//m_delete_unused_sectors_timer = 0.0;
|
//m_delete_unused_sectors_timer = 0.0;
|
||||||
|
@ -733,6 +734,63 @@ void Client::step(float dtime)
|
||||||
m_inventory_updated = true;
|
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
|
// 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->deSerialize(tmp_is2);
|
||||||
m_itemdef_received = true;
|
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
|
else
|
||||||
{
|
{
|
||||||
infostream<<"Client: Ignoring unknown command "
|
infostream<<"Client: Ignoring unknown command "
|
||||||
|
|
|
@ -376,6 +376,15 @@ private:
|
||||||
bool m_time_of_day_set;
|
bool m_time_of_day_set;
|
||||||
float m_last_time_of_day_f;
|
float m_last_time_of_day_f;
|
||||||
float m_time_of_day_update_timer;
|
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
|
#endif // !SERVER
|
||||||
|
|
|
@ -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
|
Compress the contents of TOCLIENT_ITEMDEF and TOCLIENT_NODEDEF
|
||||||
PROTOCOL_VERSION 8:
|
PROTOCOL_VERSION 8:
|
||||||
Digging based on item groups
|
Digging based on item groups
|
||||||
|
Many things
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define PROTOCOL_VERSION 8
|
#define PROTOCOL_VERSION 8
|
||||||
|
@ -269,6 +270,24 @@ enum ToClientCommand
|
||||||
serialized ItemDefManager
|
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
|
enum ToServerCommand
|
||||||
|
@ -442,8 +461,14 @@ enum ToServerCommand
|
||||||
(Obsoletes TOSERVER_GROUND_ACTION and TOSERVER_CLICK_ACTIVEOBJECT.)
|
(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 command
|
||||||
u16 number of textures requested
|
u16 number of textures requested
|
||||||
|
|
|
@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "itemdef.h"
|
#include "itemdef.h"
|
||||||
#include "tool.h"
|
#include "tool.h"
|
||||||
#include "content_cso.h"
|
#include "content_cso.h"
|
||||||
|
#include "sound.h"
|
||||||
|
#include "nodedef.h"
|
||||||
class Settings;
|
class Settings;
|
||||||
struct ToolCapabilities;
|
struct ToolCapabilities;
|
||||||
|
|
||||||
|
@ -1008,6 +1010,7 @@ private:
|
||||||
LocalPlayer *m_local_player;
|
LocalPlayer *m_local_player;
|
||||||
float m_damage_visual_timer;
|
float m_damage_visual_timer;
|
||||||
bool m_dead;
|
bool m_dead;
|
||||||
|
float m_step_distance_counter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
|
PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
|
||||||
|
@ -1020,7 +1023,8 @@ public:
|
||||||
m_is_local_player(false),
|
m_is_local_player(false),
|
||||||
m_local_player(NULL),
|
m_local_player(NULL),
|
||||||
m_damage_visual_timer(0),
|
m_damage_visual_timer(0),
|
||||||
m_dead(false)
|
m_dead(false),
|
||||||
|
m_step_distance_counter(0)
|
||||||
{
|
{
|
||||||
if(gamedef == NULL)
|
if(gamedef == NULL)
|
||||||
ClientActiveObject::registerType(getType(), create);
|
ClientActiveObject::registerType(getType(), create);
|
||||||
|
@ -1202,7 +1206,9 @@ public:
|
||||||
|
|
||||||
void step(float dtime, ClientEnvironment *env)
|
void step(float dtime, ClientEnvironment *env)
|
||||||
{
|
{
|
||||||
|
v3f lastpos = pos_translator.vect_show;
|
||||||
pos_translator.translate(dtime);
|
pos_translator.translate(dtime);
|
||||||
|
float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
|
||||||
updateVisibility();
|
updateVisibility();
|
||||||
updateNodePos();
|
updateNodePos();
|
||||||
|
|
||||||
|
@ -1212,6 +1218,18 @@ public:
|
||||||
updateTextures("");
|
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)
|
void processMessage(const std::string &data)
|
||||||
|
|
|
@ -2222,7 +2222,7 @@ private:
|
||||||
|
|
||||||
static const char className[];
|
static const char className[];
|
||||||
static const luaL_reg methods[];
|
static const luaL_reg methods[];
|
||||||
|
public:
|
||||||
static ObjectRef *checkobject(lua_State *L, int narg)
|
static ObjectRef *checkobject(lua_State *L, int narg)
|
||||||
{
|
{
|
||||||
luaL_checktype(L, narg, LUA_TUSERDATA);
|
luaL_checktype(L, narg, LUA_TUSERDATA);
|
||||||
|
@ -2236,7 +2236,7 @@ private:
|
||||||
ServerActiveObject *co = ref->m_object;
|
ServerActiveObject *co = ref->m_object;
|
||||||
return co;
|
return co;
|
||||||
}
|
}
|
||||||
|
private:
|
||||||
static LuaEntitySAO* getluaobject(ObjectRef *ref)
|
static LuaEntitySAO* getluaobject(ObjectRef *ref)
|
||||||
{
|
{
|
||||||
ServerActiveObject *obj = getobject(ref);
|
ServerActiveObject *obj = getobject(ref);
|
||||||
|
@ -3134,10 +3134,6 @@ const luaL_reg EnvRef::methods[] = {
|
||||||
{0,0}
|
{0,0}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
Global functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
class LuaABM : public ActiveBlockModifier
|
class LuaABM : public ActiveBlockModifier
|
||||||
{
|
{
|
||||||
private:
|
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)
|
// debug(text)
|
||||||
// Writes a line to dstream
|
// Writes a line to dstream
|
||||||
static int l_debug(lua_State *L)
|
static int l_debug(lua_State *L)
|
||||||
|
@ -3674,6 +3711,26 @@ static int l_get_worldpath(lua_State *L)
|
||||||
return 1;
|
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 [] = {
|
static const struct luaL_Reg minetest_f [] = {
|
||||||
{"debug", l_debug},
|
{"debug", l_debug},
|
||||||
{"log", l_log},
|
{"log", l_log},
|
||||||
|
@ -3691,6 +3748,8 @@ static const struct luaL_Reg minetest_f [] = {
|
||||||
{"get_current_modname", l_get_current_modname},
|
{"get_current_modname", l_get_current_modname},
|
||||||
{"get_modpath", l_get_modpath},
|
{"get_modpath", l_get_modpath},
|
||||||
{"get_worldpath", l_get_worldpath},
|
{"get_worldpath", l_get_worldpath},
|
||||||
|
{"sound_play", l_sound_play},
|
||||||
|
{"sound_stop", l_sound_stop},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
134
src/server.cpp
134
src/server.cpp
|
@ -3126,6 +3126,24 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
||||||
<<action<<std::endl;
|
<<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
|
else
|
||||||
{
|
{
|
||||||
infostream<<"Server::ProcessData(): Ignoring "
|
infostream<<"Server::ProcessData(): Ignoring "
|
||||||
|
@ -3575,6 +3593,107 @@ void Server::SendMovePlayer(Player *player)
|
||||||
m_con.Send(player->peer_id, 0, data, true);
|
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,
|
void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
|
||||||
core::list<u16> *far_players, float far_d_nodes)
|
core::list<u16> *far_players, float far_d_nodes)
|
||||||
{
|
{
|
||||||
|
@ -4511,6 +4630,21 @@ void Server::handlePeerChange(PeerChange &c)
|
||||||
obj->m_known_by_count--;
|
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 =
|
ServerRemotePlayer* player =
|
||||||
static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
|
static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
|
||||||
|
|
||||||
|
|
64
src/server.h
64
src/server.h
|
@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "mods.h"
|
#include "mods.h"
|
||||||
#include "inventorymanager.h"
|
#include "inventorymanager.h"
|
||||||
#include "subgame.h"
|
#include "subgame.h"
|
||||||
|
#include "sound.h"
|
||||||
struct LuaState;
|
struct LuaState;
|
||||||
typedef struct lua_State lua_State;
|
typedef struct lua_State lua_State;
|
||||||
class IWritableItemDefManager;
|
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
|
class RemoteClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -464,6 +517,11 @@ public:
|
||||||
// Envlock and conlock should be locked when calling this
|
// Envlock and conlock should be locked when calling this
|
||||||
void SendMovePlayer(Player *player);
|
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
|
// Thread-safe
|
||||||
u64 getPlayerAuthPrivs(const std::string &name);
|
u64 getPlayerAuthPrivs(const std::string &name);
|
||||||
void setPlayerAuthPrivs(const std::string &name, u64 privs);
|
void setPlayerAuthPrivs(const std::string &name, u64 privs);
|
||||||
|
@ -775,6 +833,12 @@ private:
|
||||||
friend class RemoteClient;
|
friend class RemoteClient;
|
||||||
|
|
||||||
std::map<std::string,TextureInformation> m_Textures;
|
std::map<std::string,TextureInformation> m_Textures;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sounds
|
||||||
|
*/
|
||||||
|
std::map<s32, ServerPlayingSound> m_playing_sounds;
|
||||||
|
s32 m_next_sound_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -67,6 +67,8 @@ public:
|
||||||
virtual int playSoundAt(const std::string &name, bool loop,
|
virtual int playSoundAt(const std::string &name, bool loop,
|
||||||
float volume, v3f pos) = 0;
|
float volume, v3f pos) = 0;
|
||||||
virtual void stopSound(int sound) = 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)
|
int playSound(const SimpleSoundSpec &spec, bool loop)
|
||||||
{ return playSound(spec.name, loop, spec.gain); }
|
{ return playSound(spec.name, loop, spec.gain); }
|
||||||
|
@ -87,6 +89,8 @@ public:
|
||||||
int playSoundAt(const std::string &name, bool loop,
|
int playSoundAt(const std::string &name, bool loop,
|
||||||
float volume, v3f pos) {return 0;}
|
float volume, v3f pos) {return 0;}
|
||||||
void stopSound(int sound) {}
|
void stopSound(int sound) {}
|
||||||
|
bool soundExists(int sound) {return false;}
|
||||||
|
void updateSoundPosition(int sound, v3f pos) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global DummySoundManager singleton
|
// Global DummySoundManager singleton
|
||||||
|
|
|
@ -482,6 +482,24 @@ public:
|
||||||
maintain();
|
maintain();
|
||||||
deleteSound(sound);
|
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)
|
ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
|
||||||
|
|
Loading…
Reference in New Issue