mirror of
https://github.com/minetest/minetest.git
synced 2024-06-30 13:10:33 +02:00
Compare commits
3 Commits
342ed4d665
...
5862cb786e
Author | SHA1 | Date | |
---|---|---|---|
|
5862cb786e | ||
|
7a9479e58b | ||
|
0da16f9937 |
@ -42,6 +42,7 @@ core.features = {
|
||||
node_interaction_actor = true,
|
||||
moveresult_new_pos = true,
|
||||
override_item_remove_fields = true,
|
||||
sounds_updating = true,
|
||||
}
|
||||
|
||||
function core.has_feature(arg)
|
||||
|
@ -1101,6 +1101,11 @@ Table used to specify how a sound is played:
|
||||
|
||||
-- Available since feature `sound_params_start_time`.
|
||||
|
||||
resend_time = 600.0,
|
||||
-- Approximate playback duration (from `start_time` to end) in seconds.
|
||||
-- This is needed to re-send sounds to new players in hearing distance.
|
||||
-- Unused for looped sounds.
|
||||
|
||||
loop = false,
|
||||
-- If true, sound is played in a loop.
|
||||
|
||||
@ -1136,10 +1141,10 @@ Table used to specify how a sound is played:
|
||||
-- Can't be used together with `to_player`.
|
||||
|
||||
max_hear_distance = 32,
|
||||
-- Only play for players that are at most this far away when the sound
|
||||
-- starts playing.
|
||||
-- Only play for players that are at most this far away.
|
||||
-- Needs `pos` or `object` to be set.
|
||||
-- `32` is the default.
|
||||
-- See `sounds_updating`.
|
||||
}
|
||||
```
|
||||
|
||||
@ -5452,6 +5457,9 @@ Utilities
|
||||
moveresult_new_pos = true,
|
||||
-- Allow removing definition fields in `minetest.override_item`
|
||||
override_item_remove_fields = true,
|
||||
-- Sounds can be send to players which comes to hear distance.
|
||||
-- Field `resend_time` added to sound parameters. (5.9.0)
|
||||
sounds_updating = true,
|
||||
}
|
||||
```
|
||||
|
||||
@ -6652,6 +6660,8 @@ Sounds
|
||||
------
|
||||
|
||||
* `minetest.sound_play(spec, parameters, [ephemeral])`: returns a handle
|
||||
* Returned handle is positive number if function sucessufully
|
||||
create sound and `ephernal` if `false`.
|
||||
* `spec` is a `SimpleSoundSpec`
|
||||
* `parameters` is a sound parameter table
|
||||
* `ephemeral` is a boolean (default: false)
|
||||
|
@ -15,6 +15,7 @@ local meta_keys = {
|
||||
"sparam.pitch",
|
||||
"sparam.fade",
|
||||
"sparam.start_time",
|
||||
"sparam.resend_time",
|
||||
"sparam.loop",
|
||||
"sparam.pos",
|
||||
"sparam.object",
|
||||
@ -40,7 +41,8 @@ local function get_all_metadata(meta)
|
||||
gain = meta:get_string("sparam.gain"),
|
||||
pitch = meta:get_string("sparam.pitch"),
|
||||
fade = meta:get_string("sparam.fade"),
|
||||
start_time = meta:get_string("sparam.start_time"),
|
||||
start_time = meta:get_string("sparam.start_time"),
|
||||
resend_time = meta:get_string("sparam.resend_time"),
|
||||
loop = meta:get_string("sparam.loop"),
|
||||
pos = meta:get_string("sparam.pos"),
|
||||
object = meta:get_string("sparam.object"),
|
||||
@ -88,7 +90,7 @@ local function show_formspec(pos, player)
|
||||
|
||||
fs_add([[
|
||||
formspec_version[6]
|
||||
size[14,12]
|
||||
size[14,13]
|
||||
]])
|
||||
|
||||
-- SimpleSoundSpec
|
||||
@ -120,17 +122,19 @@ local function show_formspec(pos, player)
|
||||
field[1.25,1;1,0.75;sparam.pitch;pitch;%s]
|
||||
field[2.50,1;1,0.75;sparam.fade;fade;%s]
|
||||
field[0,2.25;4,0.75;sparam.start_time;start_time;%s]
|
||||
field[0,3.50;4,0.75;sparam.loop;loop;%s]
|
||||
field[0,4.75;4,0.75;sparam.pos;pos;%s]
|
||||
field[0,6.00;4,0.75;sparam.object;object;%s]
|
||||
field[0,7.25;4,0.75;sparam.to_player;to_player;%s]
|
||||
field[0,8.50;4,0.75;sparam.exclude_player;exclude_player;%s]
|
||||
field[0,9.75;4,0.75;sparam.max_hear_distance;max_hear_distance;%s]
|
||||
field[0,3.50;4,0.75;sparam.resend_time;resend_time;%s]
|
||||
field[0,4.75;4,0.75;sparam.loop;loop;%s]
|
||||
field[0,6.00;4,0.75;sparam.pos;pos;%s]
|
||||
field[0,7.25;4,0.75;sparam.object;object;%s]
|
||||
field[0,8.50;4,0.75;sparam.to_player;to_player;%s]
|
||||
field[0,9.75;4,0.75;sparam.exclude_player;exclude_player;%s]
|
||||
field[0,11.00;4,0.75;sparam.max_hear_distance;max_hear_distance;%s]
|
||||
container_end[]
|
||||
field_close_on_enter[sparam.gain;false]
|
||||
field_close_on_enter[sparam.pitch;false]
|
||||
field_close_on_enter[sparam.fade;false]
|
||||
field_close_on_enter[sparam.start_time;false]
|
||||
field_close_on_enter[sparam.resend_time;false]
|
||||
field_close_on_enter[sparam.loop;false]
|
||||
field_close_on_enter[sparam.pos;false]
|
||||
field_close_on_enter[sparam.object;false]
|
||||
@ -139,7 +143,8 @@ local function show_formspec(pos, player)
|
||||
field_close_on_enter[sparam.max_hear_distance;false]
|
||||
tooltip[sparam.object;Get a name with the Branding Iron.]
|
||||
]], F(md.sparam.gain), F(md.sparam.pitch), F(md.sparam.fade),
|
||||
F(md.sparam.start_time), F(md.sparam.loop), F(md.sparam.pos),
|
||||
F(md.sparam.start_time), F(md.sparam.resend_time),
|
||||
F(md.sparam.loop), F(md.sparam.pos),
|
||||
F(md.sparam.object), F(md.sparam.to_player), F(md.sparam.exclude_player),
|
||||
F(md.sparam.max_hear_distance)))
|
||||
|
||||
@ -192,7 +197,7 @@ local function show_formspec(pos, player)
|
||||
|
||||
-- save and quit button
|
||||
fs_add([[
|
||||
button_exit[10.75,11;3,0.75;btn_save_quit;Save & Quit]
|
||||
button_exit[10.75,11.5;3,0.75;btn_save_quit;Save & Quit]
|
||||
]])
|
||||
|
||||
minetest.show_formspec(player:get_player_name(), "soundstuff:jukebox@"..pos:to_string(),
|
||||
@ -216,6 +221,7 @@ minetest.register_node("soundstuff:jukebox", {
|
||||
meta:set_string("sparam.pitch", "")
|
||||
meta:set_string("sparam.fade", "")
|
||||
meta:set_string("sparam.start_time", "")
|
||||
meta:set_string("sparam.resend_time", "")
|
||||
meta:set_string("sparam.loop", "")
|
||||
meta:set_string("sparam.pos", pos:to_string())
|
||||
meta:set_string("sparam.object", "")
|
||||
@ -274,6 +280,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
pitch = tonumber(md.sparam.pitch),
|
||||
fade = tonumber(md.sparam.fade),
|
||||
start_time = tonumber(md.sparam.start_time),
|
||||
resend_time = tonumber(md.sparam.resend_time),
|
||||
loop = minetest.is_yes(md.sparam.loop),
|
||||
pos = vector.from_string(md.sparam.pos),
|
||||
object = testtools.get_branded_object(md.sparam.object),
|
||||
@ -287,9 +294,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
"[soundstuff:jukebox] Playing sound: minetest.sound_play(%s, %s, %s)",
|
||||
string.format("{name=\"%s\", gain=%s, pitch=%s, fade=%s}",
|
||||
sss.name, sss.gain, sss.pitch, sss.fade),
|
||||
string.format("{gain=%s, pitch=%s, fade=%s, start_time=%s, loop=%s, pos=%s, "
|
||||
string.format("{gain=%s, pitch=%s, fade=%s, start_time=%s, resend_time=%s, loop=%s, pos=%s, "
|
||||
.."object=%s, to_player=\"%s\", exclude_player=\"%s\", max_hear_distance=%s}",
|
||||
sparam.gain, sparam.pitch, sparam.fade, sparam.start_time,
|
||||
sparam.gain, sparam.pitch, sparam.fade,
|
||||
sparam.start_time, sparam.resend_time,
|
||||
sparam.loop, sparam.pos, sparam.object and "<objref>",
|
||||
sparam.to_player, sparam.exclude_player,
|
||||
sparam.max_hear_distance),
|
||||
|
@ -1339,10 +1339,39 @@ void Server::handleCommand_RemovedSounds(NetworkPacket* pkt)
|
||||
if (i == m_playing_sounds.end())
|
||||
continue;
|
||||
|
||||
session_t peer_id = pkt->getPeerId();
|
||||
|
||||
ServerPlayingSound &psound = i->second;
|
||||
psound.clients.erase(pkt->getPeerId());
|
||||
if (psound.clients.empty())
|
||||
psound.clients.erase(peer_id);
|
||||
verbosestream << "Server: Sound " << id << " erase peer "
|
||||
<< peer_id << "." << std::endl;
|
||||
if (!psound.allow_resend && psound.clients.empty()) {
|
||||
m_playing_sounds.erase(i);
|
||||
verbosestream << "Server: Erase sound " << id << "." << std::endl;
|
||||
}
|
||||
else if (psound.allow_resend && !psound.spec.loop) {
|
||||
if (psound.resend_time < m_playing_sounds_time) {
|
||||
/* Sound resend time reached, done_clients do not need to be updated. */
|
||||
verbosestream << "Server: Done sound " << id << " for peer "
|
||||
<< peer_id << "." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
PlayerSAO *plrsao = getPlayerSAO(peer_id);
|
||||
if (!plrsao)
|
||||
continue;
|
||||
bool pos_exists;
|
||||
v3f pos = psound.getPos(m_env, &pos_exists);
|
||||
assert(pos_exists);
|
||||
|
||||
if (pos.getDistanceFrom(plrsao->getBasePosition()) <= psound.max_hear_distance) {
|
||||
/* If client remove sound in hear distance,
|
||||
we are sure, that sound has been fully played. */
|
||||
psound.done_clients.insert(peer_id);
|
||||
verbosestream << "Server: Done sound " << id << " for peer "
|
||||
<< peer_id << "." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1179,6 +1179,7 @@ void read_server_sound_params(lua_State *L, int index,
|
||||
getfloatfield(L, index, "fade", params.spec.fade);
|
||||
getfloatfield(L, index, "pitch", params.spec.pitch);
|
||||
getfloatfield(L, index, "start_time", params.spec.start_time);
|
||||
getfloatfield(L, index, "resend_time", params.spec.resend_time);
|
||||
getboolfield(L, index, "loop", params.spec.loop);
|
||||
|
||||
getfloatfield(L, index, "gain", params.gain);
|
||||
|
127
src/server.cpp
127
src/server.cpp
@ -915,6 +915,65 @@ void Server::AsyncRunStep(float dtime, bool initial_step)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Send sounds if needed
|
||||
*/
|
||||
{
|
||||
m_playing_sounds_time += dtime;
|
||||
|
||||
MutexAutoLock lock(m_env_mutex);
|
||||
|
||||
ClientInterface::AutoLock clientlock(m_clients);
|
||||
const RemoteClientMap &clients = m_clients.getClientList();
|
||||
|
||||
for (auto it = m_playing_sounds.begin(); it != m_playing_sounds.end(); it++) {
|
||||
ServerPlayingSound &sound = it->second;
|
||||
|
||||
if (!sound.allow_resend)
|
||||
continue;
|
||||
|
||||
if (!sound.spec.loop && (sound.resend_time < m_playing_sounds_time)) {
|
||||
sound.allow_resend = false;
|
||||
verbosestream << "Server: Marking sound " << it->first
|
||||
<< " as expired." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// this should be never called for sound not attached to pos or object
|
||||
bool pos_exists;
|
||||
v3f pos = sound.getPos(m_env, &pos_exists);
|
||||
assert(pos_exists);
|
||||
|
||||
for (const auto &client_it : clients) {
|
||||
RemoteClient *client = client_it.second;
|
||||
|
||||
if (client->getState() < CS_DefinitionsSent)
|
||||
continue;
|
||||
|
||||
PlayerSAO *playersao = getPlayerSAO(client->peer_id);
|
||||
if (!playersao)
|
||||
continue;
|
||||
|
||||
if (sound.clients.find(client->peer_id) != sound.clients.end())
|
||||
continue;
|
||||
|
||||
if (sound.done_clients.find(client->peer_id) != sound.clients.end())
|
||||
continue;
|
||||
|
||||
if (!sound.exclude_player.empty() &&
|
||||
sound.exclude_player == client->getName())
|
||||
continue;
|
||||
|
||||
if (pos.getDistanceFrom(playersao->getBasePosition()) <= sound.max_hear_distance) {
|
||||
SendSound(client->peer_id, it->first, sound, pos);
|
||||
sound.clients.insert(client->peer_id);
|
||||
verbosestream << "Server: Sound " << it->first << " add peer "
|
||||
<< client->peer_id << "." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Send queued-for-sending map edit events.
|
||||
*/
|
||||
@ -2118,6 +2177,15 @@ void Server::SendActiveObjectMessages(session_t peer_id, const std::string &data
|
||||
m_clients.sendCustom(pkt.getPeerId(), reliable ? ccf.channel : 1, &pkt, reliable);
|
||||
}
|
||||
|
||||
void Server::SendSound(session_t peer_id, s32 sound_id,
|
||||
const ServerPlayingSound ¶ms, const v3f &pos)
|
||||
{
|
||||
NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0, peer_id);
|
||||
createSoundPacket(pkt, sound_id, params, pos);
|
||||
|
||||
Send(&pkt);
|
||||
}
|
||||
|
||||
void Server::SendCSMRestrictionFlags(session_t peer_id)
|
||||
{
|
||||
NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
|
||||
@ -2159,6 +2227,8 @@ s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
|
||||
if(pos_exists != (params.type != SoundLocation::Local))
|
||||
return -1;
|
||||
|
||||
params.allow_resend = pos_exists;
|
||||
|
||||
// Filter destination clients
|
||||
std::vector<session_t> dst_clients;
|
||||
if (!params.to_player.empty()) {
|
||||
@ -2169,6 +2239,7 @@ s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
|
||||
return -1;
|
||||
}
|
||||
dst_clients.push_back(player->getPeerId());
|
||||
params.allow_resend = false;
|
||||
} else {
|
||||
std::vector<session_t> clients = m_clients.getClientIDs();
|
||||
|
||||
@ -2193,7 +2264,10 @@ s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
|
||||
}
|
||||
}
|
||||
|
||||
if(dst_clients.empty())
|
||||
// lets finish only for ephmeral sounds, non-looped sounds and
|
||||
// sounds without resend_time if no clients in hear distance.
|
||||
if (dst_clients.empty() &&
|
||||
(ephemeral || (params.spec.resend_time == 0 && !params.spec.loop)))
|
||||
return -1;
|
||||
|
||||
// old clients will still use this, so pick a reserved ID (-1)
|
||||
@ -2201,23 +2275,27 @@ s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
|
||||
if (id == 0)
|
||||
return 0;
|
||||
|
||||
float gain = params.gain * params.spec.gain;
|
||||
NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
|
||||
pkt << id << params.spec.name << gain
|
||||
<< (u8) params.type << pos << params.object
|
||||
<< params.spec.loop << params.spec.fade << params.spec.pitch
|
||||
<< ephemeral << params.spec.start_time;
|
||||
params.start_time = m_playing_sounds_time;
|
||||
params.resend_time = m_playing_sounds_time + params.spec.resend_time;
|
||||
|
||||
const bool as_reliable = !ephemeral;
|
||||
if (!dst_clients.empty()) {
|
||||
NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
|
||||
createSoundPacket(pkt, id, params, pos, ephemeral);
|
||||
|
||||
for (const session_t peer_id : dst_clients) {
|
||||
if (!ephemeral)
|
||||
params.clients.insert(peer_id);
|
||||
m_clients.sendCustom(peer_id, 0, &pkt, as_reliable);
|
||||
const bool as_reliable = !ephemeral;
|
||||
|
||||
for (const session_t peer_id : dst_clients) {
|
||||
if (!ephemeral)
|
||||
params.clients.insert(peer_id);
|
||||
m_clients.sendCustom(peer_id, 0, &pkt, as_reliable);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ephemeral)
|
||||
if (!ephemeral) {
|
||||
m_playing_sounds[id] = std::move(params);
|
||||
verbosestream << "Server:playSound: Create sound "
|
||||
<< id << "." << std::endl;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
void Server::stopSound(s32 handle)
|
||||
@ -2237,6 +2315,9 @@ void Server::stopSound(s32 handle)
|
||||
|
||||
// Remove sound reference
|
||||
m_playing_sounds.erase(it);
|
||||
|
||||
verbosestream << "Server:stopSound: Stop sound "
|
||||
<< handle << "." << std::endl;
|
||||
}
|
||||
|
||||
void Server::fadeSound(s32 handle, float step, float gain)
|
||||
@ -2280,17 +2361,33 @@ void Server::stopAttachedSounds(session_t peer_id,
|
||||
|
||||
sound.clients.erase(clients_it);
|
||||
// delete if client list empty
|
||||
return sound.clients.empty();
|
||||
return !sound.allow_resend && sound.clients.empty();
|
||||
};
|
||||
|
||||
for (auto it = m_playing_sounds.begin(); it != m_playing_sounds.end(); ) {
|
||||
if (cb(it->first, it->second))
|
||||
if (cb(it->first, it->second)) {
|
||||
// Remove sound reference
|
||||
verbosestream << "Server:stopAttachedSounds: Stop sound "
|
||||
<< it->first << "." << std::endl;
|
||||
it = m_playing_sounds.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::createSoundPacket(NetworkPacket &pkt, s32 sound_id,
|
||||
const ServerPlayingSound ¶ms, const v3f &pos, bool ephemeral)
|
||||
{
|
||||
float gain = params.gain * params.spec.gain;
|
||||
float start_time = params.spec.start_time +
|
||||
m_playing_sounds_time - params.start_time;
|
||||
pkt << sound_id << params.spec.name << gain
|
||||
<< (u8) params.type << pos << params.object
|
||||
<< params.spec.loop << params.spec.fade << params.spec.pitch
|
||||
<< ephemeral << start_time;
|
||||
}
|
||||
|
||||
void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
|
||||
float far_d_nodes)
|
||||
{
|
||||
|
13
src/server.h
13
src/server.h
@ -119,6 +119,16 @@ struct ServerPlayingSound
|
||||
|
||||
SoundSpec spec;
|
||||
|
||||
// server processing values
|
||||
|
||||
// relative server time when sound has been added
|
||||
// used for update spec.start_time when sending sound to new client
|
||||
float start_time;
|
||||
// hold time limit for sound sending to new clients
|
||||
float resend_time;
|
||||
bool allow_resend = false;
|
||||
|
||||
std::unordered_set<session_t> done_clients; // peer ids
|
||||
std::unordered_set<session_t> clients; // peer ids
|
||||
};
|
||||
|
||||
@ -242,6 +252,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
// Stop all sounds attached to given objects, for a certain client
|
||||
void stopAttachedSounds(session_t peer_id,
|
||||
const std::vector<u16> &object_ids);
|
||||
void createSoundPacket(NetworkPacket &pkt, s32 sound_id, const ServerPlayingSound ¶ms, const v3f &pos, bool ephemeral = false);
|
||||
|
||||
// Envlock
|
||||
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
|
||||
@ -568,6 +579,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
void SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao);
|
||||
void SendActiveObjectMessages(session_t peer_id, const std::string &datas,
|
||||
bool reliable = true);
|
||||
void SendSound(session_t peer_id, s32 sound_id, const ServerPlayingSound ¶ms, const v3f &pos);
|
||||
void SendCSMRestrictionFlags(session_t peer_id);
|
||||
|
||||
/*
|
||||
@ -733,6 +745,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
|
||||
std::unordered_map<s32, ServerPlayingSound> m_playing_sounds;
|
||||
s32 m_playing_sounds_id_last_used = 0; // positive values only
|
||||
s32 nextSoundId();
|
||||
float m_playing_sounds_time = 0.0f;
|
||||
|
||||
ModStorageDatabase *m_mod_storage_database = nullptr;
|
||||
float m_mod_storage_save_timer = 10.0f;
|
||||
|
@ -71,6 +71,9 @@ struct SoundSpec
|
||||
float fade = 0.0f;
|
||||
float pitch = 1.0f;
|
||||
float start_time = 0.0f;
|
||||
// keep time defines a time windows, where sound cannot be removed
|
||||
// and can be resend, takes no effect for looped sounds
|
||||
float resend_time = 600.0f;
|
||||
bool loop = false;
|
||||
// If true, a local fallback (ie. from the user's sound pack) is used if the
|
||||
// sound-group does not exist.
|
||||
|
Loading…
Reference in New Issue
Block a user