From 46e8bf5283163eb031c0ff671545049ac42a4392 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 22 Dec 2016 12:42:50 -0800 Subject: [PATCH] First steps, holding off til Howard finishes --- assignment-client/src/audio/AudioMixer.cpp | 50 ++++++++++++++++ assignment-client/src/audio/AudioMixer.h | 2 + interface/resources/qml/hifi/Pal.qml | 33 +++++++---- libraries/networking/src/NodeList.cpp | 58 +++++++++++++++++++ libraries/networking/src/NodeList.h | 4 ++ libraries/networking/src/udt/PacketHeaders.h | 6 +- .../src/UsersScriptingInterface.cpp | 12 ++++ .../src/UsersScriptingInterface.h | 23 +++++++- scripts/system/pal.js | 2 + 9 files changed, 178 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 9a80289911..f7ea49a642 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -66,6 +66,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket"); packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::NodeUnignoreRequest, this, "handleNodeUnignoreRequestPacket"); + packetReceiver.registerListener(PacketType::NodePersonalMuteRequest, this, "handleNodePersonalMuteRequestPacket"); + packetReceiver.registerListener(PacketType::NodePersonalMuteStatusRequest, this, "handleNodePersonalMuteStatusRequestPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket"); packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); @@ -230,6 +232,54 @@ void AudioMixer::handleNodeUnignoreRequestPacket(QSharedPointer sendingNode->parseUnignoreRequestMessage(packet); } +void AudioMixer::handleNodePersonalMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + // parse out the UUID being muted from the packet + QUuid ignoredUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + bool enabled; + packet->readPrimitive(&enabled); + + if (!ignoredUUID.isNull() && ignoredUUID != _uuid) { + if (enabled) { + qDebug() << "Adding" << uuidStringWithoutCurlyBraces(ignoredUUID) << "to personally muted set for" + << uuidStringWithoutCurlyBraces(_uuid); + + // Add the session UUID to the set of personally muted ones for this listening node + sendingNode->addIgnoredNode(ignoredUUID); + } else { + qDebug() << "Removing" << uuidStringWithoutCurlyBraces(ignoredUUID) << "from personally muted set for" + << uuidStringWithoutCurlyBraces(_uuid); + + // Remove the session UUID to the set of personally muted ones for this listening node + sendingNode->_ignoredNodeIDSet.unsafe_erase(ignoredUUID); + } + } else { + qWarning() << "Node::addPersonalMutedNode called with null ID or ID of personal muting node."; + } +} + +void AudioMixer::handleNodePersonalMuteStatusRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + // parse out the UUID whose personal mute status is being requested from the packet + QUuid UUIDToCheck = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + if (!UUIDToCheck.isNull()) { + // First, make sure we actually have a node with this UUID + auto limitedNodeList = DependencyManager::get(); + auto matchingNode = limitedNodeList->nodeWithUUID(UUIDToCheck); + + // If we do have a matching node... + if (matchingNode) { + auto personalMuteStatusPacket = NLPacket::create(PacketType::NodePersonalMuteStatusReply, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); + + // write the node ID to the packet + personalMuteStatusPacket->write(UUIDToCheck.toRfc4122()); + personalMuteStatusPacket->writePrimitive(isIgnoringNodeWithID(UUIDToCheck)); + + qCDebug(networking) << "Sending Personal Mute Status Request Packet for node" << uuidStringWithoutCurlyBraces(nodeID); + + limitedNodeList->sendPacket(std::move(personalMuteStatusPacket), *sendingNode); + } + } +} void AudioMixer::handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { sendingNode->parseIgnoreRadiusRequestMessage(packet); } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 14a0167c3e..353db84a15 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -63,6 +63,8 @@ private slots: void handleNodeKilled(SharedNodePointer killedNode); void handleNodeIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodeUnignoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handleNodePersonalMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handleNodePersonalMuteStatusRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleKillAvatarPacket(QSharedPointer packet, SharedNodePointer sendingNode); void handleNodeMuteRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 5c475f16dd..eb35664707 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -210,22 +210,28 @@ Item { var newValue = !model[styleData.role] var datum = userData[model.userIndex] datum[styleData.role] = model[styleData.role] = newValue - var key = styleData.role; - if (!newValue) { - key = 'un' + key; - } - if (styleData.role === 'ignore') { + if (styleData.role === "personalMute") { + Users[styleData.role](model.sessionId, newValue) + } else if (styleData.role === 'ignore') { + var key = styleData.role; + if (!newValue) { + key = 'un' + key; + } if (newValue) { ignored[datum.sessionId] = datum; console.log('fixme hrs adding to ignored', JSON.stringify(datum), 'at', datum.sessionId); } else { delete ignored[datum.sessionId]; - } + } + console.log('fixme hrs pal action', key, model.sessionId); + Users[key](model.sessionId); + } else { + Users[styleData.role](model.sessionId) + // Just for now, while we cannot undo things: + userData.splice(model.userIndex, 1) + sortModel() } - console.log('fixme hrs pal action', key, model.sessionId); - Users[key](model.sessionId); } - } } } // Refresh button @@ -422,7 +428,14 @@ Item { } } break; - default: + case 'updateMuted': + var userId = message.params[0]; + var enabled = message.params[1]; + var userIndex = findSessionIndex(userId); + userModel.get(userIndex).personalMute.property = enabled; + userData[userIndex].personalMute.property = enabled; // Defensive programming + break; + default: console.log('Unrecognized message:', JSON.stringify(message)); } } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e745954d88..f80dbad23d 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -875,6 +875,64 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { } } +void NodeList::personalMuteNodeBySessionID(const QUuid& nodeID, bool enabled) { + // cannot personal mute yourself, or nobody + if (!nodeID.isNull() && _sessionUUID != nodeID) { + auto audioMixer = soloNodeOfType(NodeType::AudioMixer); + if (audioMixer) { + // setup the packet + auto personalMutePacket = NLPacket::create(PacketType::NodePersonalMuteRequest, NUM_BYTES_RFC4122_UUID + sizeof(bool), true); + + // write the node ID to the packet + personalMutePacket->write(nodeID.toRfc4122()); + personalMutePacket->writePrimitive(enabled); + + qCDebug(networking) << "Sending Personal Mute Packet to" << (enabled ? "mute" : "unmute") << "node" << uuidStringWithoutCurlyBraces(nodeID); + + sendPacket(std::move(personalMutePacket), *audioMixer); + } else { + qWarning() << "Couldn't find audio mixer to send node personal mute request"; + } + } else { + qWarning() << "NodeList::personalMuteNodeBySessionID called with an invalid ID or an ID which matches the current session ID."; + } +} + +void NodeList::requestPersonalMuteStatus(const QUuid& nodeID) { + // cannot personal mute yourself, or nobody; don't bother checking the status + if (!nodeID.isNull() && _sessionUUID != nodeID) { + auto audioMixer = soloNodeOfType(NodeType::AudioMixer); + if (audioMixer) { + // send a request to the audio mixer to get the personal mute status associated with the given session ID + // setup the packet + auto personalMuteStatusPacket = NLPacket::create(PacketType::NodePersonalMuteStatusRequest, NUM_BYTES_RFC4122_UUID, true); + + // write the node ID to the packet + personalMuteStatusPacket->write(nodeID.toRfc4122()); + + qCDebug(networking) << "Sending Personal Mute Status Request Packet for node" << uuidStringWithoutCurlyBraces(nodeID); + + sendPacket(std::move(personalMuteStatusPacket), *audioMixer); + } else { + qWarning() << "Couldn't find audio mixer to send node personal mute status request"; + } + } else { + qWarning() << "NodeList::requestPersonalMuteStatus called with an invalid ID or an ID which matches the current session ID."; + } +} + +void NodeList::processPersonalMuteStatusReply(QSharedPointer message) { + // read the UUID from the packet + QString nodeUUIDString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString(); + // read the personal mute status + bool isPersonalMuted; + message->readPrimitive(&isPersonalMuted); + + qCDebug(networking) << "Got personal muted status" << isPersonalMuted << "for node" << nodeUUIDString; + + emit personalMuteStatusReply(nodeUUIDString, isPersonalMuted); +} + void NodeList::kickNodeBySessionID(const QUuid& nodeID) { // send a request to domain-server to kick the node with the given session ID // the domain-server will handle the persistence of the kick (via username or IP) diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 6d7026b562..5b3f334366 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -79,12 +79,14 @@ public: void ignoreNodeBySessionID(const QUuid& nodeID); void unignoreNodeBySessionID(const QUuid& nodeID); bool isIgnoringNode(const QUuid& nodeID) const; + void personalMuteNodeBySessionID(const QUuid& nodeID, bool enabled); void kickNodeBySessionID(const QUuid& nodeID); void muteNodeBySessionID(const QUuid& nodeID); void requestUsernameFromSessionID(const QUuid& nodeID); bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool isRequesting); + void requestPersonalMuteStatus(const QUuid& nodeID); public slots: void reset(); @@ -104,6 +106,7 @@ public slots: void processICEPingPacket(QSharedPointer message); void processUsernameFromIDReply(QSharedPointer message); + void processPersonalMuteStatusReply(QSharedPointer message); #if (PR_BUILD || DEV_BUILD) void toggleSendNewerDSConnectVersion(bool shouldSendNewerVersion) { _shouldSendNewerVersion = shouldSendNewerVersion; } @@ -116,6 +119,7 @@ signals: void unignoredNode(const QUuid& nodeID); void ignoreRadiusEnabledChanged(bool isIgnored); void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); + void personalMuteStatusReply(const QString& nodeID, bool isPersonalMuted); private slots: void stopKeepalivePingTimer(); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index cae4eaa43e..76c5120823 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -106,7 +106,10 @@ public: ViewFrustum, RequestsDomainListData, NodeUnignoreRequest, - LAST_PACKET_TYPE = NodeUnignoreRequest + NodePersonalMuteRequest, + NodePersonalMuteStatusRequest, + NodePersonalMuteStatusReply, + LAST_PACKET_TYPE = NodePersonalMuteStatusReply }; }; @@ -245,6 +248,7 @@ enum class AudioVersion : PacketVersion { Exactly10msAudioPackets, TerminatingStreamStats, SpaceBubbleChanges, + HasPersonalMute, }; #endif // hifi_PacketHeaders_h diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index e736bf2a84..e25a9603b6 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -21,6 +21,7 @@ UsersScriptingInterface::UsersScriptingInterface() { connect(nodeList.data(), &NodeList::usernameFromIDReply, this, &UsersScriptingInterface::usernameFromIDReply); connect(nodeList.data(), &NodeList::ignoredNode, this, &UsersScriptingInterface::ignoredNode); connect(nodeList.data(), &NodeList::unignoredNode, this, &UsersScriptingInterface::unignoredNode); + connect(nodeList.data(), &NodeList::personalMuteStatusReply, this, &UsersScriptingInterface::personalMuteStatusReply); } void UsersScriptingInterface::ignore(const QUuid& nodeID) { @@ -33,6 +34,17 @@ void UsersScriptingInterface::unignore(const QUuid& nodeID) { DependencyManager::get()->unignoreNodeBySessionID(nodeID); } +void UsersScriptingInterface::personalMute(const QUuid& nodeID, bool enabled) { + // ask the NodeList to mute the user with the given session ID + // "Personal Mute" only applies one way and is not global + DependencyManager::get()->personalMuteNodeBySessionID(nodeID, enabled); +} + +void UsersScriptingInterface::requestPersonalMuteStatus(const QUuid& nodeID) { + // ask the Audio Mixer via the NodeList for the Personal Mute status associated with the given session ID + DependencyManager::get()->requestPersonalMuteStatus(nodeID); +} + void UsersScriptingInterface::kick(const QUuid& nodeID) { // ask the NodeList to kick the user with the given session ID diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 394e006451..a7e6ec3a3e 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -39,6 +39,21 @@ public slots: void ignore(const QUuid& nodeID); void unignore(const QUuid& nodeID); + /**jsdoc + * Mute another user for you and you only. + * @function Users.personalMute + * @param {nodeID} nodeID The node or session ID of the user you want to mute. + * @param {bool} enable True for enabled; false for disabled. + */ + void personalMute(const QUuid& nodeID, bool enabled); + + /**jsdoc + * Requests a bool containing whether you have given the given Avatar UUID. + * @function Users.requestPersonalMuteStatus + * @param {nodeID} nodeID The node or session ID of the user whose personal mute status you want. + */ + void requestPersonalMuteStatus(const QUuid& nodeID); + /**jsdoc * Kick another user. * @function Users.kick @@ -47,7 +62,7 @@ public slots: void kick(const QUuid& nodeID); /**jsdoc - * Mute another user. + * Mute another user for everyone. * @function Users.mute * @param {nodeID} nodeID The node or session ID of the user you want to mute. */ @@ -110,6 +125,12 @@ signals: */ void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint); + /**jsdoc + * Notifies scripts of the Personal Mute status associated with a UUID. + * @function Users.usernameFromIDReply + */ + void personalMuteStatusReply(const QString& nodeID, bool isPersonalMuted); + private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7e25496537..7f38857702 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -132,6 +132,8 @@ function populateUserList() { // Request the username from the given UUID Users.requestUsernameFromID(id); } + // Request personal mute status from AudioMixer + Users.requestPersonalMuteStatus(id); data.push(avatarPalDatum); if (id) { // No overlay for ourself. addAvatarNode(id);