diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 8c6478b59f..4c034721d1 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -195,8 +195,8 @@ void AssignmentClientMonitor::checkSpares() { SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId); childNode->activateLocalSocket(); - QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); - nodeList->writeUnverifiedDatagram(diePacket, childNode); + auto diePacket { NLPacket::create(PacketType::StopNode); } + nodeList->sendPacket(diePacket, childNode); } } } @@ -229,8 +229,9 @@ void AssignmentClientMonitor::readPendingDatagrams() { } else { // tell unknown assignment-client child to exit. qDebug() << "asking unknown child to exit."; - QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); - nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr); + + auto diePacket { NL::create(PacketType::StopNode); } + nodeList->sendPacket(diePacket, childNode); } } } diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index abbf8071f7..055294b9bf 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -509,24 +509,20 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { if (sendData) { auto nodeList = DependencyManager::get<NodeList>(); - int numBytesEnvPacketHeader = nodeList->populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment); - char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader; + auto envPacket = NLPacket::create(PacketType::AudioEnvironment); unsigned char bitset = 0; if (hasReverb) { setAtBit(bitset, HAS_REVERB_BIT); } - memcpy(envDataAt, &bitset, sizeof(unsigned char)); - envDataAt += sizeof(unsigned char); + envPacket.write(&bitset, sizeof(unsigned char)); if (hasReverb) { - memcpy(envDataAt, &reverbTime, sizeof(float)); - envDataAt += sizeof(float); - memcpy(envDataAt, &wetLevel, sizeof(float)); - envDataAt += sizeof(float); + envPacket.write(&reverbTime, sizeof(float)); + envPacket.write(&wetLevel, sizeof(float)); } - nodeList->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node); + nodeList->sendPacket(envPacket, node); } } @@ -712,8 +708,6 @@ void AudioMixer::run() { QElapsedTimer timer; timer.start(); - char clientMixBuffer[MAX_PACKET_SIZE]; - int usecToSleep = AudioConstants::NETWORK_FRAME_USECS; const int TRAILING_AVERAGE_FRAMES = 100; @@ -791,8 +785,8 @@ void AudioMixer::run() { // if the stream should be muted, send mute packet if (nodeData->getAvatarAudioStream() && shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) { - QByteArray packet = nodeList->byteArrayWithPopulatedHeader(PacketTypeNoisyMute); - nodeList->writeDatagram(packet, node); + auto mutePacket { NLPacket::create(PacketType::NoisyMute); } + nodeList->sendPacket(mutePacket, node); } if (node->getType() == NodeType::Agent && node->getActiveSocket() @@ -800,41 +794,36 @@ void AudioMixer::run() { int streamsMixed = prepareMixForListeningNode(node.data()); - char* mixDataAt; + std::unique_ptr<NLPacket> mixPacket; + if (streamsMixed > 0) { - // pack header - int numBytesMixPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio); - mixDataAt = clientMixBuffer + numBytesMixPacketHeader; + int mixPacketBytes = sizeof(quint16) + AudioConstants::NETWORK_FRAME_BYTES_STEREO; + mixPacket = NLPacket::create(PacketType::MixedAudio); // pack sequence number quint16 sequence = nodeData->getOutgoingSequenceNumber(); - memcpy(mixDataAt, &sequence, sizeof(quint16)); - mixDataAt += sizeof(quint16); + mixPacket.write(&sequence, sizeof(quint16)); // pack mixed audio samples - memcpy(mixDataAt, _mixSamples, AudioConstants::NETWORK_FRAME_BYTES_STEREO); - mixDataAt += AudioConstants::NETWORK_FRAME_BYTES_STEREO; + mixPacket.write(mixSamples, AudioConstants::NETWORK_FRAME_BYTES_STEREO); } else { - // pack header - int numBytesPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame); - mixDataAt = clientMixBuffer + numBytesPacketHeader; + int silentPacketBytes = sizeof(quint16) + sizeof(quint16); + mixPacket = NLPacket::create(PacketType::SilentAudioFrame); // pack sequence number quint16 sequence = nodeData->getOutgoingSequenceNumber(); - memcpy(mixDataAt, &sequence, sizeof(quint16)); - mixDataAt += sizeof(quint16); + mixPacket.write(&sequence, sizeof(quint16)); // pack number of silent audio samples quint16 numSilentSamples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; - memcpy(mixDataAt, &numSilentSamples, sizeof(quint16)); - mixDataAt += sizeof(quint16); + mixPacket.write(&numSilentSamples, sizeof(quint16)); } // Send audio environment sendAudioEnvironmentPacket(node); // send mixed audio packet - nodeList->writeDatagram(clientMixBuffer, mixDataAt - clientMixBuffer, node); + nodeList->sendPacket(mixPacket, node); nodeData->incrementOutgoingMixedAudioSequenceNumber(); // send an audio stream stats packet if it's time diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 6879da8c08..05dc54665f 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -111,7 +111,7 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend() { QHash<QUuid, PositionalAudioStream*>::ConstIterator i; for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) { PositionalAudioStream* stream = i.value(); - + if (stream->popFrames(1, true) > 0) { stream->updateLastPopOutputLoudnessAndTrailingLoudness(); } @@ -147,53 +147,46 @@ void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& // since audio stream stats packets are sent periodically, this is a good place to remove our dead injected streams. removeDeadInjectedStreams(); - char packet[MAX_PACKET_SIZE]; auto nodeList = DependencyManager::get<NodeList>(); - // The append flag is a boolean value that will be packed right after the header. The first packet sent + // The append flag is a boolean value that will be packed right after the header. The first packet sent // inside this method will have 0 for this flag, while every subsequent packet will have 1 for this flag. // The sole purpose of this flag is so the client can clear its map of injected audio stream stats when // it receives a packet with an appendFlag of 0. This prevents the buildup of dead audio stream stats in the client. quint8 appendFlag = 0; - // pack header - int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeAudioStreamStats); - char* headerEndAt = packet + numBytesPacketHeader; - - // calculate how many stream stat structs we can fit in each packet - const int numStreamStatsRoomFor = (MAX_PACKET_SIZE - numBytesPacketHeader - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats); - // pack and send stream stats packets until all audio streams' stats are sent int numStreamStatsRemaining = _audioStreams.size(); QHash<QUuid, PositionalAudioStream*>::ConstIterator audioStreamsIterator = _audioStreams.constBegin(); while (numStreamStatsRemaining > 0) { - char* dataAt = headerEndAt; + auto statsPacket { NLPacket::create(PacketType::AudioStreamStats); } - // pack the append flag - memcpy(dataAt, &appendFlag, sizeof(quint8)); + // pack the append flag in this packet + statsPacket.write(&appendFlag, sizeof(quint8)); appendFlag = 1; - dataAt += sizeof(quint8); + + int numStreamStatsRoomFor = (statsPacket.size() - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats); // calculate and pack the number of stream stats to follow quint16 numStreamStatsToPack = std::min(numStreamStatsRemaining, numStreamStatsRoomFor); - memcpy(dataAt, &numStreamStatsToPack, sizeof(quint16)); - dataAt += sizeof(quint16); + statsPacket.write(&numStreamStatsToPack, sizeof(quint16)); // pack the calculated number of stream stats for (int i = 0; i < numStreamStatsToPack; i++) { PositionalAudioStream* stream = audioStreamsIterator.value(); + stream->perSecondCallbackForUpdatingStats(); + AudioStreamStats streamStats = stream->getAudioStreamStats(); - memcpy(dataAt, &streamStats, sizeof(AudioStreamStats)); - dataAt += sizeof(AudioStreamStats); + statsPacket.write(&streamStats, sizeof(AudioStreamStats)); audioStreamsIterator++; } numStreamStatsRemaining -= numStreamStatsToPack; // send the current packet - nodeList->writeDatagram(packet, dataAt - packet, destinationNode); + nodeList->sendPacket(statsPacket, destinationNode); } } @@ -220,7 +213,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() const { result["downstream"] = downstreamStats; AvatarAudioStream* avatarAudioStream = getAvatarAudioStream(); - + if (avatarAudioStream) { QJsonObject upstreamStats; @@ -246,7 +239,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() const { } else { result["upstream"] = "mic unknown"; } - + QHash<QUuid, PositionalAudioStream*>::ConstIterator i; QJsonArray injectorArray; for (i = _audioStreams.constBegin(); i != _audioStreams.constEnd(); i++) { @@ -270,7 +263,7 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() const { upstreamStats["min_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMin); upstreamStats["max_gap_30s"] = formatUsecTime(streamStats._timeGapWindowMax); upstreamStats["avg_gap_30s"] = formatUsecTime(streamStats._timeGapWindowAverage); - + injectorArray.push_back(upstreamStats); } } @@ -304,7 +297,7 @@ void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamS streamStats._desiredJitterBufferFrames, streamStats._framesAvailableAverage, streamStats._framesAvailable); - + printf(" Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u\n", streamStats._starveCount, streamStats._consecutiveNotMixedCount, @@ -323,10 +316,10 @@ void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamS } -PerListenerSourcePairData* AudioMixerClientData::getListenerSourcePairData(const QUuid& sourceUUID) { +PerListenerSourcePairData* AudioMixerClientData::getListenerSourcePairData(const QUuid& sourceUUID) { if (!_listenerSourcePairData.contains(sourceUUID)) { PerListenerSourcePairData* newData = new PerListenerSourcePairData(); _listenerSourcePairData[sourceUUID] = newData; } - return _listenerSourcePairData[sourceUUID]; + return _listenerSourcePairData[sourceUUID]; } diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 7ef9c42524..af9328cd12 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -30,7 +30,7 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer"; -const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 60; +const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 60; const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000; AvatarMixer::AvatarMixer(const QByteArray& packet) : @@ -63,73 +63,70 @@ const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f; // 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present // if the avatar is not in view or in the keyhole. void AvatarMixer::broadcastAvatarData() { - + int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp; - + ++_numStatFrames; - + const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f; const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f; - + const float RATIO_BACK_OFF = 0.02f; - + const int TRAILING_AVERAGE_FRAMES = 100; int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; - + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; - + // NOTE: The following code calculates the _performanceThrottlingRatio based on how much the avatar-mixer was // able to sleep. This will eventually be used to ask for an additional avatar-mixer to help out. Currently the value // is unused as it is assumed this should not be hit before the avatar-mixer hits the desired bandwidth limit per client. // It is reported in the domain-server stats for the avatar-mixer. - + _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) + (idleTime * CURRENT_FRAME_RATIO / (float) AVATAR_DATA_SEND_INTERVAL_MSECS); - + float lastCutoffRatio = _performanceThrottlingRatio; bool hasRatioChanged = false; - + if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) { if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { // we're struggling - change our performance throttling ratio _performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio)); - + qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; hasRatioChanged = true; } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) { // we've recovered and can back off the performance throttling _performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF; - + if (_performanceThrottlingRatio < 0) { _performanceThrottlingRatio = 0; } - + qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; hasRatioChanged = true; } - + if (hasRatioChanged) { framesSinceCutoffEvent = 0; } } - + if (!hasRatioChanged) { ++framesSinceCutoffEvent; } - - static QByteArray mixedAvatarByteArray; - + auto nodeList = DependencyManager::get<NodeList>(); - int numPacketHeaderBytes = nodeList->populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData); - - // setup for distributed random floating point values + + // setup for distributed random floating point values std::random_device randomDevice; std::mt19937 generator(randomDevice()); std::uniform_real_distribution<float> distribution; - + nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { if (!node->getLinkedData()) { @@ -150,25 +147,22 @@ void AvatarMixer::broadcastAvatarData() { return; } ++_sumListeners; - - // reset packet pointers for this node - mixedAvatarByteArray.resize(numPacketHeaderBytes); AvatarData& avatar = nodeData->getAvatar(); glm::vec3 myPosition = avatar.getPosition(); // reset the internal state for correct random number distribution distribution.reset(); - + // reset the max distance for this frame float maxAvatarDistanceThisFrame = 0.0f; - + // reset the number of sent avatars nodeData->resetNumAvatarsSentLastFrame(); // keep a counter of the number of considered avatars int numOtherAvatars = 0; - + // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; @@ -185,7 +179,7 @@ void AvatarMixer::broadcastAvatarData() { // bandwidth to this node. We do this once a second, which is also the window for // the bandwidth reported by node->getOutboundBandwidth(); if (nodeData->getNumFramesSinceFRDAdjustment() > AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) { - + const float FRD_ADJUSTMENT_ACCEPTABLE_RATIO = 0.8f; const float HYSTERISIS_GAP = (1 - FRD_ADJUSTMENT_ACCEPTABLE_RATIO); const float HYSTERISIS_MIDDLE_PERCENTAGE = (1 - (HYSTERISIS_GAP * 0.5f)); @@ -195,22 +189,22 @@ void AvatarMixer::broadcastAvatarData() { if (avatarDataRateLastSecond > _maxKbpsPerNode) { - // is the FRD greater than the farthest avatar? + // is the FRD greater than the farthest avatar? // if so, before we calculate anything, set it to that distance currentFullRateDistance = std::min(currentFullRateDistance, nodeData->getMaxAvatarDistance()); // we're adjusting the full rate distance to target a bandwidth in the middle // of the hysterisis gap - currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; - + currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; + nodeData->setFullRateDistance(currentFullRateDistance); nodeData->resetNumFramesSinceFRDAdjustment(); - } else if (currentFullRateDistance < nodeData->getMaxAvatarDistance() + } else if (currentFullRateDistance < nodeData->getMaxAvatarDistance() && avatarDataRateLastSecond < _maxKbpsPerNode * FRD_ADJUSTMENT_ACCEPTABLE_RATIO) { - // we are constrained AND we've recovered to below the acceptable ratio + // we are constrained AND we've recovered to below the acceptable ratio // lets adjust the full rate distance to target a bandwidth in the middle of the hyterisis gap currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; - + nodeData->setFullRateDistance(currentFullRateDistance); nodeData->resetNumFramesSinceFRDAdjustment(); } @@ -218,6 +212,9 @@ void AvatarMixer::broadcastAvatarData() { nodeData->incrementNumFramesSinceFRDAdjustment(); } + // setup a PacketList for the avatarPackets + PacketList avatarPacketList(PacketType::AvatarData); + // this is an AGENT we have received head data from // send back a packet with other active node data to this node nodeList->eachMatchingNode( @@ -233,16 +230,16 @@ void AvatarMixer::broadcastAvatarData() { }, [&](const SharedNodePointer& otherNode) { ++numOtherAvatars; - + AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData()); MutexTryLocker lock(otherNodeData->getMutex()); if (!lock.isLocked()) { return; } - + AvatarData& otherAvatar = otherNodeData->getAvatar(); // Decide whether to send this avatar's data based on it's distance from us - + // The full rate distance is the distance at which EVERY update will be sent for this avatar // at twice the full rate distance, there will be a 50% chance of sending this avatar's update glm::vec3 otherPosition = otherAvatar.getPosition(); @@ -251,24 +248,24 @@ void AvatarMixer::broadcastAvatarData() { // potentially update the max full rate distance for this frame maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar); - if (distanceToAvatar != 0.0f + if (distanceToAvatar != 0.0f && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { return; } PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); - PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData); + PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData); if (lastSeqToReceiver > lastSeqFromSender) { // Did we somehow get out of order packets from the sender? // We don't expect this to happen - in RELEASE we add this to a trackable stat // and in DEBUG we crash on the assert - + otherNodeData->incrementNumOutOfOrderSends(); assert(false); } - + // make sure we haven't already sent this data from this sender to this receiver // or that somehow we haven't sent if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { @@ -277,76 +274,70 @@ void AvatarMixer::broadcastAvatarData() { } else if (lastSeqFromSender - lastSeqToReceiver > 1) { // this is a skip - we still send the packet but capture the presence of the skip so we see it happening ++numAvatarsWithSkippedFrames; - } - + } + // we're going to send this avatar - + // increment the number of avatars sent to this reciever nodeData->incrementNumAvatarsSentLastFrame(); - + // set the last sent sequence number for this sender on the receiver - nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), - otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData)); + nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), + otherNode->getLastSequenceNumberForPacketType(PacketType::AvatarData)); - QByteArray avatarByteArray; - avatarByteArray.append(otherNode->getUUID().toRfc4122()); - avatarByteArray.append(otherAvatar.toByteArray()); - - if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) { - nodeList->writeDatagram(mixedAvatarByteArray, node); + // start a new segment in the PacketList for this avatar + avatarPacketList.startSegment(); + + numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122()); + numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray()); + + avatarPacketList.endSegment(); - numAvatarDataBytes += mixedAvatarByteArray.size(); - - // reset the packet - mixedAvatarByteArray.resize(numPacketHeaderBytes); - } - - // copy the avatar into the mixedAvatarByteArray packet - mixedAvatarByteArray.append(avatarByteArray); - // if the receiving avatar has just connected make sure we send out the mesh and billboard // for this avatar (assuming they exist) bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets(); - + // we will also force a send of billboard or identity packet // if either has changed in the last frame - + if (otherNodeData->getBillboardChangeTimestamp() > 0 && (forceSend || otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - QByteArray billboardPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); - billboardPacket.append(otherNode->getUUID().toRfc4122()); - billboardPacket.append(otherNodeData->getAvatar().getBillboard()); - nodeList->writeDatagram(billboardPacket, node); - + auto billboardPacket { NLPacket::create(PacketType::AvatarBillboard); } + billboardPacket.write(otherNode->getUUID().toRfc4122()); + billboardPacket.write(otherNodeData->getAvatar().getBillboard()); + + nodeList->sendPacket(billboardPacket, node); + ++_sumBillboardPackets; } - + if (otherNodeData->getIdentityChangeTimestamp() > 0 && (forceSend || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - - QByteArray identityPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); - + + auto identityPacket { NLPacket::create(PacketType::AvatarIdentity); } + QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122()); - identityPacket.append(individualData); - - nodeList->writeDatagram(identityPacket, node); - + + identityPacket.write(individualData); + + nodeList->sendPacket(identityPacket, node); + ++_sumIdentityPackets; } }); - - // send the last packet - nodeList->writeDatagram(mixedAvatarByteArray, node); - + + // send the avatar data PacketList + nodeList->sendPacketList(avatarPacketList, node); + // record the bytes sent for other avatar data in the AvatarMixerClientData - nodeData->recordSentAvatarData(numAvatarDataBytes + mixedAvatarByteArray.size()); - + nodeData->recordSentAvatarData(numAvatarDataBytes); + // record the number of avatars held back this frame nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); @@ -359,7 +350,7 @@ void AvatarMixer::broadcastAvatarData() { } } ); - + _lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch(); } @@ -370,9 +361,9 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { // this was an avatar we were sending to other people // send a kill packet for it to our other nodes - QByteArray killPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeKillAvatar); - killPacket += killedNode->getUUID().toRfc4122(); - + auto killPacket { NLPacket::create(PacketType::KillAvatar); } + killPacket.write(killedNode->getUUID().toRfc4122()); + nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::Agent); // we also want to remove sequence number data for this avatar on our other avatars @@ -402,9 +393,9 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { void AvatarMixer::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; - + auto nodeList = DependencyManager::get<NodeList>(); - + while (readAvailableDatagram(receivedPacket, senderSockAddr)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { switch (packetTypeForPacket(receivedPacket)) { @@ -413,14 +404,14 @@ void AvatarMixer::readPendingDatagrams() { break; } case PacketTypeAvatarIdentity: { - + // check if we have a matching node in our list SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket); - + if (avatarNode && avatarNode->getLinkedData()) { AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(avatarNode->getLinkedData()); AvatarData& avatar = nodeData->getAvatar(); - + // parse the identity packet and update the change timestamp if appropriate if (avatar.hasIdentityChangedAfterParsing(receivedPacket)) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); @@ -430,20 +421,20 @@ void AvatarMixer::readPendingDatagrams() { break; } case PacketTypeAvatarBillboard: { - + // check if we have a matching node in our list SharedNodePointer avatarNode = nodeList->sendingNodeForPacket(receivedPacket); - + if (avatarNode && avatarNode->getLinkedData()) { AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(avatarNode->getLinkedData()); AvatarData& avatar = nodeData->getAvatar(); - + // parse the billboard packet and update the change timestamp if appropriate if (avatar.hasBillboardChangedAfterParsing(receivedPacket)) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch()); } - + } break; } @@ -463,15 +454,15 @@ void AvatarMixer::readPendingDatagrams() { void AvatarMixer::sendStatsPacket() { QJsonObject statsObject; statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames; - + statsObject["average_billboard_packets_per_frame"] = (float) _sumBillboardPackets / (float) _numStatFrames; statsObject["average_identity_packets_per_frame"] = (float) _sumIdentityPackets / (float) _numStatFrames; - + statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100; statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio; QJsonObject avatarsObject; - + auto nodeList = DependencyManager::get<NodeList>(); // add stats for each listerner nodeList->eachNode([&](const SharedNodePointer& node) { @@ -482,7 +473,7 @@ void AvatarMixer::sendStatsPacket() { // add the key to ask the domain-server for a username replacement, if it has it avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth(); - + AvatarMixerClientData* clientData = static_cast<AvatarMixerClientData*>(node->getLinkedData()); if (clientData) { MutexTryLocker lock(clientData->getMutex()); @@ -490,9 +481,9 @@ void AvatarMixer::sendStatsPacket() { clientData->loadJSONStats(avatarStats); // add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only - avatarStats["delta_full_vs_avatar_data_kbps"] = + avatarStats["delta_full_vs_avatar_data_kbps"] = avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble(); - } + } } avatarsObject[uuidStringWithoutCurlyBraces(node->getUUID())] = avatarStats; @@ -500,7 +491,7 @@ void AvatarMixer::sendStatsPacket() { statsObject["avatars"] = avatarsObject; ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); - + _sumListeners = 0; _sumBillboardPackets = 0; _sumIdentityPackets = 0; @@ -509,41 +500,41 @@ void AvatarMixer::sendStatsPacket() { void AvatarMixer::run() { ThreadedAssignment::commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer); - + auto nodeList = DependencyManager::get<NodeList>(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - + nodeList->linkedDataCreateCallback = [] (Node* node) { node->setLinkedData(new AvatarMixerClientData()); }; - + // setup the timer that will be fired on the broadcast thread _broadcastTimer = new QTimer; _broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS); _broadcastTimer->moveToThread(&_broadcastThread); - + // connect appropriate signals and slots connect(_broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData, Qt::DirectConnection); connect(&_broadcastThread, SIGNAL(started()), _broadcastTimer, SLOT(start())); // wait until we have the domain-server settings, otherwise we bail DomainHandler& domainHandler = nodeList->getDomainHandler(); - + qDebug() << "Waiting for domain settings from domain-server."; - + // block until we get the settingsRequestComplete signal QEventLoop loop; connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); domainHandler.requestDomainSettings(); loop.exec(); - + if (domainHandler.getSettingsObject().isEmpty()) { qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; setFinished(true); return; } - + // parse the settings to pull out the values we need parseDomainServerSettings(domainHandler.getSettingsObject()); @@ -554,13 +545,13 @@ void AvatarMixer::run() { void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { const QString AVATAR_MIXER_SETTINGS_KEY = "avatar_mixer"; const QString NODE_SEND_BANDWIDTH_KEY = "max_node_send_bandwidth"; - + const float DEFAULT_NODE_SEND_BANDWIDTH = 1.0f; QJsonValue nodeBandwidthValue = domainSettings[AVATAR_MIXER_SETTINGS_KEY].toObject()[NODE_SEND_BANDWIDTH_KEY]; if (!nodeBandwidthValue.isDouble()) { qDebug() << NODE_SEND_BANDWIDTH_KEY << "is not a double - will continue with default value"; - } + } _maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA; - qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps."; + qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps."; } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 35be97d610..63e5f5b1eb 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -573,33 +573,34 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); + QUuid connectUUID; + packetStream >> connectUUID; + parseNodeDataFromByteArray(packetStream, nodeType, publicSockAddr, localSockAddr, senderSockAddr); - QUuid packetUUID = uuidFromPacketHeader(packet); - // check if this connect request matches an assignment in the queue - bool isAssignment = _pendingAssignedNodes.contains(packetUUID); + bool isAssignment = _pendingAssignedNodes.contains(connectUUID); SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer(); PendingAssignedNodeData* pendingAssigneeData = NULL; if (isAssignment) { - pendingAssigneeData = _pendingAssignedNodes.value(packetUUID); + pendingAssigneeData = _pendingAssignedNodes.value(connectUUID); if (pendingAssigneeData) { matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(pendingAssigneeData->getAssignmentUUID(), nodeType); if (matchingQueuedAssignment) { - qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID) + qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(connectUUID) << "matches unfulfilled assignment" << uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID()); // remove this unique assignment deployment from the hash of pending assigned nodes // cleanup of the PendingAssignedNodeData happens below after the node has been added to the LimitedNodeList - _pendingAssignedNodes.remove(packetUUID); + _pendingAssignedNodes.remove(connectUUID); } else { // this is a node connecting to fulfill an assignment that doesn't exist // don't reply back to them so they cycle back and re-request an assignment - qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID); + qDebug() << "No match for assignment deployed with" << uuidStringWithoutCurlyBraces(connectUUID); return; } } @@ -621,9 +622,8 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock QByteArray utfString = reason.toUtf8(); int payloadSize = utfString.size(); - auto connectionDeniedPacket = NodeListPacket::make(PacketType::DomainConnectionDenied, payloadSize); - - memcpy(connectionDeniedPacket.payload().data(), utfString.data(), utfString.size()); + auto connectionDeniedPacket = NLPacket::make(PacketType::DomainConnectionDenied, payloadSize); + connectionDeniedPacket.write(utfString); // tell client it has been refused. limitedNodeList->sendPacket(std::move(connectionDeniedPacket, senderSockAddr); @@ -638,18 +638,18 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock QUuid nodeUUID; HifiSockAddr discoveredSocket = senderSockAddr; - SharedNetworkPeer connectedPeer = _icePeers.value(packetUUID); + SharedNetworkPeer connectedPeer = _icePeers.value(connectUUID); if (connectedPeer) { // this user negotiated a connection with us via ICE, so re-use their ICE client ID - nodeUUID = packetUUID; + nodeUUID = connectUUID; if (connectedPeer->getActiveSocket()) { // set their discovered socket to whatever the activated socket on the network peer object was discoveredSocket = *connectedPeer->getActiveSocket(); } } else { - // we got a packetUUID we didn't recognize, just add the node + // we got a connectUUID we didn't recognize, just add the node with a new UUID nodeUUID = QUuid::createUuid(); } diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 36b0345d34..96bca798e3 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -153,54 +153,56 @@ void AudioInjector::injectToMixer() { // make sure we actually have samples downloaded to inject if (_audioData.size()) { + auto audioPacket { NLPacket::create(PacketType::InjectAudio); } + // setup the packet for injected audio - QByteArray injectAudioPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeInjectAudio); - QDataStream packetStream(&injectAudioPacket, QIODevice::Append); + QDataStream audioPacketStream(&audioPacket); // pack some placeholder sequence number for now - int numPreSequenceNumberBytes = injectAudioPacket.size(); - packetStream << (quint16)0; + audioPacketStream << (quint16) 0; // pack stream identifier (a generated UUID) - packetStream << QUuid::createUuid(); + audioPacketStream << QUuid::createUuid(); // pack the stereo/mono type of the stream - packetStream << _options.stereo; + audioPacketStream << _options.stereo; // pack the flag for loopback uchar loopbackFlag = (uchar) true; - packetStream << loopbackFlag; + audioPacketStream << loopbackFlag; // pack the position for injected audio - int positionOptionOffset = injectAudioPacket.size(); - packetStream.writeRawData(reinterpret_cast<const char*>(&_options.position), + int positionOptionOffset = audioPacket.pos(); + audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.position), sizeof(_options.position)); // pack our orientation for injected audio - int orientationOptionOffset = injectAudioPacket.size(); - packetStream.writeRawData(reinterpret_cast<const char*>(&_options.orientation), + int orientationOptionOffset = audioPacket.pos(); + audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.orientation), sizeof(_options.orientation)); // pack zero for radius float radius = 0; - packetStream << radius; + audioPacketStream << radius; // pack 255 for attenuation byte - int volumeOptionOffset = injectAudioPacket.size(); + int volumeOptionOffset = audioPacket.pos(); quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; - packetStream << volume; + audioPacketStream << volume; - packetStream << _options.ignorePenumbra; + audioPacketStream << _options.ignorePenumbra; + + int audioDataOffset = audioPacket.pos(); QElapsedTimer timer; timer.start(); int nextFrame = 0; - int numPreAudioDataBytes = injectAudioPacket.size(); bool shouldLoop = _options.loop; // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks quint16 outgoingInjectedAudioSequenceNumber = 0; + while (_currentSendPosition < _audioData.size() && !_shouldStop) { int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, @@ -214,31 +216,29 @@ void AudioInjector::injectToMixer() { } _loudness /= (float)(bytesToCopy / sizeof(int16_t)); - memcpy(injectAudioPacket.data() + positionOptionOffset, - &_options.position, - sizeof(_options.position)); - memcpy(injectAudioPacket.data() + orientationOptionOffset, - &_options.orientation, - sizeof(_options.orientation)); - volume = MAX_INJECTOR_VOLUME * _options.volume; - memcpy(injectAudioPacket.data() + volumeOptionOffset, &volume, sizeof(volume)); + audioPacket.seek(positionOptionOffset); + audioPacket.write(&_options.position, sizeof(_options.position)); - // resize the QByteArray to the right size - injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy); + audioPacket.seek(orientationOptionOffset); + audioPacket.write(&_options.orientation, sizeof(_options.orientation)); + + volume = MAX_INJECTOR_VOLUME * _options.volume; + audioPacket.seek(volumeOptionOffset); + audioPacket.write(&volume, sizeof(volume)); + + audioPacket.seek(audioDataOffset); // pack the sequence number - memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes, - &outgoingInjectedAudioSequenceNumber, sizeof(quint16)); + audioPacket.write(&outgoingInjectedAudioSequenceNumber, sizeof(quint16)); // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet - memcpy(injectAudioPacket.data() + numPreAudioDataBytes, - _audioData.data() + _currentSendPosition, bytesToCopy); + audioPacket.write(_audioData.data() + _currentSendPosition, bytesToCopy); // grab our audio mixer from the NodeList, if it exists SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); // send off this audio packet - nodeList->writeDatagram(injectAudioPacket, audioMixer); + nodeList->sendUnreliablePacket(audioPacket, audioMixer); outgoingInjectedAudioSequenceNumber++; _currentSendPosition += bytesToCopy; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 4eaa61b5e3..235427d705 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -508,22 +508,22 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t } } -unsigned LimitedNodeList::broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) { - unsigned n = 0; +// unsigned LimitedNodeList::broadcastToNodes(PacketList& packetList, const NodeSet& destinationNodeTypes) { + // unsigned n = 0; + // + // eachNode([&](const SharedNodePointer& node){ + // if (destinationNodeTypes.contains(node->getType())) { + // writeDatagram(packet, node); + // ++n; + // } + // }); + // + // return n; +// } - eachNode([&](const SharedNodePointer& node){ - if (destinationNodeTypes.contains(node->getType())) { - writeDatagram(packet, node); - ++n; - } - }); - - return n; -} - -NodeListPacket&& LimitedNodeList::constructPingPacket(PingType_t pingType) { +NLPacket&& LimitedNodeList::constructPingPacket(PingType_t pingType) { int packetSize = sizeof(PingType_t) + sizeof(quint64); - NodeListPacket pingPacket = NodeListPacket::create(PacketType::Ping, packetSize); + auto pingPacket { NLPacket::create(PacketType::Ping, packetSize); } QDataStream packetStream(&pingPacket.payload(), QIODevice::Append); @@ -533,7 +533,7 @@ NodeListPacket&& LimitedNodeList::constructPingPacket(PingType_t pingType) { return pingPacket; } -NodeListPacket&& LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacket) { +NLPacket&& LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacket) { QDataStream pingPacketStream(pingPacket); pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket)); @@ -542,41 +542,41 @@ NodeListPacket&& LimitedNodeList::constructPingReplyPacket(const QByteArray& pin quint64 timeFromOriginalPing; pingPacketStream >> timeFromOriginalPing; - + int packetSize = sizeof(PingType_t) + sizeof(quint64) + sizeof(quint64); - - NodeListPacket replyPacket = NodeListPacket::create(PacketType::Ping, packetSize); - + + auto replyPacket { NLPacket::create(PacketType::Ping, packetSize); } + QDataStream packetStream(&replyPacket, QIODevice::Append); packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow(); return replyPacket; } -NodeListPacket&& constructICEPingPacket(PingType_t pingType, const QUuid& iceID) { +NLPacket&& constructICEPingPacket(PingType_t pingType, const QUuid& iceID) { int packetSize = NUM_BYTES_RFC4122_UUID + sizeof(PingType_t); - - NodeListPacket icePingPacket = NodeListPacket::create(PacketType::ICEPing, packetSize); - + + auto icePingPacket { NLPacket::create(PacketType::ICEPing, packetSize); } + icePingPacket.payload().replace(0, NUM_BYTES_RFC4122_UUID, iceID.toRfc4122().data()); memcpy(icePingPacket.payload() + NUM_BYTES_RFC4122_UUID, &pingType, sizeof(PingType_t)); - + return icePingPacket; } -NodeListPacket&& constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID) { +NLPacket&& constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID) { // pull out the ping type so we can reply back with that PingType_t pingType; - + memcpy(&pingType, pingPacket.data() + NUM_BYTES_RFC4122_UUID, sizeof(PingType_t)); - + int packetSize = NUM_BYTES_RFC4122_UUID + sizeof(PingType_t); - NodeListPacket icePingReplyPacket = NodeListPacket::create(PacketType::ICEPingReply, packetSize); - + auto icePingReplyPacket { NLPacket::create(PacketType::ICEPingReply, packetSize); } + // pack the ICE ID and then the ping type memcpy(icePingReplyPacket.payload(), iceID.toRfc4122().data(), NUM_BYTES_RFC4122_UUID); memcpy(icePingReplyPacket.payload() + NUM_BYTES_RFC4122_UUID, &pingType, sizeof(PingType_t)); - + return icePingReplyPacket; } diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 785933120f..b1905fd593 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -141,6 +141,14 @@ public: // // qint64 writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode, // const HifiSockAddr& overridenSockAddr = HifiSockAddr()); +// + + qint64 sendUnreliablePacket(NLPacket& packet, const SharedNodePointer& destinationNode) {}; + qint64 sendUnreliablePacket(NLPacket& packet, const HifiSockAddr& sockAddr) {}; + qint64 sendPacket(NLPacket&& packet, const SharedNodePointer& destinationNode) {}; + qint64 sendPacket(NLPacket&& packet, const HifiSockAddr& sockAddr) {}; + qint64 sendPacketList(PacketList& packetList, const SharedNodePointer& destinationNode) {}; + qint64 sendPacketList(PacketList& packetList, const HifiSockAddr& sockAddr) {}; void (*linkedDataCreateCallback)(Node *); @@ -165,17 +173,17 @@ public: int updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray& packet); int findNodeAndUpdateWithDataFromPacket(const QByteArray& packet); - unsigned broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes); + unsigned broadcastToNodes(PacketList& packetList, const NodeSet& destinationNodeTypes) {}; SharedNodePointer soloNodeOfType(char nodeType); void getPacketStats(float &packetsPerSecond, float &bytesPerSecond); void resetPacketStats(); - NodeListPacket&& constructPingPacket(PingType_t pingType = PingType::Agnostic); - NodeListPacket&& constructPingReplyPacket(const QByteArray& pingPacket); - - NodeListPacket&& constructICEPingPacket(PingType_t pingType, const QUuid& iceID); - NodeListPacket&& constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID); + NLPacket&& constructPingPacket(PingType_t pingType = PingType::Agnostic); + NLPacket&& constructPingReplyPacket(const QByteArray& pingPacket); + + NLPacket&& constructICEPingPacket(PingType_t pingType, const QUuid& iceID); + NLPacket&& constructICEPingReplyPacket(const QByteArray& pingPacket, const QUuid& iceID); virtual bool processSTUNResponse(const QByteArray& packet); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 11f04e188f..dc21826f2c 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -310,7 +310,7 @@ void NodeList::sendDomainServerCheckIn() { bool isUsingDTLS = false; PacketType::Value domainPacketType = !_domainHandler.isConnected() - ? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest; + ? PacketType::DomainConnectRequest : PacketType::DomainListRequest; if (!_domainHandler.isConnected()) { qCDebug(networking) << "Sending connect request to domain-server at" << _domainHandler.getHostname(); @@ -329,24 +329,26 @@ void NodeList::sendDomainServerCheckIn() { } - // construct the DS check in packet - QUuid packetUUID = _sessionUUID; + auto domainPacket { NLPacket::create(domainPacketType); } + QDataStream packetStream(&domainPacket->getPayload); + + if (domainPacketType == PacketType::DomainConnectRequest) { + QUuid connectUUID; - if (domainPacketType == PacketTypeDomainConnectRequest) { if (!_domainHandler.getAssignmentUUID().isNull()) { // this is a connect request and we're an assigned node // so set our packetUUID as the assignment UUID - packetUUID = _domainHandler.getAssignmentUUID(); + connectUUID = _domainHandler.getAssignmentUUID(); } else if (_domainHandler.requiresICE()) { // this is a connect request and we're an interface client // that used ice to discover the DS // so send our ICE client UUID with the connect request - packetUUID = _domainHandler.getICEClientID(); + connectUUID = _domainHandler.getICEClientID(); } - } - QByteArray domainServerPacket = byteArrayWithUUIDPopulatedHeader(domainPacketType, packetUUID); - QDataStream packetStream(&domainServerPacket, QIODevice::Append); + // pack the connect UUID for this connect request + packetStream << connectUUID; + } // pack our data to send to the domain-server packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); @@ -367,7 +369,7 @@ void NodeList::sendDomainServerCheckIn() { flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn); if (!isUsingDTLS) { - writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr()); + sendPacket(domainPacket, _domainHandler.getSockAddr()); } if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { @@ -410,7 +412,7 @@ void NodeList::sendDSPathQuery(const QString& newPath) { // only send a path query if we know who our DS is or is going to be if (_domainHandler.isSocketKnown()) { // construct the path query packet - QByteArray pathQueryPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerPathQuery); + auto pathQueryPacket = NLPacket::create(PacketType::DomainServerPathQuery); // get the UTF8 representation of path query QByteArray pathQueryUTF8 = newPath.toUtf8(); @@ -418,18 +420,18 @@ void NodeList::sendDSPathQuery(const QString& newPath) { // get the size of the UTF8 representation of the desired path quint16 numPathBytes = pathQueryUTF8.size(); - if (pathQueryPacket.size() + numPathBytes + sizeof(numPathBytes) < MAX_PACKET_SIZE) { + if (numPathBytes + sizeof(numPathBytes) < pathQueryPacket.size() ) { // append the size of the path to the query packet - pathQueryPacket.append(reinterpret_cast<char*>(&numPathBytes), sizeof(numPathBytes)); + pathQueryPacket.write(&numPathBytes, sizeof(numPathBytes)); // append the path itself to the query packet - pathQueryPacket.append(pathQueryUTF8); + pathQueryPacket.write(pathQueryUTF8); qCDebug(networking) << "Sending a path query packet for path" << newPath << "to domain-server at" << _domainHandler.getSockAddr(); // send off the path query - writeUnverifiedDatagram(pathQueryPacket, _domainHandler.getSockAddr()); + sendPacket(pathQueryPacket, _domainHandler.getSockAddr()); } else { qCDebug(networking) << "Path" << newPath << "would make PacketTypeDomainServerPathQuery packet > MAX_PACKET_SIZE." << "Will not send query."; diff --git a/libraries/networking/src/PacketByteArray.cpp b/libraries/networking/src/PacketByteArray.cpp deleted file mode 100644 index 4d456b9066..0000000000 --- a/libraries/networking/src/PacketByteArray.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// -// PacketPayload.cpp -// libraries/networking/src -// -// Created by Stephen Birarda on 07/06/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "PacketPayload.h" - -PacketPayload::PacketPayload(char* data, int maxBytes) : - _data(data) - _maxBytes(maxBytes) -{ - -} - -int PacketPayload::append(const char* src, int srcBytes) { - // this is a call to write at the current index - int numWrittenBytes = write(src, srcBytes, _index); - - if (numWrittenBytes > 0) { - // we wrote some bytes, push the index - _index += numWrittenBytes; - return numWrittenBytes; - } else { - return numWrittenBytes; - } -} - -const int PACKET_WRITE_ERROR = -1; - -int PacketPayload::write(const char* src, int srcBytes, int index) { - if (index >= _maxBytes) { - // we were passed a bad index, return -1 - return PACKET_WRITE_ERROR; - } - - // make sure we have the space required to write this block - int bytesAvailable = _maxBytes - index; - - if (bytesAvailable < srcBytes) { - // good to go - write the data - memcpy(_data + index, src, srcBytes); - - // should this cause us to push our index (is this the farthest we've written in data)? - _index = std::max(_data + index + srcBytes, _index); - - // return the number of bytes written - return srcBytes; - } else { - // not enough space left for this write - return an error - return PACKET_WRITE_ERROR; - } -} - - diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 83b424d44d..02097402c6 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -31,7 +31,8 @@ const QSet<PacketType::Value> NON_VERIFIED_PACKETS = QSet<PacketType::Value>() const QSet<PacketType::Value> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType::Value>() << AvatarData; -const QSet<PacketType::Value> NON_SOURCED_PACKETS = QSet<PacketType::Value>() << ICEPing << ICEPingReply; +const QSet<PacketType::Value> NON_SOURCED_PACKETS = QSet<PacketType::Value>() + << ICEPing << ICEPingReply << DomainConnectRequest; int arithmeticCodingValueFromBuffer(const char* checkValue) { if (((uchar) *checkValue) < 255) { diff --git a/libraries/networking/src/PacketList.cpp b/libraries/networking/src/PacketList.cpp index bb61dd39d1..31b793cc76 100644 --- a/libraries/networking/src/PacketList.cpp +++ b/libraries/networking/src/PacketList.cpp @@ -47,18 +47,15 @@ qint64 writeData(const char* data, qint64 maxSize) { if (!_isOrdered) { auto newPacket = T::create(_packetType); - PacketPayload& newPayload = newPacket.getPayload(); if (_segmentStartIndex >= 0) { // We in the process of writing a segment for an unordered PacketList. // We need to try and pull the first part of the segment out to our new packet - PacketPayload& currentPayload = _currentPacket->getPayload(); - // check now to see if this is an unsupported write - int numBytesToEnd = currentPayload.size() - _segmentStartIndex; + int numBytesToEnd = _currentPacket.size() - _segmentStartIndex; - if ((newPayload.size() - numBytesToEnd) < maxSize) { + if ((newPacket.size() - numBytesToEnd) < maxSize) { // this is an unsupported case - the segment is bigger than the size of an individual packet // but the PacketList is not going to be sent ordered qDebug() << "Error in PacketList::writeData - attempted to write a segment to an unordered packet that is" @@ -67,20 +64,20 @@ qint64 writeData(const char* data, qint64 maxSize) { } // copy from currentPacket where the segment started to the beginning of the newPacket - newPayload.write(currentPacket.constData() + _segmentStartIndex, numBytesToEnd); + newPacket.write(currentPacket.constData() + _segmentStartIndex, numBytesToEnd); // the current segment now starts at the beginning of the new packet _segmentStartIndex = 0; // shrink the current payload to the actual size of the packet - currentPayload.setSizeUsed(_segmentStartIndex); + currentPacket.setSizeUsed(_segmentStartIndex); } // move the current packet to our list of packets _packets.insert(std::move(_currentPacket)); // write the data to the newPacket - newPayload.write(data, maxSize); + newPacket.write(data, maxSize); // set our current packet to the new packet _currentPacket = newPacket; @@ -90,9 +87,8 @@ qint64 writeData(const char* data, qint64 maxSize) { } else { // we're an ordered PacketList - let's fit what we can into the current packet and then put the leftover // into a new packet - PacketPayload& currentPayload = _currentPacket.getPayload(); - int numBytesToEnd = _currentPayload.size() - _currentPayload.pos(); + int numBytesToEnd = _currentPacket.size() - _currentPacket.sizeUsed(); _currentPacket.write(data, numBytesToEnd); // move the current packet to our list of packets