diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index ab4b20b9cc..5e0cd740e6 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -103,11 +103,6 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : ); connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled); - connect(nodeList.data(), &NodeList::nodeAdded, this, [this](const SharedNodePointer& node) { - if (node->getType() == NodeType::DownstreamAudioMixer) { - node->activatePublicSocket(); - } - }); } void AudioMixer::queueAudioPacket(QSharedPointer message, SharedNodePointer node) { @@ -389,7 +384,10 @@ void AudioMixer::start() { auto nodeList = DependencyManager::get(); // prepare the NodeList - nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::DownstreamAudioMixer, NodeType::EntityScriptServer }); + nodeList->addSetOfNodeTypesToNodeInterestSet({ + NodeType::Agent, NodeType::EntityScriptServer, + NodeType::UpstreamAudioMixer, NodeType::DownstreamAudioMixer + }); nodeList->linkedDataCreateCallback = [&](Node* node) { getOrCreateClientData(node); }; // parse out any AudioMixer settings diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index e542d82a6a..ed3a5b0541 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -51,6 +51,11 @@ public: static const QVector& getReverbSettings() { return _zoneReverbSettings; } static const std::pair negotiateCodec(std::vector codecs); + static bool shouldReplicateTo(const Node& from, const Node& to) { + return to.getType() == NodeType::DownstreamAudioMixer && + to.getPublicSocket() != from.getPublicSocket() && + to.getLocalSocket() != from.getLocalSocket(); + } public slots: void run() override; void sendStatsPacket() override; diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index bf19a02d9a..408ddf038c 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -67,24 +67,25 @@ void AudioMixerClientData::processPackets() { case PacketType::MicrophoneAudioNoEcho: case PacketType::MicrophoneAudioWithEcho: case PacketType::InjectAudio: - case PacketType::AudioStreamStats: - case PacketType::SilentAudioFrame: - case PacketType::ReplicatedMicrophoneAudioNoEcho: - case PacketType::ReplicatedMicrophoneAudioWithEcho: - case PacketType::ReplicatedInjectAudio: - case PacketType::ReplicatedSilentAudioFrame: { + case PacketType::SilentAudioFrame: { - if (node->isUpstream() && !_hasSetupCodecForUpstreamNode) { + if (node->isUpstream()) { setupCodecForReplicatedAgent(packet); } QMutexLocker lock(&getMutex()); parseData(*packet); - + optionallyReplicatePacket(*packet, *node); break; } + case PacketType::AudioStreamStats: { + QMutexLocker lock(&getMutex()); + parseData(*packet); + + break; + } case PacketType::NegotiateAudioFormat: negotiateAudioFormat(*packet, node); break; @@ -141,7 +142,7 @@ void AudioMixerClientData::optionallyReplicatePacket(ReceivedMessage& message, c // enumerate the downstream audio mixers and send them the replicated version of this packet nodeList->unsafeEachNode([&](const SharedNodePointer& downstreamNode) { - if (downstreamNode->getType() == NodeType::DownstreamAudioMixer) { + if (AudioMixer::shouldReplicateTo(node, *downstreamNode)) { // construct the packet only once, if we have any downstream audio mixers to send to if (!packet) { // construct an NLPacket to send to the replicant that has the contents of the received packet @@ -687,14 +688,14 @@ void AudioMixerClientData::setupCodecForReplicatedAgent(QSharedPointerreadString(); - qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID()) + if (codecString != _selectedCodecName) { + qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID()) << "-" << codecString; - const std::pair codec = AudioMixer::negotiateCodec({ codecString }); - setupCodec(codec.second, codec.first); + const std::pair codec = AudioMixer::negotiateCodec({ codecString }); + setupCodec(codec.second, codec.first); - _hasSetupCodecForUpstreamNode = true; - - // seek back to the beginning of the message so other readers are in the right place - message->seek(0); + // seek back to the beginning of the message so other readers are in the right place + message->seek(0); + } } diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 76a9cd6aa7..7a8690d8cc 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -184,8 +184,6 @@ private: bool _shouldMuteClient { false }; bool _requestsDomainListData { false }; - - bool _hasSetupCodecForUpstreamNode { false }; }; #endif // hifi_AudioMixerClientData_h diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 28ede7a77d..3a5116a2e9 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -66,7 +66,6 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : connect(nodeList.data(), &NodeList::nodeAdded, this, [this](const SharedNodePointer& node) { if (node->getType() == NodeType::DownstreamAvatarMixer) { getOrCreateClientData(node); - node->activatePublicSocket(); } }); } @@ -144,8 +143,8 @@ void AvatarMixer::optionallyReplicatePacket(ReceivedMessage& message, const Node std::unique_ptr packet; auto nodeList = DependencyManager::get(); - nodeList->eachMatchingNode([&](const SharedNodePointer& node) { - return node->getType() == NodeType::DownstreamAvatarMixer; + nodeList->eachMatchingNode([&](const SharedNodePointer& downstreamNode) { + return shouldReplicateTo(node, *downstreamNode); }, [&](const SharedNodePointer& node) { if (!packet) { // construct an NLPacket to send to the replicant that has the contents of the received packet @@ -165,10 +164,6 @@ void AvatarMixer::queueIncomingPacket(QSharedPointer message, S _queueIncomingPacketElapsedTime += (end - start); } - -AvatarMixer::~AvatarMixer() { -} - void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { if (destinationNode->getType() == NodeType::Agent && !destinationNode->isUpstream()) { QByteArray individualData = nodeData->getAvatar().identityByteArray(); @@ -429,8 +424,8 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { nodeList->eachMatchingNode([&](const SharedNodePointer& node) { // we relay avatar kill packets to agents that are not upstream // and downstream avatar mixers, if the node that was just killed was being replicated - return (node->getType() == NodeType::Agent && !node->isUpstream()) - || (killedNode->isReplicated() && node->getType() == NodeType::DownstreamAvatarMixer); + return (node->getType() == NodeType::Agent && !node->isUpstream()) || + (killedNode->isReplicated() && shouldReplicateTo(*killedNode, *node)); }, [&](const SharedNodePointer& node) { if (node->getType() == NodeType::Agent) { if (!killPacket) { @@ -862,7 +857,10 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node void AvatarMixer::domainSettingsRequestComplete() { auto nodeList = DependencyManager::get(); - nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::DownstreamAvatarMixer, NodeType::EntityScriptServer }); + nodeList->addSetOfNodeTypesToNodeInterestSet({ + NodeType::Agent, NodeType::EntityScriptServer, + NodeType::UpstreamAvatarMixer, NodeType::DownstreamAvatarMixer + }); // parse the settings to pull out the values we need parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject()); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 58d4487652..610da8ad57 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -28,7 +28,13 @@ class AvatarMixer : public ThreadedAssignment { Q_OBJECT public: AvatarMixer(ReceivedMessage& message); - ~AvatarMixer(); + + static bool shouldReplicateTo(const Node& from, const Node& to) { + return to.getType() == NodeType::DownstreamAvatarMixer && + to.getPublicSocket() != from.getPublicSocket() && + to.getLocalSocket() != from.getLocalSocket(); + } + public slots: /// runs the avatar mixer void run() override; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 8092518454..bbf68f0e22 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -79,13 +79,13 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, } } -int AvatarMixerSlave::sendReplicatedIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { - if (destinationNode->getType() == NodeType::DownstreamAvatarMixer) { +int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) { + if (AvatarMixer::shouldReplicateTo(agentNode, destinationNode)) { QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious - auto identityPacket = NLPacket::create(PacketType::ReplicatedAvatarIdentity); + auto identityPacket = NLPacketList::create(PacketType::ReplicatedAvatarIdentity, QByteArray(), true, true); identityPacket->write(individualData); - DependencyManager::get()->sendUnreliablePacket(*identityPacket, *destinationNode); + DependencyManager::get()->sendPacketList(std::move(identityPacket), destinationNode); _stats.numIdentityPackets++; return individualData.size(); } else { @@ -453,6 +453,10 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin nodeData->resetNumAvatarsSentLastFrame(); std::for_each(_begin, _end, [&](const SharedNodePointer& agentNode) { + if (!AvatarMixer::shouldReplicateTo(*agentNode, *node)) { + return; + } + // collect agents that we have avatar data for that we are supposed to replicate if (agentNode->getType() == NodeType::Agent && agentNode->getLinkedData() && agentNode->isReplicated()) { const AvatarMixerClientData* agentNodeData = reinterpret_cast(agentNode->getLinkedData()); @@ -479,7 +483,7 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin auto lastBroadcastTime = nodeData->getLastBroadcastTime(agentNode->getUUID()); if (lastBroadcastTime <= agentNodeData->getIdentityChangeTimestamp() || (start - lastBroadcastTime) >= REBROADCAST_IDENTITY_TO_DOWNSTREAM_EVERY_US) { - sendReplicatedIdentityPacket(agentNodeData, node); + sendReplicatedIdentityPacket(*agentNode, agentNodeData, *node); nodeData->setLastBroadcastTime(agentNode->getUUID(), start); } diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 69c707bbf1..bdddd5ceab 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -95,7 +95,7 @@ public: private: int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); - int sendReplicatedIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); + int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode); void broadcastAvatarDataToAgent(const SharedNodePointer& node); void broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node); diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 033e9e0255..710fd81316 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -504,6 +504,10 @@ Function PostInstallOptionsPage ; set the checkbox state depending on what is present in the registry !insertmacro SetPostInstallOption $LaunchServerNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED} + ${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE + ${IfNot} $substringResult == "" + ${NSD_SetState} $LaunchServerNowCheckbox ${BST_UNCHECKED} + ${EndIf} IntOp $CurrentOffset $CurrentOffset + 15 ${EndIf} @@ -514,6 +518,10 @@ Function PostInstallOptionsPage ; set the checkbox state depending on what is present in the registry !insertmacro SetPostInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED} + ${StrContains} $substringResult "/forceNoLaunchClient" $CMDLINE + ${IfNot} $substringResult == "" + ${NSD_SetState} $LaunchClientNowCheckbox ${BST_UNCHECKED} + ${EndIf} ${EndIf} ${If} @PR_BUILD@ == 1 diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index c7dc97f595..bc67a31c02 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1339,6 +1339,7 @@ { "name": "broadcasting", "label": "Broadcasting", + "restart": false, "settings": [ { "name": "users", @@ -1395,6 +1396,46 @@ ] } ] + }, + { + "name": "upstream_servers", + "label": "Broadcasting Servers", + "assignment-types": [0,1], + "type": "table", + "advanced": true, + "can_add_new_rows": true, + "help": "Servers that broadcast data to this domain", + "numbered": false, + "columns": [ + { + "name": "address", + "label": "Address", + "can_set": true + }, + { + "name": "port", + "label": "Port", + "can_set": true + }, + { + "name": "server_type", + "label": "Server Type", + "type": "select", + "placeholder": "Audio Mixer", + "default": "Audio Mixer", + "can_set": true, + "options": [ + { + "value": "Audio Mixer", + "label": "Audio Mixer" + }, + { + "value": "Avatar Mixer", + "label": "Avatar Mixer" + } + ] + } + ] } ] } diff --git a/domain-server/resources/web/header.html b/domain-server/resources/web/header.html index 0dc08e6e31..a37e9a6ff0 100644 --- a/domain-server/resources/web/header.html +++ b/domain-server/resources/web/header.html @@ -10,6 +10,7 @@ +