Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ChannelListenerManager.cpp86
-rw-r--r--src/ChannelListenerManager.h44
-rw-r--r--src/Mumble.proto7
-rw-r--r--src/mumble/AudioOutput.cpp17
-rw-r--r--src/mumble/CMakeLists.txt4
-rw-r--r--src/mumble/ListenerLocalVolumeSlider.cpp30
-rw-r--r--src/mumble/ListenerVolumeSlider.cpp56
-rw-r--r--src/mumble/ListenerVolumeSlider.h (renamed from src/mumble/ListenerLocalVolumeSlider.h)7
-rw-r--r--src/mumble/MainWindow.cpp8
-rw-r--r--src/mumble/MainWindow.h4
-rw-r--r--src/mumble/Messages.cpp12
-rw-r--r--src/mumble/TalkingUI.cpp26
-rw-r--r--src/mumble/UserModel.cpp4
-rw-r--r--src/mumble/VolumeSliderWidgetAction.h2
-rw-r--r--src/mumble/main.cpp7
-rw-r--r--src/murmur/Messages.cpp103
-rw-r--r--src/murmur/Meta.cpp4
-rw-r--r--src/murmur/Meta.h2
-rw-r--r--src/murmur/MumbleServer.ice14
-rw-r--r--src/murmur/MumbleServerI.h6
-rw-r--r--src/murmur/MumbleServerIce.cpp22
-rw-r--r--src/murmur/RPC.cpp46
-rw-r--r--src/murmur/Server.cpp163
-rw-r--r--src/murmur/Server.h18
-rw-r--r--src/murmur/ServerDB.cpp165
-rw-r--r--src/murmur/ServerDB.h2
-rw-r--r--src/murmur/ServerUser.h2
27 files changed, 678 insertions, 183 deletions
diff --git a/src/ChannelListenerManager.cpp b/src/ChannelListenerManager.cpp
index 4954bb0ff..7151d90c1 100644
--- a/src/ChannelListenerManager.cpp
+++ b/src/ChannelListenerManager.cpp
@@ -10,13 +10,17 @@
#include <QReadLocker>
#include <QWriteLocker>
+std::size_t qHash(const ChannelListener &listener) {
+ return std::hash< ChannelListener >()(listener);
+};
+
+bool operator==(const ChannelListener &lhs, const ChannelListener &rhs) {
+ return lhs.channelID == rhs.channelID && lhs.userSession == rhs.userSession;
+}
+
ChannelListenerManager::ChannelListenerManager()
- : QObject(nullptr), m_listenerLock(), m_listeningUsers(), m_listenedChannels()
-#ifdef MUMBLE
- ,
- m_volumeLock(), m_listenerVolumeAdjustments()
-#endif
-{
+ : QObject(nullptr), m_listenerLock(), m_listeningUsers(), m_listenedChannels(), m_volumeLock(),
+ m_listenerVolumeAdjustments() {
}
void ChannelListenerManager::addListener(unsigned int userSession, int channelID) {
@@ -75,49 +79,69 @@ int ChannelListenerManager::getListenedChannelCountForUser(unsigned int userSess
return m_listeningUsers[userSession].size();
}
-#ifdef MUMBLE
-void ChannelListenerManager::setListenerLocalVolumeAdjustment(int channelID, float volumeAdjustment) {
- float oldValue;
+void ChannelListenerManager::setListenerVolumeAdjustment(unsigned int userSession, int channelID,
+ const VolumeAdjustment &volumeAdjustment) {
+ float oldValue = 1.0f;
{
QWriteLocker lock(&m_volumeLock);
- oldValue = m_listenerVolumeAdjustments.value(channelID, 1.0f);
- m_listenerVolumeAdjustments.insert(channelID, volumeAdjustment);
+ ChannelListener key = {};
+ key.channelID = channelID;
+ key.userSession = userSession;
+
+ auto it = m_listenerVolumeAdjustments.find(key);
+ if (it != m_listenerVolumeAdjustments.end()) {
+ oldValue = it->second.factor;
+ }
+
+ m_listenerVolumeAdjustments[key] = volumeAdjustment;
}
- if (oldValue != volumeAdjustment) {
- emit localVolumeAdjustmentsChanged(channelID, volumeAdjustment, oldValue);
+ if (oldValue != volumeAdjustment.factor) {
+ emit localVolumeAdjustmentsChanged(channelID, volumeAdjustment.factor, oldValue);
}
}
-float ChannelListenerManager::getListenerLocalVolumeAdjustment(int channelID) const {
+const VolumeAdjustment &ChannelListenerManager::getListenerVolumeAdjustment(unsigned int userSession,
+ int channelID) const {
+ static VolumeAdjustment fallbackObj = VolumeAdjustment::fromFactor(1.0f);
+
QReadLocker lock(&m_volumeLock);
- return m_listenerVolumeAdjustments.value(channelID, 1.0f);
-}
+ ChannelListener key = {};
+ key.channelID = channelID;
+ key.userSession = userSession;
-QHash< int, float > ChannelListenerManager::getAllListenerLocalVolumeAdjustments(bool filter) const {
- QReadLocker lock(&m_volumeLock);
+ auto it = m_listenerVolumeAdjustments.find(key);
- if (!filter) {
- return m_listenerVolumeAdjustments;
+ if (it == m_listenerVolumeAdjustments.end()) {
+ return fallbackObj;
} else {
- QHash< int, float > volumeMap;
+ return it->second;
+ }
+}
- QHashIterator< int, float > it(m_listenerVolumeAdjustments);
+std::unordered_map< int, VolumeAdjustment >
+ ChannelListenerManager::getAllListenerVolumeAdjustments(unsigned int userSession) const {
+ QReadLocker lock1(&m_volumeLock);
+ QReadLocker lock2(&m_listenerLock);
- while (it.hasNext()) {
- it.next();
+ std::unordered_map< int, VolumeAdjustment > adjustments;
- if (it.value() != 1.0f) {
- volumeMap.insert(it.key(), it.value());
- }
- }
+ for (int channelID : m_listeningUsers.value(userSession)) {
+ ChannelListener listener = {};
+ listener.channelID = channelID;
+ listener.userSession = userSession;
- return volumeMap;
+ auto it = m_listenerVolumeAdjustments.find(listener);
+
+ if (it != m_listenerVolumeAdjustments.end() && it->second.factor != 1.0f) {
+ adjustments[channelID] = it->second;
+ }
}
+
+ return adjustments;
}
-#endif
void ChannelListenerManager::clear() {
{
@@ -125,10 +149,8 @@ void ChannelListenerManager::clear() {
m_listeningUsers.clear();
m_listenedChannels.clear();
}
-#ifdef MUMBLE
{
QWriteLocker lock(&m_volumeLock);
m_listenerVolumeAdjustments.clear();
}
-#endif
}
diff --git a/src/ChannelListenerManager.h b/src/ChannelListenerManager.h
index a7beaf685..2cf401ea8 100644
--- a/src/ChannelListenerManager.h
+++ b/src/ChannelListenerManager.h
@@ -6,16 +6,35 @@
#ifndef MUMBLE_CHANNELLISTENERMANAGER_H_
#define MUMBLE_CHANNELLISTENERMANAGER_H_
+#include "VolumeAdjustment.h"
+
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <QtCore/QReadWriteLock>
#include <QtCore/QSet>
-#include <atomic>
+#include <unordered_map>
class User;
class Channel;
+struct ChannelListener {
+ /// The session ID of the owning user
+ unsigned int userSession;
+ /// The ID of the channel this listener is placed in
+ int channelID;
+};
+
+// Make ChannelListener hashable and comparable
+template<> struct std::hash< ChannelListener > {
+ std::size_t operator()(const ChannelListener &val) const {
+ return std::hash< unsigned int >()(val.userSession) ^ (std::hash< int >()(val.channelID) << 2);
+ }
+};
+std::size_t qHash(const ChannelListener &listener);
+bool operator==(const ChannelListener &lhs, const ChannelListener &rhs);
+
+
/// This class serves as a namespace for storing information about ChannelListeners. This is a feature
/// that allows a user to listen to a channel without being in it. Kinda similar to linked channels
/// except that this is something each user can do individually.
@@ -31,13 +50,11 @@ protected:
QHash< unsigned int, QSet< int > > m_listeningUsers;
/// A map between a channel's ID and a list of all user-sessions of users listening to that channel
QHash< int, QSet< unsigned int > > m_listenedChannels;
-#ifdef MUMBLE
/// A lock for guarding m_listenerVolumeAdjustments
mutable QReadWriteLock m_volumeLock;
/// A map between channel IDs and local volume adjustments to be made for ChannelListeners
/// in that channel
- QHash< int, float > m_listenerVolumeAdjustments;
-#endif
+ std::unordered_map< ChannelListener, VolumeAdjustment > m_listenerVolumeAdjustments;
public:
/// Constructor
@@ -84,29 +101,26 @@ public:
/// @returns The amount of channels the given user is listening to
int getListenedChannelCountForUser(unsigned int userSession) const;
-#ifdef MUMBLE
- /// Sets the local volume adjustment for any channelListener in the given channel.
+ /// Sets the volume adjustment for the channelListener of the given user in the given channel.
///
+ /// @param userSession The session ID of the user
/// @param channelID The ID of the channel
/// @param volumeAdjustment The volume adjustment to apply
- void setListenerLocalVolumeAdjustment(int channelID, float volumeAdjustment);
+ void setListenerVolumeAdjustment(unsigned int userSession, int channelID, const VolumeAdjustment &volumeAdjustment);
+ /// @param userSession The session ID of the user
/// @param channelID The ID of the channel
- /// @param The local volume adjustment for the given channel. If none has been set,
- /// 1.0f is being returned.
- float getListenerLocalVolumeAdjustment(int channelID) const;
+ /// @returns The volume adjustment for the listener of the given user in the given channel.
+ const VolumeAdjustment &getListenerVolumeAdjustment(unsigned int userSession, int channelID) const;
- /// @param filter Whether to filter out adjustments of 1 (which have no effect)
+ /// @param userSession The session ID of the user whose listener's volume adjustments to obtain
/// @returns A map between channel IDs and the currently set volume adjustment
- QHash< int, float > getAllListenerLocalVolumeAdjustments(bool filter = false) const;
-#endif
+ std::unordered_map< int, VolumeAdjustment > getAllListenerVolumeAdjustments(unsigned int userSession) const;
/// Clears all ChannelListeners and volume adjustments
void clear();
signals:
-#ifdef MUMBLE
void localVolumeAdjustmentsChanged(int channelID, float newAdjustment, float oldAdjustment);
-#endif
};
#endif // MUMBLE_CHANNELLISTENERMANAGER_H_
diff --git a/src/Mumble.proto b/src/Mumble.proto
index 8f8b7c9a4..8561e21c5 100644
--- a/src/Mumble.proto
+++ b/src/Mumble.proto
@@ -179,6 +179,11 @@ message UserRemove {
// First seen during login procedure. May be sent by the client when it wishes
// to alter its state.
message UserState {
+ message VolumeAdjustment {
+ optional uint32 listening_channel = 1;
+ optional float volume_adjustment = 2;
+ }
+
// Unique user session ID of the user whose state this is, may change on
// reconnect.
optional uint32 session = 1;
@@ -230,6 +235,8 @@ message UserState {
repeated uint32 listening_channel_add = 21;
// a list of channels the user does no longer want to listen to.
repeated uint32 listening_channel_remove = 22;
+ // A list of volume adjustments the user has applied to listeners
+ repeated VolumeAdjustment listening_volume_adjustment = 23;
}
// Relays information on the bans. The client may send the BanList message to
diff --git a/src/mumble/AudioOutput.cpp b/src/mumble/AudioOutput.cpp
index 07a5d7dae..838e0b367 100644
--- a/src/mumble/AudioOutput.cpp
+++ b/src/mumble/AudioOutput.cpp
@@ -506,14 +506,21 @@ bool AudioOutput::mix(void *outbuff, unsigned int frameCount) {
volumeAdjustment *= user->getLocalVolumeAdjustments();
- if (user->cChannel
- && Global::get().channelListenerManager->isListening(Global::get().uiSession, user->cChannel->iId)
- && (speech->m_audioContext == Mumble::Protocol::AudioContext::LISTEN)) {
+ if (sh && sh->m_version >= Mumble::Protocol::PROTOBUF_INTRODUCTION_VERSION) {
+ // The new protocol supports sending volume adjustments which is used to figure out the correct
+ // volume adjustment for listeners on the server. Thus, we only have to apply that here.
+ volumeAdjustment *= speech->m_suggestedVolumeAdjustment;
+ } else if (user->cChannel
+ && Global::get().channelListenerManager->isListening(Global::get().uiSession,
+ user->cChannel->iId)
+ && (speech->m_audioContext == Mumble::Protocol::AudioContext::LISTEN)) {
// We are receiving this audio packet only because we are listening to the channel
// the speaking user is in. Thus we receive the audio via our "listener proxy".
// Thus we'll apply the volume adjustment for our listener proxy as well
- volumeAdjustment *=
- Global::get().channelListenerManager->getListenerLocalVolumeAdjustment(user->cChannel->iId);
+ volumeAdjustment *= Global::get()
+ .channelListenerManager
+ ->getListenerVolumeAdjustment(Global::get().uiSession, user->cChannel->iId)
+ .factor;
}
if (prioritySpeakerActive) {
diff --git a/src/mumble/CMakeLists.txt b/src/mumble/CMakeLists.txt
index 585f09f47..018a1d3a1 100644
--- a/src/mumble/CMakeLists.txt
+++ b/src/mumble/CMakeLists.txt
@@ -161,8 +161,8 @@ set(MUMBLE_SOURCES
"LCD.ui"
"LegacyPlugin.cpp"
"LegacyPlugin.h"
- "ListenerLocalVolumeSlider.cpp"
- "ListenerLocalVolumeSlider.h"
+ "ListenerVolumeSlider.cpp"
+ "ListenerVolumeSlider.h"
"Log.cpp"
"Log.h"
"Log.ui"
diff --git a/src/mumble/ListenerLocalVolumeSlider.cpp b/src/mumble/ListenerLocalVolumeSlider.cpp
deleted file mode 100644
index f3ccc0897..000000000
--- a/src/mumble/ListenerLocalVolumeSlider.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2022 The Mumble Developers. All rights reserved.
-// Use of this source code is governed by a BSD-style license
-// that can be found in the LICENSE file at the root of the
-// Mumble source tree or at <https://www.mumble.info/LICENSE>.
-
-#include "ListenerLocalVolumeSlider.h"
-#include "Channel.h"
-#include "ChannelListenerManager.h"
-#include "VolumeAdjustment.h"
-#include "Global.h"
-
-ListenerLocalVolumeSlider::ListenerLocalVolumeSlider(QWidget *parent) : VolumeSliderWidgetAction(parent) {
-}
-
-void ListenerLocalVolumeSlider::setListenedChannel(const Channel &channel) {
- m_channel = &channel;
-
- float initialAdjustment = Global::get().channelListenerManager->getListenerLocalVolumeAdjustment(m_channel->iId);
- updateSliderValue(initialAdjustment);
-}
-
-void ListenerLocalVolumeSlider::on_VolumeSlider_valueChanged(int value) {
- updateTooltip(value);
- displayTooltip(value);
-
- if (m_channel) {
- float factor = VolumeAdjustment::toFactor(value);
- Global::get().channelListenerManager->setListenerLocalVolumeAdjustment(m_channel->iId, factor);
- }
-}
diff --git a/src/mumble/ListenerVolumeSlider.cpp b/src/mumble/ListenerVolumeSlider.cpp
new file mode 100644
index 000000000..4fab69286
--- /dev/null
+++ b/src/mumble/ListenerVolumeSlider.cpp
@@ -0,0 +1,56 @@
+// Copyright 2022 The Mumble Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file at the root of the
+// Mumble source tree or at <https://www.mumble.info/LICENSE>.
+
+#include "ListenerVolumeSlider.h"
+#include "Channel.h"
+#include "ChannelListenerManager.h"
+#include "ServerHandler.h"
+#include "VolumeAdjustment.h"
+#include "Global.h"
+
+ListenerVolumeSlider::ListenerVolumeSlider(QWidget *parent) : VolumeSliderWidgetAction(parent) {
+}
+
+void ListenerVolumeSlider::setListenedChannel(const Channel &channel) {
+ m_channel = &channel;
+
+ float initialAdjustment =
+ Global::get()
+ .channelListenerManager->getListenerVolumeAdjustment(Global::get().uiSession, m_channel->iId)
+ .factor;
+ updateSliderValue(initialAdjustment);
+}
+
+void ListenerVolumeSlider::on_VolumeSlider_valueChanged(int value) {
+ updateTooltip(value);
+ displayTooltip(value);
+}
+
+void ListenerVolumeSlider::on_VolumeSlider_sliderReleased() {
+ ServerHandlerPtr handler = Global::get().sh;
+
+ if (!handler || !m_channel || !m_volumeSlider) {
+ return;
+ }
+
+ VolumeAdjustment adjustment = VolumeAdjustment::fromDBAdjustment(m_volumeSlider->value());
+
+ if (handler->m_version >= Mumble::Protocol::PROTOBUF_INTRODUCTION_VERSION) {
+ // With the new audio protocol, volume adjustments for listeners are handled on the server and thus we want
+ // to avoid spamming updates to the adjustments, which is why we only update them once the slider is released.
+ MumbleProto::UserState mpus;
+ mpus.set_session(Global::get().uiSession);
+
+ MumbleProto::UserState::VolumeAdjustment *adjustmentMsg = mpus.add_listening_volume_adjustment();
+ adjustmentMsg->set_listening_channel(m_channel->iId);
+ adjustmentMsg->set_volume_adjustment(adjustment.factor);
+
+ handler->sendMessage(mpus);
+ } else {
+ // Before the new audio protocol, volume adjustments for listeners are handled locally
+ Global::get().channelListenerManager->setListenerVolumeAdjustment(Global::get().uiSession, m_channel->iId,
+ adjustment);
+ }
+}
diff --git a/src/mumble/ListenerLocalVolumeSlider.h b/src/mumble/ListenerVolumeSlider.h
index fb9ff539e..b41294419 100644
--- a/src/mumble/ListenerLocalVolumeSlider.h
+++ b/src/mumble/ListenerVolumeSlider.h
@@ -10,11 +10,11 @@
class Channel;
-class ListenerLocalVolumeSlider : public VolumeSliderWidgetAction {
+class ListenerVolumeSlider : public VolumeSliderWidgetAction {
Q_OBJECT
public:
- ListenerLocalVolumeSlider(QWidget *parent = nullptr);
+ ListenerVolumeSlider(QWidget *parent = nullptr);
/// Must be called before adding this object as an action
void setListenedChannel(const Channel &channel);
@@ -24,7 +24,8 @@ private:
const Channel *m_channel;
private slots:
- void on_VolumeSlider_valueChanged(int value);
+ void on_VolumeSlider_valueChanged(int value) override;
+ void on_VolumeSlider_sliderReleased() override;
};
#endif
diff --git a/src/mumble/MainWindow.cpp b/src/mumble/MainWindow.cpp
index 02db85f04..4ba554ec7 100644
--- a/src/mumble/MainWindow.cpp
+++ b/src/mumble/MainWindow.cpp
@@ -29,7 +29,7 @@
#endif
#include "../SignalCurry.h"
#include "ChannelListenerManager.h"
-#include "ListenerLocalVolumeSlider.h"
+#include "ListenerVolumeSlider.h"
#include "Markdown.h"
#include "MenuLabel.h"
#include "PTTButtonWidget.h"
@@ -98,7 +98,7 @@ OpenURLEvent::OpenURLEvent(QUrl u) : QEvent(static_cast< QEvent::Type >(OU_QEVEN
MainWindow::MainWindow(QWidget *p)
: QMainWindow(p), m_localVolumeLabel(make_qt_unique< MenuLabel >(tr("Local Volume Adjustment:"), this)),
m_userLocalVolumeSlider(make_qt_unique< UserLocalVolumeSlider >(this)),
- m_listenerLocalVolumeSlider(make_qt_unique< ListenerLocalVolumeSlider >(this)) {
+ m_listenerVolumeSlider(make_qt_unique< ListenerVolumeSlider >(this)) {
SvgIcon::addSvgPixmapsToIcon(qiIconMuteSelf, QLatin1String("skin:muted_self.svg"));
SvgIcon::addSvgPixmapsToIcon(qiIconMuteServer, QLatin1String("skin:muted_server.svg"));
SvgIcon::addSvgPixmapsToIcon(qiIconMuteSuppressed, QLatin1String("skin:muted_suppressed.svg"));
@@ -1759,8 +1759,8 @@ void MainWindow::qmListener_aboutToShow() {
qmListener->addAction(m_localVolumeLabel.get());
Channel *channel = getContextMenuChannel();
if (channel) {
- m_listenerLocalVolumeSlider->setListenedChannel(*channel);
- qmListener->addAction(m_listenerLocalVolumeSlider.get());
+ m_listenerVolumeSlider->setListenedChannel(*channel);
+ qmListener->addAction(m_listenerVolumeSlider.get());
qmListener->addSeparator();
}
diff --git a/src/mumble/MainWindow.h b/src/mumble/MainWindow.h
index 880b33239..2390b8bbd 100644
--- a/src/mumble/MainWindow.h
+++ b/src/mumble/MainWindow.h
@@ -44,7 +44,7 @@ class SearchDialog;
};
class MenuLabel;
-class ListenerLocalVolumeSlider;
+class ListenerVolumeSlider;
class UserLocalVolumeSlider;
struct ShortcutTarget;
@@ -183,7 +183,7 @@ protected:
qt_unique_ptr< MenuLabel > m_localVolumeLabel;
qt_unique_ptr< UserLocalVolumeSlider > m_userLocalVolumeSlider;
- qt_unique_ptr< ListenerLocalVolumeSlider > m_listenerLocalVolumeSlider;
+ qt_unique_ptr< ListenerVolumeSlider > m_listenerVolumeSlider;
void createActions();
void setupGui();
diff --git a/src/mumble/Messages.cpp b/src/mumble/Messages.cpp
index cf4a4e819..5881029ca 100644
--- a/src/mumble/Messages.cpp
+++ b/src/mumble/Messages.cpp
@@ -500,6 +500,18 @@ void MainWindow::msgUserState(const MumbleProto::UserState &msg) {
Global::get().l->log(Log::ChannelListeningRemove, logMsg);
}
}
+ for (int i = 0; i < msg.listening_volume_adjustment_size(); i++) {
+ int channelID = msg.listening_volume_adjustment(i).listening_channel();
+ float adjustment = msg.listening_volume_adjustment(i).volume_adjustment();
+
+ const Channel *channel = Channel::get(channelID);
+ if (channel && pSelf && pSelf->uiSession == pDst->uiSession) {
+ Global::get().channelListenerManager->setListenerVolumeAdjustment(pDst->uiSession, channel->iId,
+ VolumeAdjustment::fromFactor(adjustment));
+ } else if (!channel) {
+ qWarning("msgUserState(): Invalid channel ID encountered in volume adjustment");
+ }
+ }
if (msg.has_name()) {
QString oldName = pDst->qsName;
diff --git a/src/mumble/TalkingUI.cpp b/src/mumble/TalkingUI.cpp
index 7f00b328c..7c7d3119d 100644
--- a/src/mumble/TalkingUI.cpp
+++ b/src/mumble/TalkingUI.cpp
@@ -31,6 +31,7 @@
#include <QtGui/QPixmap>
#include <algorithm>
+#include <cassert>
TalkingUI::TalkingUI(QWidget *parent) : QWidget(parent), m_containers(), m_currentSelection(nullptr) {
setupUI();
@@ -666,13 +667,36 @@ void TalkingUI::on_mainWindowSelectionChanged(const QModelIndex &current, const
}
void TalkingUI::on_serverSynchronized() {
+ ClientUser *self = ClientUser::get(Global::get().uiSession);
+
+ assert(self);
+
if (Global::get().s.bTalkingUI_LocalUserStaysVisible) {
// According to the settings the local user should always be visible and as we
// can't count on it to change its talking state right after it has connected to
// a server, we have to add it manually.
- ClientUser *self = ClientUser::get(Global::get().uiSession);
findOrAddUser(self);
}
+
+ // The client may have received add listener messages for the user before the
+ // sync was complete. So we do this to ensure that they appear in the
+ // TalkingUI. Removing all listeners is probably not necessary but could
+ // prevent duplicates appearing in the case of a race.
+ removeAllListeners();
+ if (Global::get().s.bTalkingUI_ShowLocalListeners) {
+ if (self) {
+ const QSet< int > channels =
+ Global::get().channelListenerManager->getListenedChannelsForUser(self->uiSession);
+
+ for (int currentChannelID : channels) {
+ const Channel *channel = Channel::get(currentChannelID);
+
+ if (channel) {
+ addListener(self, channel);
+ }
+ }
+ }
+ }
}
void TalkingUI::on_serverDisconnected() {
diff --git a/src/mumble/UserModel.cpp b/src/mumble/UserModel.cpp
index d39d2f830..0145f3b72 100644
--- a/src/mumble/UserModel.cpp
+++ b/src/mumble/UserModel.cpp
@@ -1971,7 +1971,9 @@ QString UserModel::createDisplayString(const ClientUser &user, bool isChannelLis
if (parentChannel && user.uiSession == Global::get().uiSession) {
// Only the listener of the local user can have a volume adjustment
volumeAdjustment =
- Global::get().channelListenerManager->getListenerLocalVolumeAdjustment(parentChannel->iId);
+ Global::get()
+ .channelListenerManager->getListenerVolumeAdjustment(user.uiSession, parentChannel->iId)
+ .factor;
}
} else {
volumeAdjustment = user.getLocalVolumeAdjustments();
diff --git a/src/mumble/VolumeSliderWidgetAction.h b/src/mumble/VolumeSliderWidgetAction.h
index e661427bb..3a53c3566 100644
--- a/src/mumble/VolumeSliderWidgetAction.h
+++ b/src/mumble/VolumeSliderWidgetAction.h
@@ -26,7 +26,7 @@ protected:
void updateTooltip(int value);
protected slots:
- virtual void on_VolumeSlider_valueChanged(int value) = 0;
+ virtual void on_VolumeSlider_valueChanged(int){};
virtual void on_VolumeSlider_sliderReleased(){};
};
diff --git a/src/mumble/main.cpp b/src/mumble/main.cpp
index 90b8e5edc..8d76c92b7 100644
--- a/src/mumble/main.cpp
+++ b/src/mumble/main.cpp
@@ -701,6 +701,13 @@ int main(int argc, char **argv) {
QObject::connect(Global::get().channelListenerManager.get(), &ChannelListenerManager::localVolumeAdjustmentsChanged,
Global::get().talkingUI, &TalkingUI::on_channelListenerLocalVolumeAdjustmentChanged);
+ QObject::connect(Global::get().mw, &MainWindow::userAddedChannelListener, Global::get().talkingUI,
+ &TalkingUI::on_channelListenerAdded);
+ QObject::connect(Global::get().mw, &MainWindow::userRemovedChannelListener, Global::get().talkingUI,
+ &TalkingUI::on_channelListenerRemoved);
+ QObject::connect(Global::get().channelListenerManager.get(), &ChannelListenerManager::localVolumeAdjustmentsChanged,
+ Global::get().talkingUI, &TalkingUI::on_channelListenerLocalVolumeAdjustmentChanged);
+
QObject::connect(Global::get().mw, &MainWindow::serverSynchronized, Global::get().talkingUI,
&TalkingUI::on_serverSynchronized);
diff --git a/src/murmur/Messages.cpp b/src/murmur/Messages.cpp
index 486a22946..329d99c68 100644
--- a/src/murmur/Messages.cpp
+++ b/src/murmur/Messages.cpp
@@ -5,6 +5,7 @@
#include "ACL.h"
#include "Channel.h"
+#include "ChannelListenerManager.h"
#include "Connection.h"
#include "Group.h"
#include "Meta.h"
@@ -22,6 +23,7 @@
#include <QtCore/QtEndian>
#include <cassert>
+#include <unordered_map>
#include <Tracy.hpp>
@@ -390,6 +392,8 @@ void Server::msgAuthenticate(ServerUser *uSource, MumbleProto::Authenticate &msg
}
}
+ loadChannelListenersOf(*uSource);
+
// Transmit user profile
MumbleProto::UserState mpus;
@@ -483,8 +487,16 @@ void Server::msgAuthenticate(ServerUser *uSource, MumbleProto::Authenticate &msg
if (!u->qsHash.isEmpty())
mpus.set_hash(u8(u->qsHash));
- foreach (int channelID, m_channelListenerManager.getListenedChannelsForUser(u->uiSession)) {
+
+ for (int channelID : m_channelListenerManager.getListenedChannelsForUser(u->uiSession)) {
mpus.add_listening_channel_add(channelID);
+
+ if (broadcastListenerVolumeAdjustments) {
+ VolumeAdjustment volume = m_channelListenerManager.getListenerVolumeAdjustment(u->uiSession, channelID);
+ MumbleProto::UserState::VolumeAdjustment *adjustment = mpus.add_listening_volume_adjustment();
+ adjustment->set_listening_channel(channelID);
+ adjustment->set_volume_adjustment(volume.factor);
+ }
}
sendMessage(uSource, mpus);
@@ -507,6 +519,39 @@ void Server::msgAuthenticate(ServerUser *uSource, MumbleProto::Authenticate &msg
sendMessage(uSource, mpss);
+ // Transmit user's listeners - this has to be done AFTER the server-sync message has been sent to uSource as the
+ // client may require its own session ID for processing the listeners properly.
+ mpus.Clear();
+ mpus.set_session(uSource->uiSession);
+ for (int channelID : m_channelListenerManager.getListenedChannelsForUser(uSource->uiSession)) {
+ mpus.add_listening_channel_add(channelID);
+ }
+
+ // If we are not intending to broadcast the volume adjustments to everyone, we have to send the message to all but
+ // uSource without the volume adjustments. Then append the adjustments, but only send them to uSource. If we are in
+ // fact broadcasting, just append the adjustments and send to everyone.
+ if (!broadcastListenerVolumeAdjustments && mpus.listening_channel_add_size() > 0) {
+ sendExcept(uSource, mpus, Version::fromComponents(1, 2, 2), Version::CompareMode::AtLeast);
+ }
+
+ std::unordered_map< int, VolumeAdjustment > volumeAdjustments =
+ m_channelListenerManager.getAllListenerVolumeAdjustments(uSource->uiSession);
+ for (auto it = volumeAdjustments.begin(); it != volumeAdjustments.end(); ++it) {
+ MumbleProto::UserState::VolumeAdjustment *adjustment = mpus.add_listening_volume_adjustment();
+ adjustment->set_listening_channel(it->first);
+ adjustment->set_volume_adjustment(it->second.factor);
+ }
+
+ if (mpus.listening_channel_add_size() > 0 || mpus.listening_volume_adjustment_size() > 0) {
+ if (!broadcastListenerVolumeAdjustments) {
+ if (uSource->m_version >= Version::fromComponents(1, 2, 2)) {
+ sendMessage(uSource, mpus);
+ }
+ } else {
+ sendAll(mpus, Version::fromComponents(1, 2, 2), Version::CompareMode::AtLeast);
+ }
+ }
+
MumbleProto::ServerConfig mpsc;
mpsc.set_allow_html(bAllowHTML);
mpsc.set_message_length(iMaxTextMessageLength);
@@ -959,18 +1004,42 @@ void Server::msgUserState(ServerUser *uSource, MumbleProto::UserState &msg) {
// Handle channel listening
// Note that it is important to handle the listening channels after channel-joins
- foreach (Channel *c, listeningChannelsAdd) {
- m_channelListenerManager.addListener(pDstServerUser->uiSession, c->iId);
+ QSet< int > volumeAdjustedChannels;
+ for (int i = 0; i < msg.listening_volume_adjustment_size(); i++) {
+ const MumbleProto::UserState::VolumeAdjustment &adjustment = msg.listening_volume_adjustment(i);
+
+ const Channel *channel = qhChannels.value(adjustment.listening_channel());
+
+ if (channel) {
+ setChannelListenerVolume(*pDstServerUser, *channel, adjustment.volume_adjustment());
+
+ volumeAdjustedChannels << channel->iId;
+ } else {
+ log(uSource, QString::fromLatin1("Invalid channel ID \"%1\" in volume adjustment")
+ .arg(adjustment.listening_channel()));
+ }
+ }
+ for (Channel *c : listeningChannelsAdd) {
+ addChannelListener(*pDstServerUser, *c);
log(QString::fromLatin1("\"%1\" is now listening to channel \"%2\"")
.arg(QString(*pDstServerUser))
.arg(QString(*c)));
+
+ float volumeAdjustment =
+ m_channelListenerManager.getListenerVolumeAdjustment(pDstServerUser->uiSession, c->iId).factor;
+
+ if (volumeAdjustment != 1.0f && !volumeAdjustedChannels.contains(c->iId)) {
+ MumbleProto::UserState::VolumeAdjustment *adjustment = msg.add_listening_volume_adjustment();
+ adjustment->set_listening_channel(c->iId);
+ adjustment->set_volume_adjustment(volumeAdjustment);
+ }
}
for (int i = 0; i < msg.listening_channel_remove_size(); i++) {
Channel *c = qhChannels.value(msg.listening_channel_remove(i));
if (c) {
- m_channelListenerManager.removeListener(pDstServerUser->uiSession, c->iId);
+ disableChannelListener(*pDstServerUser, *c);
log(QString::fromLatin1("\"%1\" is no longer listening to \"%2\"")
.arg(QString(*pDstServerUser))
@@ -978,9 +1047,17 @@ void Server::msgUserState(ServerUser *uSource, MumbleProto::UserState &msg) {
}
}
- bool listenerChanged = !listeningChannelsAdd.isEmpty() || msg.listening_channel_remove_size() > 0;
+ bool listenerVolumeChanged = msg.listening_volume_adjustment_size() > 0;
+ bool listenerChanged = !listeningChannelsAdd.isEmpty() || msg.listening_channel_remove_size() > 0;
- bBroadcast = bBroadcast || listenerChanged;
+ bool broadcastingBecauseOfVolumeChange = !bBroadcast && listenerVolumeChanged;
+ bBroadcast = bBroadcast || listenerChanged || listenerVolumeChanged;
+
+ if (listenerChanged || listenerVolumeChanged) {
+ // As whisper targets also contain information about ChannelListeners and
+ // their associated volume adjustment, we have to clear the target cache
+ clearWhisperTargetCache();
+ }
bool bDstAclChanged = false;
@@ -1033,11 +1110,21 @@ void Server::msgUserState(ServerUser *uSource, MumbleProto::UserState &msg) {
msg.set_comment_hash(blob(pDstServerUser->qbaCommentHash));
}
- sendAll(msg, Version::fromComponents(1, 2, 2), Version::CompareMode::AtLeast);
+ if (uSource->m_version >= Version::fromComponents(1, 2, 2)) {
+ sendMessage(uSource, msg);
+ }
+ if (!broadcastListenerVolumeAdjustments) {
+ // Don't broadcast the volume adjustments to everyone
+ msg.clear_listening_volume_adjustment();
+ }
+
+ if (broadcastListenerVolumeAdjustments || !broadcastingBecauseOfVolumeChange) {
+ sendExcept(uSource, msg, Version::fromComponents(1, 2, 2), Version::CompareMode::AtLeast);
+ }
if (bDstAclChanged) {
clearACLCache(pDstServerUser);
- } else if (listenerChanged) {
+ } else if (listenerChanged || listenerVolumeChanged) {
// We only have to do this if the ACLs didn't change as
// clearACLCache calls clearWhisperTargetChache anyways
clearWhisperTargetCache();
diff --git a/src/murmur/Meta.cpp b/src/murmur/Meta.cpp
index 88cb9cb3a..7bbb379fb 100644
--- a/src/murmur/Meta.cpp
+++ b/src/murmur/Meta.cpp
@@ -106,6 +106,8 @@ MetaParams::MetaParams() {
iPluginMessageLimit = 4;
iPluginMessageBurst = 15;
+ broadcastListenerVolumeAdjustments = false;
+
qsCiphers = MumbleSSL::defaultOpenSSLCipherString();
bLogGroupChanges = false;
@@ -370,6 +372,8 @@ void MetaParams::read(QString fname) {
iPluginMessageLimit = typeCheckedFromSettings("pluginmessagelimit", 4);
iPluginMessageBurst = typeCheckedFromSettings("pluginmessageburst", 15);
+ broadcastListenerVolumeAdjustments = typeCheckedFromSettings("broadcastlistenervolumeadjustments", false);
+
bool bObfuscate = typeCheckedFromSettings("obfuscate", false);
if (bObfuscate) {
qWarning("IP address obfuscation enabled.");
diff --git a/src/murmur/Meta.h b/src/murmur/Meta.h
index 655c841a5..c9ef82dfa 100644
--- a/src/murmur/Meta.h
+++ b/src/murmur/Meta.h
@@ -104,6 +104,8 @@ public:
unsigned int iPluginMessageLimit;
unsigned int iPluginMessageBurst;
+ bool broadcastListenerVolumeAdjustments;
+
QSslCertificate qscCert;
QSslKey qskKey;
diff --git a/src/murmur/MumbleServer.ice b/src/murmur/MumbleServer.ice
index 3f7d8ccca..280b0214b 100644
--- a/src/murmur/MumbleServer.ice
+++ b/src/murmur/MumbleServer.ice
@@ -827,6 +827,20 @@ module MumbleServer
idempotent IntList getListeningUsers(int channelid);
/**
+ * @param channelid The ID of the channel
+ * @param userid The ID of the user
+ * @returns The volume adjustment set for a listener of the given user in the given channel
+ */
+ idempotent float getListenerVolumeAdjustment(int channelid, int userid);
+
+ /**
+ * Sets the volume adjustment set for a listener of the given user in the given channel
+ * @param channelid The ID of the channel
+ * @param userid The ID of the user
+ */
+ idempotent void setListenerVolumeAdjustment(int channelid, int userid, float volumeAdjustment);
+
+ /**
* @param receiverUserIDs list of IDs of the users the message shall be sent to
*/
idempotent void sendWelcomeMessage(IdList receiverUserIDs);
diff --git a/src/murmur/MumbleServerI.h b/src/murmur/MumbleServerI.h
index e1ddb2d3c..1cc88e317 100644
--- a/src/murmur/MumbleServerI.h
+++ b/src/murmur/MumbleServerI.h
@@ -161,6 +161,12 @@ public:
virtual void getListeningUsers_async(const ::MumbleServer::AMD_Server_getListeningUsersPtr &, ::Ice::Int,
const Ice::Current &);
+ virtual void getListenerVolumeAdjustment_async(const ::MumbleServer::AMD_Server_getListenerVolumeAdjustmentPtr &,
+ ::Ice::Int, ::Ice::Int, const Ice::Current &);
+
+ virtual void setListenerVolumeAdjustment_async(const ::MumbleServer::AMD_Server_setListenerVolumeAdjustmentPtr &,
+ ::Ice::Int, ::Ice::Int, ::Ice::Float, const Ice::Current &);
+
virtual void sendWelcomeMessage_async(const ::MumbleServer::AMD_Server_sendWelcomeMessagePtr &,
const ::MumbleServer::IdList &p1, const ::Ice::Current &current);
diff --git a/src/murmur/MumbleServerIce.cpp b/src/murmur/MumbleServerIce.cpp
index 69a098b4c..e2e0ddcdb 100644
--- a/src/murmur/MumbleServerIce.cpp
+++ b/src/murmur/MumbleServerIce.cpp
@@ -7,6 +7,7 @@
#include "Ban.h"
#include "Channel.h"
+#include "ChannelListenerManager.h"
#include "Group.h"
#include "Meta.h"
#include "MumbleServer.h"
@@ -1809,6 +1810,27 @@ static void impl_Server_sendWelcomeMessage(const ::MumbleServer::AMD_Server_send
cb->ice_response();
}
+static void impl_Server_getListenerVolumeAdjustment(const ::MumbleServer::AMD_Server_getListenerVolumeAdjustmentPtr cb,
+ int server_id, int channelid, int session) {
+ NEED_SERVER;
+ NEED_CHANNEL;
+ NEED_PLAYER;
+
+ cb->ice_response(
+ server->m_channelListenerManager.getListenerVolumeAdjustment(user->uiSession, channel->iId).factor);
+}
+
+static void impl_Server_setListenerVolumeAdjustment(const ::MumbleServer::AMD_Server_setListenerVolumeAdjustmentPtr cb,
+ int server_id, int channelid, int session, float volumeAdjustment) {
+ NEED_SERVER;
+ NEED_CHANNEL;
+ NEED_PLAYER;
+
+ server->setListenerVolumeAdjustment(user, channel, VolumeAdjustment::fromFactor(volumeAdjustment));
+
+ cb->ice_response();
+}
+
static void impl_Server_addUserToGroup(const ::MumbleServer::AMD_Server_addUserToGroupPtr cb, int server_id,
::Ice::Int channelid, ::Ice::Int session, const ::std::string &group) {
NEED_SERVER;
diff --git a/src/murmur/RPC.cpp b/src/murmur/RPC.cpp
index d04e29c04..2cc62da66 100644
--- a/src/murmur/RPC.cpp
+++ b/src/murmur/RPC.cpp
@@ -10,6 +10,7 @@
#endif
#include "Channel.h"
+#include "ChannelListenerManager.h"
#include "Group.h"
#include "Meta.h"
#include "QtUtils.h"
@@ -357,14 +358,30 @@ void Server::startListeningToChannel(ServerUser *user, Channel *cChannel) {
return;
}
- m_channelListenerManager.addListener(user->uiSession, cChannel->iId);
+ addChannelListener(*user, *cChannel);
MumbleProto::UserState mpus;
mpus.set_session(user->uiSession);
mpus.add_listening_channel_add(cChannel->iId);
- sendAll(mpus);
+ if (!broadcastListenerVolumeAdjustments) {
+ sendExcept(user, mpus);
+ }
+
+ // Adding a listener might resurrect an old volume adjustment, so we need to
+ // inform the (all) client(s) about this volume adjustment.
+ float volumeAdjustment =
+ m_channelListenerManager.getListenerVolumeAdjustment(user->uiSession, cChannel->iId).factor;
+ MumbleProto::UserState::VolumeAdjustment *volume_adjustment = mpus.add_listening_volume_adjustment();
+ volume_adjustment->set_listening_channel(cChannel->iId);
+ volume_adjustment->set_volume_adjustment(volumeAdjustment);
+
+ if (broadcastListenerVolumeAdjustments) {
+ sendAll(mpus);
+ } else {
+ sendMessage(user, mpus);
+ }
}
void Server::stopListeningToChannel(ServerUser *user, Channel *cChannel) {
@@ -373,7 +390,7 @@ void Server::stopListeningToChannel(ServerUser *user, Channel *cChannel) {
return;
}
- m_channelListenerManager.removeListener(user->uiSession, cChannel->iId);
+ disableChannelListener(*user, *cChannel);
MumbleProto::UserState mpus;
mpus.set_session(user->uiSession);
@@ -383,6 +400,29 @@ void Server::stopListeningToChannel(ServerUser *user, Channel *cChannel) {
sendAll(mpus);
}
+void Server::setListenerVolumeAdjustment(ServerUser *user, const Channel *cChannel,
+ const VolumeAdjustment &volumeAdjustment) {
+ setChannelListenerVolume(*user, *cChannel, volumeAdjustment.factor);
+
+ // Inform clients about this change
+ MumbleProto::UserState mpus;
+ mpus.set_session(user->uiSession);
+
+ if (!broadcastListenerVolumeAdjustments) {
+ sendExcept(user, mpus);
+ }
+
+ MumbleProto::UserState::VolumeAdjustment *volume_adjustment = mpus.add_listening_volume_adjustment();
+ volume_adjustment->set_listening_channel(cChannel->iId);
+ volume_adjustment->set_volume_adjustment(volumeAdjustment.factor);
+
+ if (broadcastListenerVolumeAdjustments) {
+ sendAll(mpus);
+ } else {
+ sendMessage(user, mpus);
+ }
+}
+
void Server::sendWelcomeMessageTo(ServerUser *user) {
MumbleProto::ServerConfig mpsc;
mpsc.set_welcome_text(qsWelcomeText.toUtf8().data());
diff --git a/src/murmur/Server.cpp b/src/murmur/Server.cpp
index c98972543..705d199dd 100644
--- a/src/murmur/Server.cpp
+++ b/src/murmur/Server.cpp
@@ -315,43 +315,44 @@ Server::~Server() {
}
void Server::readParams() {
- qsPassword = Meta::mp.qsPassword;
- usPort = static_cast< unsigned short >(Meta::mp.usPort + iServerNum - 1);
- iTimeout = Meta::mp.iTimeout;
- iMaxBandwidth = Meta::mp.iMaxBandwidth;
- iMaxUsers = Meta::mp.iMaxUsers;
- iMaxUsersPerChannel = Meta::mp.iMaxUsersPerChannel;
- iMaxTextMessageLength = Meta::mp.iMaxTextMessageLength;
- iMaxImageMessageLength = Meta::mp.iMaxImageMessageLength;
- bAllowHTML = Meta::mp.bAllowHTML;
- iDefaultChan = Meta::mp.iDefaultChan;
- bRememberChan = Meta::mp.bRememberChan;
- iRememberChanDuration = Meta::mp.iRememberChanDuration;
- qsWelcomeText = Meta::mp.qsWelcomeText;
- qsWelcomeTextFile = Meta::mp.qsWelcomeTextFile;
- qlBind = Meta::mp.qlBind;
- qsRegName = Meta::mp.qsRegName;
- qsRegPassword = Meta::mp.qsRegPassword;
- qsRegHost = Meta::mp.qsRegHost;
- qsRegLocation = Meta::mp.qsRegLocation;
- qurlRegWeb = Meta::mp.qurlRegWeb;
- bBonjour = Meta::mp.bBonjour;
- bAllowPing = Meta::mp.bAllowPing;
- allowRecording = Meta::mp.allowRecording;
- bCertRequired = Meta::mp.bCertRequired;
- bForceExternalAuth = Meta::mp.bForceExternalAuth;
- qrUserName = Meta::mp.qrUserName;
- qrChannelName = Meta::mp.qrChannelName;
- iMessageLimit = Meta::mp.iMessageLimit;
- iMessageBurst = Meta::mp.iMessageBurst;
- iPluginMessageLimit = Meta::mp.iPluginMessageLimit;
- iPluginMessageBurst = Meta::mp.iPluginMessageBurst;
- m_suggestVersion = Meta::mp.m_suggestVersion;
- qvSuggestPositional = Meta::mp.qvSuggestPositional;
- qvSuggestPushToTalk = Meta::mp.qvSuggestPushToTalk;
- iOpusThreshold = Meta::mp.iOpusThreshold;
- iChannelNestingLimit = Meta::mp.iChannelNestingLimit;
- iChannelCountLimit = Meta::mp.iChannelCountLimit;
+ qsPassword = Meta::mp.qsPassword;
+ usPort = static_cast< unsigned short >(Meta::mp.usPort + iServerNum - 1);
+ iTimeout = Meta::mp.iTimeout;
+ iMaxBandwidth = Meta::mp.iMaxBandwidth;
+ iMaxUsers = Meta::mp.iMaxUsers;
+ iMaxUsersPerChannel = Meta::mp.iMaxUsersPerChannel;
+ iMaxTextMessageLength = Meta::mp.iMaxTextMessageLength;
+ iMaxImageMessageLength = Meta::mp.iMaxImageMessageLength;
+ bAllowHTML = Meta::mp.bAllowHTML;
+ iDefaultChan = Meta::mp.iDefaultChan;
+ bRememberChan = Meta::mp.bRememberChan;
+ iRememberChanDuration = Meta::mp.iRememberChanDuration;
+ qsWelcomeText = Meta::mp.qsWelcomeText;
+ qsWelcomeTextFile = Meta::mp.qsWelcomeTextFile;
+ qlBind = Meta::mp.qlBind;
+ qsRegName = Meta::mp.qsRegName;
+ qsRegPassword = Meta::mp.qsRegPassword;
+ qsRegHost = Meta::mp.qsRegHost;
+ qsRegLocation = Meta::mp.qsRegLocation;
+ qurlRegWeb = Meta::mp.qurlRegWeb;
+ bBonjour = Meta::mp.bBonjour;
+ bAllowPing = Meta::mp.bAllowPing;
+ allowRecording = Meta::mp.allowRecording;
+ bCertRequired = Meta::mp.bCertRequired;
+ bForceExternalAuth = Meta::mp.bForceExternalAuth;
+ qrUserName = Meta::mp.qrUserName;
+ qrChannelName = Meta::mp.qrChannelName;
+ iMessageLimit = Meta::mp.iMessageLimit;
+ iMessageBurst = Meta::mp.iMessageBurst;
+ iPluginMessageLimit = Meta::mp.iPluginMessageLimit;
+ iPluginMessageBurst = Meta::mp.iPluginMessageBurst;
+ broadcastListenerVolumeAdjustments = Meta::mp.broadcastListenerVolumeAdjustments;
+ m_suggestVersion = Meta::mp.m_suggestVersion;
+ qvSuggestPositional = Meta::mp.qvSuggestPositional;
+ qvSuggestPushToTalk = Meta::mp.qvSuggestPushToTalk;
+ iOpusThreshold = Meta::mp.iOpusThreshold;
+ iChannelNestingLimit = Meta::mp.iChannelNestingLimit;
+ iChannelCountLimit = Meta::mp.iChannelCountLimit;
QString qsHost = getConf("host", QString()).toString();
if (!qsHost.isEmpty()) {
@@ -462,6 +463,8 @@ void Server::readParams() {
if (iPluginMessageBurst < 1) { // Prevent disabling messages entirely
iPluginMessageBurst = 1;
}
+ broadcastListenerVolumeAdjustments =
+ getConf("broadcastlistenervolumeadjustments", broadcastListenerVolumeAdjustments).toBool();
}
void Server::setLiveConf(const QString &key, const QString &value) {
@@ -595,6 +598,9 @@ void Server::setLiveConf(const QString &key, const QString &value) {
if (iMessageBurst < 1) {
iMessageBurst = 1;
}
+ } else if (key == "broadcastlistenervolumeadjustments") {
+ broadcastListenerVolumeAdjustments =
+ (!v.isNull() ? QVariant(v).toBool() : Meta::mp.broadcastListenerVolumeAdjustments);
}
}
@@ -1087,6 +1093,16 @@ void Server::sendMessage(ServerUser &u, const unsigned char *data, int len, QByt
}
}
+void Server::addListener(QHash< ServerUser *, VolumeAdjustment > &listeners, ServerUser &user, const Channel &channel) {
+ const VolumeAdjustment &volumeAdjustment =
+ m_channelListenerManager.getListenerVolumeAdjustment(user.uiSession, channel.iId);
+
+ auto it = listeners.find(&user);
+
+ if (it == listeners.end() || it->factor < volumeAdjustment.factor) {
+ listeners[&user] = volumeAdjustment;
+ }
+}
void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, AudioReceiverBuffer &buffer,
Mumble::Protocol::UDPAudioEncoder< Mumble::Protocol::Role::Server > &encoder) {
@@ -1124,7 +1140,8 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
foreach (unsigned int currentSession, m_channelListenerManager.getListenersForChannel(c->iId)) {
ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession));
if (pDst) {
- buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::LISTEN, audioData.containsPositionalData);
+ buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::LISTEN, audioData.containsPositionalData,
+ m_channelListenerManager.getListenerVolumeAdjustment(pDst->uiSession, c->iId));
}
}
@@ -1148,8 +1165,9 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
for (unsigned int currentSession : m_channelListenerManager.getListenersForChannel(l->iId)) {
ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession));
if (pDst) {
- buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::LISTEN,
- audioData.containsPositionalData);
+ buffer.addReceiver(
+ *u, *pDst, Mumble::Protocol::AudioContext::LISTEN, audioData.containsPositionalData,
+ m_channelListenerManager.getListenerVolumeAdjustment(pDst->uiSession, l->iId));
}
}
@@ -1166,7 +1184,7 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
} else if (u->qmTargets.contains(audioData.targetOrContext)) { // Whisper/Shout
QSet< ServerUser * > channel;
QSet< ServerUser * > direct;
- QSet< ServerUser * > listener;
+ QHash< ServerUser *, VolumeAdjustment > cachedListeners;
if (u->qmTargetCache.contains(audioData.targetOrContext)) {
ZoneScopedN(TracyConstants::AUDIO_WHISPER_CACHE_STORE);
@@ -1174,7 +1192,7 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
const WhisperTargetCache &cache = u->qmTargetCache.value(audioData.targetOrContext);
channel = cache.channelTargets;
direct = cache.directTargets;
- listener = cache.listeningTargets;
+ cachedListeners = cache.listeningTargets;
} else {
ZoneScopedN(TracyConstants::AUDIO_WHISPER_CACHE_CREATE);
@@ -1198,7 +1216,7 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
ServerUser *pDst = static_cast< ServerUser * >(qhUsers.value(currentSession));
if (pDst) {
- listener << pDst;
+ addListener(cachedListeners, *pDst, *wc);
}
}
}
@@ -1229,7 +1247,7 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
if (pDst && (!group || Group::isMember(tc, tc, qsg, pDst))) {
// Only send audio to listener if the user exists and it is in the group the
// speech is directed at (if any)
- listener << pDst;
+ addListener(cachedListeners, *pDst, *tc);
}
}
}
@@ -1237,10 +1255,6 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
}
}
}
-
- // If a user receives the audio through this shout anyways, we won't send it through the
- // listening channel again (and thus sending the audio twice)
- listener -= channel;
}
{
@@ -1259,7 +1273,7 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
qrwlVoiceThread.lockForWrite();
if (qhUsers.contains(uiSession))
- u->qmTargetCache.insert(audioData.targetOrContext, { channel, direct, listener });
+ u->qmTargetCache.insert(audioData.targetOrContext, { channel, direct, cachedListeners });
qrwlVoiceThread.unlock();
qrwlVoiceThread.lockForRead();
if (!qhUsers.contains(uiSession))
@@ -1274,8 +1288,14 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
buffer.addReceiver(*u, *pDst, Mumble::Protocol::AudioContext::WHISPER, audioData.containsPositionalData);
}
// These users receive audio because someone is sending audio to one of their listeners
- for (ServerUser *current : listener) {
- buffer.addReceiver(*u, *current, Mumble::Protocol::AudioContext::LISTEN, audioData.containsPositionalData);
+ QHashIterator< ServerUser *, VolumeAdjustment > it(cachedListeners);
+ while (it.hasNext()) {
+ it.next();
+ ServerUser *user = it.key();
+ const VolumeAdjustment &volumeAdjustment = it.value();
+
+ buffer.addReceiver(*u, *user, Mumble::Protocol::AudioContext::LISTEN, audioData.containsPositionalData,
+ volumeAdjustment);
}
}
@@ -1318,7 +1338,8 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
isFirstIteration = false;
}
- audioData.targetOrContext = currentRange.begin->getContext();
+ audioData.targetOrContext = currentRange.begin->getContext();
+ audioData.volumeAdjustment = currentRange.begin->getVolumeAdjustment();
// Update data
TracyCZoneN(__tracy_zone, TracyConstants::AUDIO_UPDATE, true);
@@ -1605,8 +1626,8 @@ void Server::sslError(const QList< QSslError > &errors) {
void Server::connectionClosed(QAbstractSocket::SocketError err, const QString &reason) {
if (reason.contains(QLatin1String("140E0197"))) {
// A severe bug was introduced in qt/qtbase@93a803a6de27d9eb57931c431b5f3d074914f693.
- // q_SSL_shutdown() causes Qt to emit "error()" from unrelated QSslSocket(s), in addition to the correct one.
- // The issue causes this function to disconnect random authenticated clients.
+ // q_SSL_shutdown() causes Qt to emit "error()" from unrelated QSslSocket(s), in addition to the correct
+ // one. The issue causes this function to disconnect random authenticated clients.
//
// The workaround consists in ignoring a specific OpenSSL error:
// "Error while reading: error:140E0197:SSL routines:SSL_shutdown:shutdown while in init [20]"
@@ -1631,19 +1652,10 @@ void Server::connectionClosed(QAbstractSocket::SocketError err, const QString &r
if (u->sState == ServerUser::Authenticated) {
if (m_channelListenerManager.isListeningToAny(u->uiSession)) {
- // Send nessage to all other clients that this particular user won't be listening
- // to any channel anymore
- MumbleProto::UserState mpus;
- mpus.set_session(u->uiSession);
-
foreach (int channelID, m_channelListenerManager.getListenedChannelsForUser(u->uiSession)) {
- mpus.add_listening_channel_remove(channelID);
-
- // Also remove the client from the list on the server
+ // Remove the client from the list on the server
m_channelListenerManager.removeListener(u->uiSession, channelID);
}
-
- sendExcept(u, mpus);
}
MumbleProto::UserRemove mpur;
@@ -1878,11 +1890,16 @@ void Server::removeChannel(Channel *chan, Channel *dest) {
}
foreach (unsigned int userSession, m_channelListenerManager.getListenersForChannel(chan->iId)) {
- m_channelListenerManager.removeListener(userSession, chan->iId);
+ const ServerUser *user = qhUsers.value(userSession);
+ if (!user) {
+ continue;
+ }
+
+ deleteChannelListener(*user, *chan);
// Notify that all clients that have been listening to this channel, will do so no more
MumbleProto::UserState mpus;
- mpus.set_session(userSession);
+ mpus.set_session(user->uiSession);
mpus.add_listening_channel_remove(chan->iId);
sendAll(mpus);
@@ -2230,14 +2247,14 @@ void Server::recheckCodecVersions(ServerUser *connectingUser) {
if (bOpus) {
foreach (ServerUser *u, qhUsers) {
// Prevent connected users that could not yet declare their opus capability during msgAuthenticate from
- // being spammed. Only authenticated users and the currently connecting user (if recheck is called in that
- // context) have a reliable u->bOpus.
+ // being spammed. Only authenticated users and the currently connecting user (if recheck is called in
+ // that context) have a reliable u->bOpus.
if ((u->sState == ServerUser::Authenticated || u == connectingUser) && !u->bOpus) {
- sendTextMessage(
- nullptr, u, false,
- QLatin1String(
- "<strong>WARNING:</strong> Your client doesn't support the Opus codec the server is switching "
- "to, you won't be able to talk or hear anyone. Please upgrade to a client with Opus support."));
+ sendTextMessage(nullptr, u, false,
+ QLatin1String("<strong>WARNING:</strong> Your client doesn't support the Opus "
+ "codec the server is switching "
+ "to, you won't be able to talk or hear anyone. Please upgrade to a "
+ "client with Opus support."));
}
}
}
diff --git a/src/murmur/Server.h b/src/murmur/Server.h
index 31a0de69d..c78186f29 100644
--- a/src/murmur/Server.h
+++ b/src/murmur/Server.h
@@ -22,6 +22,7 @@
#include "Timer.h"
#include "User.h"
#include "Version.h"
+#include "VolumeAdjustment.h"
#ifndef Q_MOC_RUN
# include <boost/function.hpp>
@@ -144,7 +145,10 @@ public:
unsigned int iPluginMessageLimit;
unsigned int iPluginMessageBurst;
+ bool broadcastListenerVolumeAdjustments;
+
Version::full_t m_suggestVersion;
+
QVariant qvSuggestPositional;
QVariant qvSuggestPushToTalk;
@@ -309,6 +313,7 @@ public:
QList< Ban > qlBans;
+ void addListener(QHash< ServerUser *, VolumeAdjustment > &listeners, ServerUser &user, const Channel &channel);
void processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, AudioReceiverBuffer &buffer,
Mumble::Protocol::UDPAudioEncoder< Mumble::Protocol::Role::Server > &encoder);
void sendMessage(ServerUser &u, const unsigned char *data, int len, QByteArray &cache, bool force = false);
@@ -380,6 +385,8 @@ public:
void clearTempGroups(User *user, Channel *cChannel = nullptr, bool recurse = true);
void startListeningToChannel(ServerUser *user, Channel *cChannel);
void stopListeningToChannel(ServerUser *user, Channel *cChannel);
+ void setListenerVolumeAdjustment(ServerUser *user, const Channel *cChannel,
+ const VolumeAdjustment &volumeAdjustment);
void sendWelcomeMessageTo(ServerUser *user);
signals:
void registerUserSig(int &, const QMap< int, QString > &);
@@ -456,7 +463,16 @@ public:
void setConf(const QString &key, const QVariant &value);
void dblog(const QString &str) const;
- // From msgHandler. Implementation in Messages.cpp
+ // These functions perform both the necessary changes to ChannelListeners as
+ // well as persisting the changed listeners state to the DB. You should use
+ // these unless you have a good reason not to
+ void loadChannelListenersOf(const ServerUser &user);
+ void addChannelListener(const ServerUser &user, const Channel &channel);
+ void disableChannelListener(const ServerUser &user, const Channel &channel);
+ void deleteChannelListener(const ServerUser &user, const Channel &channel);
+ void setChannelListenerVolume(const ServerUser &user, const Channel &channel, float volumeAdjustment);
+
+ // Implementation in Messages.cpp
#define PROCESS_MUMBLE_TCP_MESSAGE(name, value) void msg##name(ServerUser *, MumbleProto::name &);
MUMBLE_ALL_TCP_MESSAGES
#undef PROCESS_MUMBLE_TCP_MESSAGE
diff --git a/src/murmur/ServerDB.cpp b/src/murmur/ServerDB.cpp
index 354dd041e..d8924c858 100644
--- a/src/murmur/ServerDB.cpp
+++ b/src/murmur/ServerDB.cpp
@@ -339,6 +339,9 @@ ServerDB::ServerDB() {
SQLQUERY("ALTER TABLE `%1user_info` RENAME TO `%1user_info%2`");
SQLQUERY("ALTER TABLE `%1channel_info` RENAME TO `%1channel_info%2`");
}
+ if (version >= 9) {
+ SQLQUERY("ALTER TABLE `%1channel_listeners` RENAME TO `%1channel_listeners%2`");
+ }
}
// Now we generate new tables that conform to the state-of-the-art structure
@@ -367,6 +370,9 @@ ServerDB::ServerDB() {
SQLDO("DROP TRIGGER IF EXISTS `%1acl_del_user`");
SQLDO("DROP TRIGGER IF EXISTS `%1channel_links_del_channel`");
SQLDO("DROP TRIGGER IF EXISTS `%1bans_del_server`");
+ SQLDO("DROP TRIGGER IF EXISTS `%1channel_listeners_del_server`");
+ SQLDO("DROP TRIGGER IF EXISTS `%1channel_listeners_del_channel`");
+ SQLDO("DROP TRIGGER IF EXISTS `%1channel_listeners_del_user`");
SQLDO("DROP INDEX IF EXISTS `%1log_time`");
SQLDO("DROP INDEX IF EXISTS `%1slog_time`");
@@ -463,6 +469,17 @@ ServerDB::ServerDB() {
"`hash` TEXT, `reason` TEXT, `start` DATE, `duration` INTEGER)");
SQLDO("CREATE TRIGGER `%1bans_del_server` AFTER DELETE ON `%1servers` FOR EACH ROW BEGIN DELETE FROM "
"`%1bans` WHERE `server_id` = old.`server_id`; END;");
+
+ SQLDO("CREATE TABLE `%1channel_listeners` (`server_id` INTEGER NOT NULL, `user_id` INTEGER NOT NULL, "
+ "`channel_id` INTEGER NOT NULL, `volume_adjustment` FLOAT DEFAULT 1, `enabled` SMALLINT DEFAULT 1)");
+ SQLDO("CREATE TRIGGER `%1channel_listeners_del_server` AFTER DELETE ON `%1servers` FOR EACH ROW BEGIN "
+ "DELETE FROM `%1channel_listeners` WHERE `server_id` = old.`server_id`; END;");
+ SQLDO("CREATE TRIGGER `%1channel_listeners_del_channel` AFTER DELETE ON `%1channels` FOR EACH ROW BEGIN "
+ "DELETE FROM `%1channel_listeners` WHERE `server_id` = old.`server_id` AND `channel_id` = "
+ "old.`channel_id`; END;");
+ SQLDO("CREATE TRIGGER `%1channel_listeners_del_user` AFTER DELETE ON `%1users` FOR EACH ROW BEGIN "
+ "DELETE FROM `%1channel_listeners` WHERE `server_id` = old.`server_id` AND `user_id` = "
+ "old.`user_id`; END;");
} else if (Meta::mp.qsDBDriver == "QPSQL") {
if (version > 0) {
typedef QPair< QString, QString > qsp;
@@ -594,6 +611,18 @@ ServerDB::ServerDB() {
"varchar(255), `hash` CHAR(40), `reason` TEXT, `start` TIMESTAMP, `duration` INTEGER)");
SQLQUERY("ALTER TABLE `%1bans` ADD CONSTRAINT `%1bans_del_server` FOREIGN KEY(`server_id`) REFERENCES "
"`%1servers`(`server_id`) ON DELETE CASCADE");
+
+ SQLQUERY(
+ "CREATE TABLE `%1channel_listeners` (`server_id` INTEGER NOT NULL, `user_id` INTEGER NOT NULL, "
+ "`channel_id` INTEGER NOT NULL, `volume_adjustment` FLOAT DEFAULT 1, `enabled` SMALLINT DEFAULT 1)");
+ SQLQUERY("ALTER TABLE `%1channel_listeners` ADD CONSTRAINT `%1channel_listeners_del_server` FOREIGN "
+ "KEY(`server_id`) REFERENCES `%1servers`(`server_id`) ON DELETE CASCADE");
+ SQLQUERY("ALTER TABLE `%1channel_listeners` ADD CONSTRAINT `%1channel_listeners_del_channel` FOREIGN KEY "
+ "(`server_id`, "
+ "`channel_id`) REFERENCES `%1channels`(`server_id`, `channel_id`) ON DELETE CASCADE");
+ SQLQUERY("ALTER TABLE `%1channel_listeners` ADD CONSTRAINT `%1channel_listeners_del_user` FOREIGN KEY "
+ "(`server_id`, "
+ "`user_id`) REFERENCES `%1users`(`server_id`, `user_id`) ON DELETE CASCADE");
} else {
// MySQL
if (version > 0) {
@@ -694,6 +723,15 @@ ServerDB::ServerDB() {
"varchar(255), `hash` CHAR(40), `reason` TEXT, `start` DATETIME, `duration` INTEGER) ENGINE=InnoDB");
SQLDO("ALTER TABLE `%1bans` ADD CONSTRAINT `%1bans_del_server` FOREIGN KEY(`server_id`) REFERENCES "
"`%1servers`(`server_id`) ON DELETE CASCADE");
+
+ SQLDO("CREATE TABLE `%1channel_listeners` (`server_id` INTEGER NOT NULL, `user_id` INTEGER NOT NULL, "
+ "`channel_id` INTEGER NOT NULL, `volume_adjustment` FLOAT DEFAULT 1, `enabled` SMALLINT DEFAULT 1)");
+ SQLDO("ALTER TABLE `%1channel_listeners` ADD CONSTRAINT `%1channel_listeners_del_server` FOREIGN "
+ "KEY(`server_id`) REFERENCES `%1servers`(`server_id`) ON DELETE CASCADE");
+ SQLDO("ALTER TABLE `%1channel_listeners` ADD CONSTRAINT `%1channel_listeners_del_channel` FOREIGN KEY "
+ "(`server_id`, `channel_id`) REFERENCES `%1channels`(`server_id`, `channel_id`) ON DELETE CASCADE");
+ SQLDO("ALTER TABLE `%1channel_listeners` ADD CONSTRAINT `%1channel_listeners_del_user` FOREIGN KEY "
+ "(`server_id`, `user_id`) REFERENCES `%1users`(`server_id`, `user_id`) ON DELETE CASCADE");
}
if (version == 0) {
@@ -809,6 +847,9 @@ ServerDB::ServerDB() {
SQLDO("INSERT INTO `%1channel_info` SELECT * FROM `%1channel_info%2`");
}
+ if (version >= 9) {
+ SQLDO("INSERT INTO `%1channel_listeners` SELECT * FROM `%1channel_listeners%2`");
+ }
if (Meta::mp.qsDBDriver == "QMYSQL")
SQLDO("SET FOREIGN_KEY_CHECKS = 1;");
@@ -826,6 +867,7 @@ ServerDB::ServerDB() {
SQLQUERY("DROP TABLE IF EXISTS `%1channels%2`");
SQLQUERY("DROP TABLE IF EXISTS `%1bans%2`");
SQLQUERY("DROP TABLE IF EXISTS `%1servers%2`");
+ SQLQUERY("DROP TABLE IF EXISTS `%1channel_listeners%2`");
SQLDO_NO_CONVERSION(QLatin1String("UPDATE `%1meta` SET `value` = ")
+ QString::fromLatin1("'%1' WHERE `keystring` = 'version'").arg(DB_STRUCTURE_VERSION));
@@ -2371,6 +2413,129 @@ void Server::dblog(const QString &str) const {
SQLEXEC();
}
+void Server::loadChannelListenersOf(const ServerUser &user) {
+ if (user.iId < 0) {
+ // Not registered
+ return;
+ }
+
+ TransactionHolder th;
+ QSqlQuery &query = *th.qsqQuery;
+
+ SQLPREP("SELECT `channel_id`, `volume_adjustment`, `enabled` FROM `%1channel_listeners` WHERE `server_id` = ? AND "
+ "`user_id` = ?");
+ query.addBindValue(iServerNum);
+ query.addBindValue(user.iId);
+ SQLEXEC();
+
+ while (query.next()) {
+ int channelID = query.value(0).toInt();
+ float volume = query.value(1).toFloat();
+ bool enabled = query.value(2).toUInt() == 1;
+
+ if (enabled) {
+ m_channelListenerManager.addListener(user.uiSession, channelID);
+ }
+
+ // We load the volume adjustment regardless of whether the listener is currently enabled in case the listener
+ // gets re-activated
+ m_channelListenerManager.setListenerVolumeAdjustment(user.uiSession, channelID,
+ VolumeAdjustment::fromFactor(volume));
+ }
+}
+
+void Server::addChannelListener(const ServerUser &user, const Channel &channel) {
+ if (user.iId >= 0) {
+ TransactionHolder th;
+ QSqlQuery &query = *th.qsqQuery;
+
+ // Update or insert entry
+ SQLPREP(
+ "SELECT COUNT(*) FROM `%1channel_listeners` WHERE `server_id` = ? AND `user_id` = ? AND `channel_id` = ?");
+ query.addBindValue(iServerNum);
+ query.addBindValue(user.iId);
+ query.addBindValue(channel.iId);
+
+ SQLEXEC();
+
+ bool entryAlreadyExists = query.next() && query.value(0).toInt() > 0;
+
+ if (entryAlreadyExists) {
+ SQLPREP("UPDATE `%1channel_listeners` SET `enabled` = 1 WHERE `server_id` = ? AND `user_id`= ? AND "
+ "`channel_id` = ?");
+ } else {
+ SQLPREP("INSERT INTO `%1channel_listeners` (`server_id`, `user_id`, `channel_id`) VALUES (?, ?, ?)");
+ }
+
+ query.addBindValue(iServerNum);
+ query.addBindValue(user.iId);
+ query.addBindValue(channel.iId);
+
+ SQLEXEC();
+ }
+
+ m_channelListenerManager.addListener(user.uiSession, channel.iId);
+}
+
+void Server::disableChannelListener(const ServerUser &user, const Channel &channel) {
+ if (!m_channelListenerManager.isListening(user.uiSession, channel.iId)) {
+ return;
+ }
+
+ if (user.iId >= 0) {
+ TransactionHolder th;
+ QSqlQuery &query = *th.qsqQuery;
+
+ SQLPREP("UPDATE `%1channel_listeners` SET `enabled` = ? WHERE `server_id` = ? AND `user_id` = ? AND "
+ "`channel_id` = ?");
+ // Explicit cast to int is required for Postgresql
+ query.addBindValue(static_cast< int >(false));
+ query.addBindValue(iServerNum);
+ query.addBindValue(user.iId);
+ query.addBindValue(channel.iId);
+ SQLEXEC();
+ }
+
+ m_channelListenerManager.removeListener(user.uiSession, channel.iId);
+}
+
+void Server::deleteChannelListener(const ServerUser &user, const Channel &channel) {
+ if (!m_channelListenerManager.isListening(user.uiSession, channel.iId)) {
+ return;
+ }
+
+ if (user.iId >= 0) {
+ TransactionHolder th;
+ QSqlQuery &query = *th.qsqQuery;
+
+ SQLPREP("DELETE FROM `%1channel_listeners` WHERE `server_id` = ? AND `user_id` = ? AND `channel_id` = ?");
+ query.addBindValue(iServerNum);
+ query.addBindValue(user.iId);
+ query.addBindValue(channel.iId);
+ SQLEXEC();
+ }
+
+ m_channelListenerManager.removeListener(user.uiSession, channel.iId);
+}
+
+void Server::setChannelListenerVolume(const ServerUser &user, const Channel &channel, float volumeAdjustment) {
+ if (user.iId >= 0) {
+ TransactionHolder th;
+ QSqlQuery &query = *th.qsqQuery;
+
+ SQLPREP("UPDATE `%1channel_listeners` SET `volume_adjustment` = ? WHERE `server_id` = ? AND `user_id` = ? AND "
+ "`channel_id` = ?");
+ query.addBindValue(volumeAdjustment);
+ query.addBindValue(iServerNum);
+ query.addBindValue(user.iId);
+ query.addBindValue(channel.iId);
+ SQLEXEC();
+ }
+
+ m_channelListenerManager.setListenerVolumeAdjustment(user.uiSession, channel.iId,
+ VolumeAdjustment::fromFactor(volumeAdjustment));
+}
+
void ServerDB::wipeLogs() {
TransactionHolder th;
QSqlQuery &query = *th.qsqQuery;
diff --git a/src/murmur/ServerDB.h b/src/murmur/ServerDB.h
index 2815ede5d..b3790a555 100644
--- a/src/murmur/ServerDB.h
+++ b/src/murmur/ServerDB.h
@@ -26,7 +26,7 @@ public:
/// Whenever you change the DB structure (add a new table, added a new column in a table, etc.)
/// you have to increase this version number by one and add the respective "backwards compatibility
/// code" into the ServerDB code.
- static const int DB_STRUCTURE_VERSION = 8;
+ static const int DB_STRUCTURE_VERSION = 9;
enum ChannelInfo { Channel_Description, Channel_Position, Channel_Max_Users };
enum UserInfo {
diff --git a/src/murmur/ServerUser.h b/src/murmur/ServerUser.h
index 4b9fcef17..6bcf201eb 100644
--- a/src/murmur/ServerUser.h
+++ b/src/murmur/ServerUser.h
@@ -65,7 +65,7 @@ class ServerUser;
struct WhisperTargetCache {
QSet< ServerUser * > channelTargets;
QSet< ServerUser * > directTargets;
- QSet< ServerUser * > listeningTargets;
+ QHash< ServerUser *, VolumeAdjustment > listeningTargets;
};
class Server;