diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 12f5abc796..4157e55754 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -88,7 +88,7 @@ void AssignmentClientMonitor::stopChildProcesses() { nodeList->eachNode([&](const SharedNodePointer& node) { qDebug() << "asking child" << node->getUUID() << "to exit."; node->activateLocalSocket(); - QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); + QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, *node->getActiveSocket()); }); @@ -193,7 +193,7 @@ void AssignmentClientMonitor::checkSpares() { qDebug() << "asking child" << aSpareId << "to exit."; SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId); childNode->activateLocalSocket(); - QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); + QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, childNode); } } @@ -229,7 +229,7 @@ void AssignmentClientMonitor::readPendingDatagrams() { } else { // tell unknown assignment-client child to exit. qDebug() << "asking unknown child to exit."; - QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); + QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr); } } diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 94439cb18b..098569b4f8 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -65,12 +65,6 @@ const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; const QString AUDIO_ENV_GROUP_KEY = "audio_env"; const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer"; -void attachNewNodeDataToNode(Node *newNode) { - if (!newNode->getLinkedData()) { - newNode->setLinkedData(new AudioMixerClientData()); - } -} - InboundAudioStream::Settings AudioMixer::_streamSettings; bool AudioMixer::_printStreamStats = false; @@ -514,7 +508,8 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { bool sendData = dataChanged || (randFloat() < CHANCE_OF_SEND); if (sendData) { - int numBytesEnvPacketHeader = populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment); + auto nodeList = DependencyManager::get(); + int numBytesEnvPacketHeader = nodeList->populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment); char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader; unsigned char bitset = 0; @@ -531,7 +526,7 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { memcpy(envDataAt, &wetLevel, sizeof(float)); envDataAt += sizeof(float); } - DependencyManager::get()->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node); + nodeList->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node); } } @@ -552,7 +547,7 @@ void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const Hif SharedNodePointer sendingNode = nodeList->sendingNodeForPacket(receivedPacket); if (sendingNode->getCanAdjustLocks()) { QByteArray packet = receivedPacket; - populatePacketHeader(packet, PacketTypeMuteEnvironment); + nodeList->populatePacketHeader(packet, PacketTypeMuteEnvironment); nodeList->eachNode([&](const SharedNodePointer& node){ if (node->getType() == NodeType::Agent && node->getActiveSocket() && @@ -686,7 +681,9 @@ void AudioMixer::run() { nodeList->addNodeTypeToInterestSet(NodeType::Agent); - nodeList->linkedDataCreateCallback = attachNewNodeDataToNode; + nodeList->linkedDataCreateCallback = [](Node* node) { + node->setLinkedData(new AudioMixerClientData()); + }; // wait until we have the domain-server settings, otherwise we bail DomainHandler& domainHandler = nodeList->getDomainHandler(); @@ -794,7 +791,7 @@ void AudioMixer::run() { // if the stream should be muted, send mute packet if (nodeData->getAvatarAudioStream() && shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) { - QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeNoisyMute); + QByteArray packet = nodeList->byteArrayWithPopulatedHeader(PacketTypeNoisyMute); nodeList->writeDatagram(packet, node); } @@ -806,7 +803,7 @@ void AudioMixer::run() { char* mixDataAt; if (streamsMixed > 0) { // pack header - int numBytesMixPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio); + int numBytesMixPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio); mixDataAt = clientMixBuffer + numBytesMixPacketHeader; // pack sequence number @@ -819,7 +816,7 @@ void AudioMixer::run() { mixDataAt += AudioConstants::NETWORK_FRAME_BYTES_STEREO; } else { // pack header - int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame); + int numBytesPacketHeader = nodeList->populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame); mixDataAt = clientMixBuffer + numBytesPacketHeader; // pack sequence number diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 4db5611bb5..8848758f86 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -157,7 +157,7 @@ void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& quint8 appendFlag = 0; // pack header - int numBytesPacketHeader = populatePacketHeader(packet, PacketTypeAudioStreamStats); + int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeAudioStreamStats); char* headerEndAt = packet + numBytesPacketHeader; // calculate how many stream stat structs we can fit in each packet diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 961cbecebb..0b510be151 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -56,14 +56,6 @@ AvatarMixer::~AvatarMixer() { _broadcastThread.wait(); } -void attachAvatarDataToNode(Node* newNode) { - if (!newNode->getLinkedData()) { - // setup the client linked data - default the number of frames since adjustment - // to our number of frames per second - newNode->setLinkedData(new AvatarMixerClientData()); - } -} - const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f; // NOTE: some additional optimizations to consider. @@ -129,10 +121,9 @@ void AvatarMixer::broadcastAvatarData() { static QByteArray mixedAvatarByteArray; - int numPacketHeaderBytes = populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData); - auto nodeList = DependencyManager::get(); - + int numPacketHeaderBytes = nodeList->populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData); + // setup for distributed random floating point values std::random_device randomDevice; std::mt19937 generator(randomDevice()); @@ -161,7 +152,7 @@ void AvatarMixer::broadcastAvatarData() { // reset packet pointers for this node mixedAvatarByteArray.resize(numPacketHeaderBytes); - + AvatarData& avatar = nodeData->getAvatar(); glm::vec3 myPosition = avatar.getPosition(); @@ -179,7 +170,13 @@ void AvatarMixer::broadcastAvatarData() { // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; - + + // keep track of the number of other avatars held back in this frame + int numAvatarsHeldBack = 0; + + // keep track of the number of other avatar frames skipped + int numAvatarsWithSkippedFrames = 0; + // use the data rate specifically for avatar data for FRD adjustment checks float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps(); @@ -257,8 +254,38 @@ void AvatarMixer::broadcastAvatarData() { && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { return; } + + PacketSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); + PacketSequenceNumber lastSeqFromSender = otherNode->getLastSequenceNumberForPacketType(PacketTypeAvatarData); + + 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) { + ++numAvatarsHeldBack; + return; + } 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)); QByteArray avatarByteArray; avatarByteArray.append(otherNode->getUUID().toRfc4122()); @@ -287,7 +314,7 @@ void AvatarMixer::broadcastAvatarData() { && (forceSend || otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); + QByteArray billboardPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); billboardPacket.append(otherNode->getUUID().toRfc4122()); billboardPacket.append(otherNodeData->getAvatar().getBillboard()); @@ -301,7 +328,7 @@ void AvatarMixer::broadcastAvatarData() { || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); + QByteArray identityPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122()); @@ -318,6 +345,10 @@ void AvatarMixer::broadcastAvatarData() { // record the bytes sent for other avatar data in the AvatarMixerClientData nodeData->recordSentAvatarData(numAvatarDataBytes + mixedAvatarByteArray.size()); + + // record the number of avatars held back this frame + nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); + nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); if (numOtherAvatars == 0) { // update the full rate distance to FLOAT_MAX since we didn't have any other avatars to send @@ -334,13 +365,36 @@ void AvatarMixer::broadcastAvatarData() { void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { if (killedNode->getType() == NodeType::Agent && killedNode->getLinkedData()) { + auto nodeList = DependencyManager::get(); + // this was an avatar we were sending to other people // send a kill packet for it to our other nodes - QByteArray killPacket = byteArrayWithPopulatedHeader(PacketTypeKillAvatar); + QByteArray killPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeKillAvatar); killPacket += killedNode->getUUID().toRfc4122(); - DependencyManager::get()->broadcastToNodes(killPacket, - NodeSet() << NodeType::Agent); + nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::Agent); + + // we also want to remove sequence number data for this avatar on our other avatars + // so invoke the appropriate method on the AvatarMixerClientData for other avatars + nodeList->eachMatchingNode( + [&](const SharedNodePointer& node)->bool { + if (!node->getLinkedData()) { + return false; + } + + if (node->getUUID() == killedNode->getUUID()) { + return false; + } + + return true; + }, + [&](const SharedNodePointer& node) { + QMetaObject::invokeMethod(node->getLinkedData(), + "removeLastBroadcastSequenceNumber", + Qt::AutoConnection, + Q_ARG(const QUuid&, QUuid(killedNode->getUUID()))); + } + ); } } @@ -430,11 +484,14 @@ void AvatarMixer::sendStatsPacket() { AvatarMixerClientData* clientData = static_cast(node->getLinkedData()); if (clientData) { - clientData->loadJSONStats(avatarStats); + MutexTryLocker lock(clientData->getMutex()); + if (lock.isLocked()) { + 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[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble(); + // add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only + 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; @@ -455,7 +512,9 @@ void AvatarMixer::run() { auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - nodeList->linkedDataCreateCallback = attachAvatarDataToNode; + nodeList->linkedDataCreateCallback = [] (Node* node) { + node->setLinkedData(new AvatarMixerClientData()); + }; // setup the timer that will be fired on the broadcast thread _broadcastTimer = new QTimer(); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 17330ac891..197e9baf5e 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -25,11 +25,24 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() { return oldValue; } +PacketSequenceNumber AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const { + // return the matching PacketSequenceNumber, or the default if we don't have it + auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID); + if (nodeMatch != _lastBroadcastSequenceNumbers.end()) { + return nodeMatch->second; + } else { + return DEFAULT_SEQUENCE_NUMBER; + } +} + void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["display_name"] = _avatar.getDisplayName(); jsonObject["full_rate_distance"] = _fullRateDistance; jsonObject["max_avatar_distance"] = _maxAvatarDistance; jsonObject["num_avatars_sent_last_frame"] = _numAvatarsSentLastFrame; + jsonObject["avg_other_avatar_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond(); + jsonObject["avg_other_avatar_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond(); + jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends; jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps(); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index cccc5ee60f..3e10b8473a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -21,7 +22,9 @@ #include #include #include +#include #include +#include const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps"; @@ -32,7 +35,12 @@ public: AvatarData& getAvatar() { return _avatar; } bool checkAndSetHasReceivedFirstPackets(); - + + PacketSequenceNumber getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const; + void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, PacketSequenceNumber sequenceNumber) + { _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; } + Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } + quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; } void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; } @@ -49,6 +57,14 @@ public: void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } int getNumAvatarsSentLastFrame() const { return _numAvatarsSentLastFrame; } + void recordNumOtherAvatarStarves(int numAvatarsHeldBack) { _otherAvatarStarves.updateAverage((float) numAvatarsHeldBack); } + float getAvgNumOtherAvatarStarvesPerSecond() const { return _otherAvatarStarves.getAverageSampleValuePerSecond(); } + + void recordNumOtherAvatarSkips(int numOtherAvatarSkips) { _otherAvatarSkips.updateAverage((float) numOtherAvatarSkips); } + float getAvgNumOtherAvatarSkipsPerSecond() const { return _otherAvatarSkips.getAverageSampleValuePerSecond(); } + + void incrementNumOutOfOrderSends() { ++_numOutOfOrderSends; } + int getNumFramesSinceFRDAdjustment() const { return _numFramesSinceAdjustment; } void incrementNumFramesSinceFRDAdjustment() { ++_numFramesSinceAdjustment; } void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; } @@ -61,13 +77,23 @@ public: void loadJSONStats(QJsonObject& jsonObject) const; private: AvatarData _avatar; + + std::unordered_map _lastBroadcastSequenceNumbers; + bool _hasReceivedFirstPackets = false; quint64 _billboardChangeTimestamp = 0; quint64 _identityChangeTimestamp = 0; + float _fullRateDistance = FLT_MAX; float _maxAvatarDistance = FLT_MAX; + int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; + + SimpleMovingAverage _otherAvatarStarves; + SimpleMovingAverage _otherAvatarSkips; + int _numOutOfOrderSends = 0; + SimpleMovingAverage _avgOtherAvatarDataRate; }; diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index bb5042f4b4..0f5332192a 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -64,7 +64,9 @@ void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePo unsigned char outputBuffer[MAX_PACKET_SIZE]; unsigned char* copyAt = outputBuffer; - int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(outputBuffer), PacketTypeEntityAddResponse); + auto nodeList = DependencyManager::get(); + + int numBytesPacketHeader = nodeList->populatePacketHeader(reinterpret_cast(outputBuffer), PacketTypeEntityAddResponse); int packetLength = numBytesPacketHeader; copyAt += numBytesPacketHeader; @@ -81,7 +83,7 @@ void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePo copyAt += sizeof(entityID); packetLength += sizeof(entityID); - DependencyManager::get()->writeDatagram((char*) outputBuffer, packetLength, senderNode); + nodeList->writeDatagram((char*) outputBuffer, packetLength, senderNode); } diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index f6ab12f421..28506b033e 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -277,9 +277,11 @@ int OctreeInboundPacketProcessor::sendNackPackets() { char* dataAt = packet; int bytesRemaining = MAX_PACKET_SIZE; + + auto nodeList = DependencyManager::get(); // pack header - int numBytesPacketHeader = populatePacketHeader(packet, _myServer->getMyEditNackType()); + int numBytesPacketHeader = nodeList->populatePacketHeader(packet, _myServer->getMyEditNackType()); dataAt += numBytesPacketHeader; bytesRemaining -= numBytesPacketHeader; @@ -301,7 +303,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() { numSequenceNumbersAvailable -= numSequenceNumbers; // send it - DependencyManager::get()->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode); + nodeList->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode); packetsSent++; qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID; diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index 2d8d8d357e..b6504863e0 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -189,7 +189,9 @@ void OctreeQueryNode::resetOctreePacket() { } _octreePacketAvailableBytes = MAX_PACKET_SIZE; - int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(_octreePacket), _myPacketType); + int numBytesPacketHeader = DependencyManager::get()->populatePacketHeader(reinterpret_cast(_octreePacket), + _myPacketType); + _octreePacketAt = _octreePacket + numBytesPacketHeader; _octreePacketAvailableBytes -= numBytesPacketHeader; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 9abace0c5b..361a619744 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -213,14 +213,6 @@ void OctreeServer::trackProcessWaitTime(float time) { _averageProcessWaitTime.updateAverage(time); } -void OctreeServer::attachQueryNodeToNode(Node* newNode) { - if (!newNode->getLinkedData() && _instance) { - OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(); - newQueryNodeData->init(); - newNode->setLinkedData(newQueryNodeData); - } -} - OctreeServer::OctreeServer(const QByteArray& packet) : ThreadedAssignment(packet), _argc(0), @@ -1132,7 +1124,11 @@ void OctreeServer::run() { setvbuf(stdout, NULL, _IOLBF, 0); #endif - nodeList->linkedDataCreateCallback = &OctreeServer::attachQueryNodeToNode; + nodeList->linkedDataCreateCallback = [] (Node* node) { + OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(); + newQueryNodeData->init(); + node->setLinkedData(newQueryNodeData); + }; srand((unsigned)time(0)); diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 41cd3259cf..ab75efe346 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -75,8 +75,6 @@ public: virtual bool hasSpecialPacketToSend(const SharedNodePointer& node) { return false; } virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; } - static void attachQueryNodeToNode(Node* newNode); - static float SKIP_TIME; // use this for trackXXXTime() calls for non-times static void trackLoopTime(float time) { _averageLoopTime.updateAverage(time); } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 6ee1d6c765..7841381422 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -619,10 +619,12 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock packetStream >> nodeInterestList >> username >> usernameSignature; + auto limitedNodeList = DependencyManager::get(); + QString reason; if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr, reason)) { // this is an agent and we've decided we won't let them connect - send them a packet to deny connection - QByteArray connectionDeniedByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied); + QByteArray connectionDeniedByteArray = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainConnectionDenied); QDataStream out(&connectionDeniedByteArray, QIODevice::WriteOnly | QIODevice::Append); out << reason; // tell client it has been refused. @@ -664,10 +666,9 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock canRez = canAdjustLocks; } - SharedNodePointer newNode = - DependencyManager::get()->addOrUpdateNode(nodeUUID, nodeType, - publicSockAddr, localSockAddr, - canAdjustLocks, canRez); + SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeType, + publicSockAddr, localSockAddr, + canAdjustLocks, canRez); // when the newNode is created the linked data is also created // if this was a static assignment set the UUID, set the sendingSockAddr DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); @@ -926,8 +927,8 @@ NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int n void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr, const NodeSet& nodeInterestList) { - - QByteArray broadcastPacket = byteArrayWithPopulatedHeader(PacketTypeDomainList); + auto limitedNodeList = DependencyManager::get(); + QByteArray broadcastPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainList); // always send the node their own UUID back QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append); @@ -939,8 +940,6 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - auto nodeList = DependencyManager::get(); - // if we've established a connection via ICE with this peer, use that socket // otherwise just try to reply back to them on their sending socket (although that may not work) HifiSockAddr destinationSockAddr = _connectedICEPeers.value(node->getUUID()); @@ -955,7 +954,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif if (nodeData->isAuthenticated()) { // if this authenticated node has any interest types, send back those nodes as well - nodeList->eachNode([&](const SharedNodePointer& otherNode){ + limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){ // reset our nodeByteArray and nodeDataStream QByteArray nodeByteArray; QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append); @@ -986,7 +985,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif // we need to break here and start a new packet // so send the current one - nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); + limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr); // reset the broadcastPacket structure broadcastPacket.resize(numBroadcastPacketLeadBytes); @@ -999,26 +998,26 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif }); } } - + // always write the last broadcastPacket - nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); + limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr); } void DomainServer::readAvailableDatagrams() { - auto nodeList = DependencyManager::get(); + auto limitedNodeList = DependencyManager::get(); HifiSockAddr senderSockAddr; QByteArray receivedPacket; - static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment); + static QByteArray assignmentPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeCreateAssignment); static int numAssignmentPacketHeaderBytes = assignmentPacket.size(); - while (nodeList->getNodeSocket().hasPendingDatagrams()) { - receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); - nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), + while (limitedNodeList->getNodeSocket().hasPendingDatagrams()) { + receivedPacket.resize(limitedNodeList->getNodeSocket().pendingDatagramSize()); + limitedNodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); if (packetTypeForPacket(receivedPacket) == PacketTypeRequestAssignment - && nodeList->packetVersionAndHashMatch(receivedPacket)) { + && limitedNodeList->packetVersionAndHashMatch(receivedPacket)) { // construct the requested assignment from the packet data Assignment requestAssignment(receivedPacket); @@ -1059,7 +1058,7 @@ void DomainServer::readAvailableDatagrams() { assignmentStream << uniqueAssignment; - nodeList->getNodeSocket().writeDatagram(assignmentPacket, + limitedNodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); // add the information for that deployed assignment to the hash of pending assigned nodes @@ -1081,16 +1080,16 @@ void DomainServer::readAvailableDatagrams() { processDatagram(receivedPacket, senderSockAddr); } else { // we're using DTLS, so tell the sender to get back to us using DTLS - static QByteArray dtlsRequiredPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS); + static QByteArray dtlsRequiredPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS); static int numBytesDTLSHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerRequireDTLS); if (dtlsRequiredPacket.size() == numBytesDTLSHeader) { // pack the port that we accept DTLS traffic on - unsigned short dtlsPort = nodeList->getDTLSSocket().localPort(); + unsigned short dtlsPort = limitedNodeList->getDTLSSocket().localPort(); dtlsRequiredPacket.replace(numBytesDTLSHeader, sizeof(dtlsPort), reinterpret_cast(&dtlsPort)); } - nodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr); + limitedNodeList->writeUnverifiedDatagram(dtlsRequiredPacket, senderSockAddr); } } } diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 77deb6125b..e72555cac1 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -117,7 +117,7 @@ void IceServer::sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, Q QSet::iterator peerID = connections.begin(); QByteArray outgoingPacket(MAX_PACKET_SIZE, 0); - int currentPacketSize = populatePacketHeader(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id); + int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id); int numHeaderBytes = currentPacketSize; // go through the connections, sending packets containing connection information for those nodes @@ -136,7 +136,7 @@ void IceServer::sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, Q destinationSockAddr.getAddress(), destinationSockAddr.getPort()); // reset the packet size to our number of header bytes - currentPacketSize = populatePacketHeader(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id); + currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id); } // append the current peer bytes diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cc62490b64..05dc2baf84 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2577,7 +2577,7 @@ int Application::sendNackPackets() { int bytesRemaining = MAX_PACKET_SIZE; // pack header - int numBytesPacketHeader = populatePacketHeader(packet, PacketTypeOctreeDataNack); + int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeOctreeDataNack); dataAt += numBytesPacketHeader; bytesRemaining -= numBytesPacketHeader; @@ -2778,7 +2778,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node unsigned char* endOfQueryPacket = queryPacket; // insert packet type/version and node UUID - endOfQueryPacket += populatePacketHeader(reinterpret_cast(endOfQueryPacket), packetType); + endOfQueryPacket += nodeList->populatePacketHeader(reinterpret_cast(endOfQueryPacket), packetType); // encode the query data... endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a470867129..fe4485b5db 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -847,8 +847,9 @@ int MyAvatar::parseDataAtOffset(const QByteArray& packet, int offset) { } void MyAvatar::sendKillAvatar() { - QByteArray killPacket = byteArrayWithPopulatedHeader(PacketTypeKillAvatar); - DependencyManager::get()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer); + auto nodeList = DependencyManager::get(); + QByteArray killPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeKillAvatar); + nodeList->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer); } void MyAvatar::updateLookAtTargetAvatar() { diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index e581105cd2..10e2bc3bbf 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -837,7 +837,7 @@ void AudioClient::handleAudioInput() { } } - char* currentPacketPtr = audioDataPacket + populatePacketHeader(audioDataPacket, packetType); + char* currentPacketPtr = audioDataPacket + nodeList->populatePacketHeader(audioDataPacket, packetType); // pack sequence number memcpy(currentPacketPtr, &_outgoingAvatarAudioSequenceNumber, sizeof(quint16)); @@ -899,7 +899,9 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr } void AudioClient::sendMuteEnvironmentPacket() { - QByteArray mutePacket = byteArrayWithPopulatedHeader(PacketTypeMuteEnvironment); + auto nodeList = DependencyManager::get(); + + QByteArray mutePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeMuteEnvironment); int headerSize = mutePacket.size(); const float MUTE_RADIUS = 50; @@ -910,12 +912,11 @@ void AudioClient::sendMuteEnvironmentPacket() { memcpy(mutePacket.data() + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float)); // grab our audio mixer from the NodeList, if it exists - auto nodelist = DependencyManager::get(); - SharedNodePointer audioMixer = nodelist->soloNodeOfType(NodeType::AudioMixer); + SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixer) { // send off this mute packet - nodelist->writeDatagram(mutePacket, audioMixer); + nodeList->writeDatagram(mutePacket, audioMixer); } } diff --git a/libraries/audio-client/src/AudioIOStats.cpp b/libraries/audio-client/src/AudioIOStats.cpp index 88a1b96d4c..0a6c94671b 100644 --- a/libraries/audio-client/src/AudioIOStats.cpp +++ b/libraries/audio-client/src/AudioIOStats.cpp @@ -104,10 +104,12 @@ void AudioIOStats::sendDownstreamAudioStatsPacket() { // also, call _receivedAudioStream's per-second callback _receivedAudioStream->perSecondCallbackForUpdatingStats(); + auto nodeList = DependencyManager::get(); + char packet[MAX_PACKET_SIZE]; // pack header - int numBytesPacketHeader = populatePacketHeader(packet, PacketTypeAudioStreamStats); + int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeAudioStreamStats); char* dataAt = packet + numBytesPacketHeader; // pack append flag @@ -126,7 +128,6 @@ void AudioIOStats::sendDownstreamAudioStatsPacket() { dataAt += sizeof(AudioStreamStats); // send packet - auto nodeList = DependencyManager::get(); SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); nodeList->writeDatagram(packet, dataAt - packet, audioMixer); -} \ No newline at end of file +} diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 26140b82c8..ae397ba97e 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -141,11 +141,13 @@ void AudioInjector::injectToMixer() { _currentSendPosition = 0; } + auto nodeList = DependencyManager::get(); + // make sure we actually have samples downloaded to inject if (_audioData.size()) { // setup the packet for injected audio - QByteArray injectAudioPacket = byteArrayWithPopulatedHeader(PacketTypeInjectAudio); + QByteArray injectAudioPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeInjectAudio); QDataStream packetStream(&injectAudioPacket, QIODevice::Append); // pack some placeholder sequence number for now @@ -226,7 +228,6 @@ void AudioInjector::injectToMixer() { _audioData.data() + _currentSendPosition, bytesToCopy); // grab our audio mixer from the NodeList, if it exists - auto nodeList = DependencyManager::get(); SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); // send off this audio packet diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f0e4eb118b..c7ab6dce2e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1077,25 +1077,31 @@ void AvatarData::setJointMappingsFromNetworkReply() { } void AvatarData::sendAvatarDataPacket() { - QByteArray dataPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarData); + auto nodeList = DependencyManager::get(); + + QByteArray dataPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarData); dataPacket.append(toByteArray()); - DependencyManager::get()->broadcastToNodes(dataPacket, NodeSet() << NodeType::AvatarMixer); + nodeList->broadcastToNodes(dataPacket, NodeSet() << NodeType::AvatarMixer); } void AvatarData::sendIdentityPacket() { - QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); + auto nodeList = DependencyManager::get(); + + QByteArray identityPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); identityPacket.append(identityByteArray()); - DependencyManager::get()->broadcastToNodes(identityPacket, NodeSet() << NodeType::AvatarMixer); + nodeList->broadcastToNodes(identityPacket, NodeSet() << NodeType::AvatarMixer); } void AvatarData::sendBillboardPacket() { if (!_billboard.isEmpty()) { - QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); + auto nodeList = DependencyManager::get(); + + QByteArray billboardPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); billboardPacket.append(_billboard); - DependencyManager::get()->broadcastToNodes(billboardPacket, NodeSet() << NodeType::AvatarMixer); + nodeList->broadcastToNodes(billboardPacket, NodeSet() << NodeType::AvatarMixer); } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5aea021e7a..1b59d3bd8e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -792,7 +792,8 @@ bool EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumbe bool hasMoreToSend = true; unsigned char* copyAt = outputBuffer; - size_t numBytesPacketHeader = populatePacketHeader(reinterpret_cast(outputBuffer), PacketTypeEntityErase); + size_t numBytesPacketHeader = DependencyManager::get()->populatePacketHeader(reinterpret_cast(outputBuffer), + PacketTypeEntityErase); copyAt += numBytesPacketHeader; outputLength = numBytesPacketHeader; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index e5badc32eb..d8d1e9289b 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "LimitedNodeList.h" + #include #include #include @@ -28,8 +30,6 @@ #include "AccountManager.h" #include "Assignment.h" #include "HifiSockAddr.h" -#include "LimitedNodeList.h" -#include "PacketHeaders.h" #include "UUID.h" #include "NetworkLogging.h" @@ -230,21 +230,13 @@ qint64 LimitedNodeList::readDatagram(QByteArray& incomingPacket, QHostAddress* a return result; } -qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr, - const QUuid& connectionSecret) { - QByteArray datagramCopy = datagram; - - if (!connectionSecret.isNull()) { - // setup the MD5 hash for source verification in the header - replaceHashInPacketGivenConnectionUUID(datagramCopy, connectionSecret); - } - +qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr) { // XXX can BandwidthRecorder be used for this? // stat collection for packets ++_numCollectedPackets; _numCollectedBytes += datagram.size(); - qint64 bytesWritten = _nodeSocket.writeDatagram(datagramCopy, + qint64 bytesWritten = _nodeSocket.writeDatagram(datagram, destinationSockAddr.getAddress(), destinationSockAddr.getPort()); if (bytesWritten < 0) { @@ -258,6 +250,12 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode, const HifiSockAddr& overridenSockAddr) { if (destinationNode) { + PacketType packetType = packetTypeForPacket(datagram); + + if (NON_VERIFIED_PACKETS.contains(packetType)) { + return writeUnverifiedDatagram(datagram, destinationNode, overridenSockAddr); + } + // if we don't have an overridden address, assume they want to send to the node's active socket const HifiSockAddr* destinationSockAddr = &overridenSockAddr; if (overridenSockAddr.isNull()) { @@ -270,8 +268,26 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, } } + QByteArray datagramCopy = datagram; + + // if we're here and the connection secret is null, debug out - this could be a problem + if (destinationNode->getConnectionSecret().isNull()) { + qDebug() << "LimitedNodeList::writeDatagram called for verified datagram with null connection secret for" + << "destination node" << destinationNode->getUUID() << " - this is either not secure or will cause" + << "this packet to be unverifiable on the receiving side."; + } + + // perform replacement of hash and optionally also sequence number in the header + if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) { + PacketSequenceNumber sequenceNumber = getNextSequenceNumberForPacket(destinationNode->getUUID(), packetType); + replaceHashAndSequenceNumberInPacket(datagramCopy, destinationNode->getConnectionSecret(), + sequenceNumber, packetType); + } else { + replaceHashInPacket(datagramCopy, destinationNode->getConnectionSecret(), packetType); + } + emit dataSent(destinationNode->getType(), datagram.size()); - auto bytesWritten = writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret()); + auto bytesWritten = writeDatagram(datagramCopy, *destinationSockAddr); // Keep track of per-destination-node bandwidth destinationNode->recordBytesSent(bytesWritten); return bytesWritten; @@ -296,8 +312,21 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, cons } } - // don't use the node secret! - return writeDatagram(datagram, *destinationSockAddr, QUuid()); + PacketType packetType = packetTypeForPacket(datagram); + + // optionally peform sequence number replacement in the header + if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) { + + QByteArray datagramCopy = datagram; + + PacketSequenceNumber sequenceNumber = getNextSequenceNumberForPacket(destinationNode->getUUID(), packetType); + replaceSequenceNumberInPacket(datagramCopy, sequenceNumber, packetType); + + // send the datagram with sequence number replaced in header + return writeDatagram(datagramCopy, *destinationSockAddr); + } else { + return writeDatagram(datagram, *destinationSockAddr); + } } // didn't have a destinationNode to send to, return 0 @@ -305,7 +334,7 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, cons } qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr) { - return writeDatagram(datagram, destinationSockAddr, QUuid()); + return writeDatagram(datagram, destinationSockAddr); } qint64 LimitedNodeList::writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode, @@ -318,6 +347,15 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const char* data, qint64 size, c return writeUnverifiedDatagram(QByteArray(data, size), destinationNode, overridenSockAddr); } +PacketSequenceNumber LimitedNodeList::getNextSequenceNumberForPacket(const QUuid& nodeUUID, PacketType packetType) { + // Thanks to std::map and std::unordered_map this line either default constructs the + // PacketTypeSequenceMap and the PacketSequenceNumber or returns the existing value. + // We use the postfix increment so that the stored value is incremented and the next + // return gives the correct value. + + return _packetSequenceNumbers[nodeUUID][packetType]++; +} + void LimitedNodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) { // the node decided not to do anything with this packet // if it comes from a known source we should keep that node alive @@ -332,6 +370,13 @@ int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& match matchingNode->setLastHeardMicrostamp(usecTimestampNow()); + // if this was a sequence numbered packet we should store the last seq number for + // a packet of this type for this node + PacketType packetType = packetTypeForPacket(packet); + if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) { + matchingNode->setLastSequenceNumberForPacketType(sequenceNumberFromHeader(packet, packetType), packetType); + } + NodeData* linkedData = matchingNode->getLinkedData(); if (!linkedData && linkedDataCreateCallback) { linkedDataCreateCallback(matchingNode.data()); @@ -466,8 +511,11 @@ unsigned LimitedNodeList::broadcastToNodes(const QByteArray& packet, const NodeS } QByteArray LimitedNodeList::constructPingPacket(PingType_t pingType, bool isVerified, const QUuid& packetHeaderID) { - QByteArray pingPacket = byteArrayWithPopulatedHeader(isVerified ? PacketTypePing : PacketTypeUnverifiedPing, - packetHeaderID); + + QUuid packetUUID = packetHeaderID.isNull() ? _sessionUUID : packetHeaderID; + + QByteArray pingPacket = byteArrayWithUUIDPopulatedHeader(isVerified ? PacketTypePing : PacketTypeUnverifiedPing, + packetUUID); QDataStream packetStream(&pingPacket, QIODevice::Append); @@ -489,8 +537,10 @@ QByteArray LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacke PacketType replyType = (packetTypeForPacket(pingPacket) == PacketTypePing) ? PacketTypePingReply : PacketTypeUnverifiedPingReply; - - QByteArray replyPacket = byteArrayWithPopulatedHeader(replyType, packetHeaderID); + + QUuid packetUUID = packetHeaderID.isNull() ? _sessionUUID : packetHeaderID; + + QByteArray replyPacket = byteArrayWithUUIDPopulatedHeader(replyType, packetUUID); QDataStream packetStream(&replyPacket, QIODevice::Append); packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow(); @@ -522,11 +572,11 @@ void LimitedNodeList::removeSilentNodes() { eachNodeHashIterator([&](NodeHash::iterator& it){ SharedNodePointer node = it->second; node->getMutex().lock(); - + if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > (NODE_SILENCE_THRESHOLD_MSECS * USECS_PER_MSEC)) { // call the NodeHash erase to get rid of this node it = _nodeHash.unsafe_erase(it); - + killedNodes.insert(node); } else { // we didn't erase this node, push the iterator forwards @@ -680,7 +730,7 @@ void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSock headerID = _sessionUUID; } - QByteArray iceRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeIceServerHeartbeat, headerID); + QByteArray iceRequestByteArray = byteArrayWithUUIDPopulatedHeader(PacketTypeIceServerHeartbeat, headerID); QDataStream iceDataStream(&iceRequestByteArray, QIODevice::Append); iceDataStream << _publicSockAddr << _localSockAddr; diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 6ebe30c930..9420b698ec 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -15,6 +15,7 @@ #include #include #include +#include #ifndef _WIN32 #include // not on windows, not needed for mac or windows @@ -34,6 +35,7 @@ #include "DomainHandler.h" #include "Node.h" +#include "PacketHeaders.h" #include "UUIDHasher.h" const int MAX_PACKET_SIZE = 1450; @@ -96,6 +98,13 @@ public: bool packetVersionAndHashMatch(const QByteArray& packet); + QByteArray byteArrayWithPopulatedHeader(PacketType packetType) + { return byteArrayWithUUIDPopulatedHeader(packetType, _sessionUUID); } + int populatePacketHeader(QByteArray& packet, PacketType packetType) + { return populatePacketHeaderWithUUID(packet, packetType, _sessionUUID); } + int populatePacketHeader(char* packet, PacketType packetType) + { return populatePacketHeaderWithUUID(packet, packetType, _sessionUUID); } + qint64 readDatagram(QByteArray& incomingPacket, QHostAddress* address, quint16 * port); qint64 writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode, @@ -105,13 +114,14 @@ public: const HifiSockAddr& overridenSockAddr = HifiSockAddr()); qint64 writeUnverifiedDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr); + qint64 writeDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode, const HifiSockAddr& overridenSockAddr = HifiSockAddr()); qint64 writeUnverifiedDatagram(const char* data, qint64 size, const SharedNodePointer& destinationNode, const HifiSockAddr& overridenSockAddr = HifiSockAddr()); - void(*linkedDataCreateCallback)(Node *); + void (*linkedDataCreateCallback)(Node *); int size() const { return _nodeHash.size(); } @@ -224,8 +234,9 @@ protected: LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton void operator=(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton - qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr, - const QUuid& connectionSecret); + qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr); + + PacketSequenceNumber getNextSequenceNumberForPacket(const QUuid& nodeUUID, PacketType packetType); void changeSocketBufferSizes(int numBytes); @@ -247,6 +258,8 @@ protected: QElapsedTimer _packetStatTimer; bool _thisNodeCanAdjustLocks; bool _thisNodeCanRez; + + std::unordered_map _packetSequenceNumbers; template void eachNodeHashIterator(IteratorLambda functor) { diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 4abae367d7..ddc0571db4 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -129,6 +129,15 @@ void Node::activateSymmetricSocket() { _activeSocket = &_symmetricSocket; } +PacketSequenceNumber Node::getLastSequenceNumberForPacketType(PacketType packetType) const { + auto typeMatch = _lastSequenceNumbers.find(packetType); + if (typeMatch != _lastSequenceNumbers.end()) { + return typeMatch->second; + } else { + return DEFAULT_SEQUENCE_NUMBER; + } +} + QDataStream& operator<<(QDataStream& out, const Node& node) { out << node._type; out << node._uuid; diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 420c52b423..0836a448b2 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -23,6 +23,7 @@ #include "HifiSockAddr.h" #include "NetworkPeer.h" #include "NodeData.h" +#include "PacketHeaders.h" #include "SimpleMovingAverage.h" #include "MovingPercentile.h" @@ -43,7 +44,7 @@ namespace NodeType { class Node : public NetworkPeer { Q_OBJECT -public: +public: Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez); ~Node(); @@ -86,6 +87,10 @@ public: void activatePublicSocket(); void activateLocalSocket(); void activateSymmetricSocket(); + + void setLastSequenceNumberForPacketType(PacketSequenceNumber sequenceNumber, PacketType packetType) + { _lastSequenceNumbers[packetType] = sequenceNumber; } + PacketSequenceNumber getLastSequenceNumberForPacketType(PacketType packetType) const; friend QDataStream& operator<<(QDataStream& out, const Node& node); friend QDataStream& operator>>(QDataStream& in, Node& node); @@ -109,6 +114,8 @@ private: MovingPercentile _clockSkewMovingPercentile; bool _canAdjustLocks; bool _canRez; + + PacketTypeSequenceMap _lastSequenceNumbers; }; QDebug operator<<(QDebug debug, const Node &message); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 819c46cdf0..7a60c51986 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -183,7 +183,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr } case PacketTypePingReply: { SharedNodePointer sendingNode = sendingNodeForPacket(packet); - + if (sendingNode) { sendingNode->setLastHeardMicrostamp(usecTimestampNow()); @@ -348,7 +348,7 @@ void NodeList::sendDomainServerCheckIn() { } } - QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID); + QByteArray domainServerPacket = byteArrayWithUUIDPopulatedHeader(domainPacketType, packetUUID); QDataStream packetStream(&domainServerPacket, QIODevice::Append); // pack our data to send to the domain-server @@ -369,7 +369,7 @@ void NodeList::sendDomainServerCheckIn() { } if (!isUsingDTLS) { - writeDatagram(domainServerPacket, _domainHandler.getSockAddr(), QUuid()); + writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr()); } const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; @@ -424,10 +424,9 @@ int NodeList::processDomainServerList(const QByteArray& packet) { _domainHandler.setUUID(uuidFromPacketHeader(packet)); _domainHandler.setIsConnected(true); } - + int readNodes = 0; - - + QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); @@ -497,7 +496,7 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { QByteArray publicPingPacket = constructPingPacket(PingType::Public); writeDatagram(publicPingPacket, node, node->getPublicSocket()); - + if (!node->getSymmetricSocket().isNull()) { QByteArray symmetricPingPacket = constructPingPacket(PingType::Symmetric); writeDatagram(symmetricPingPacket, node, node->getSymmetricSocket()); diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index ec83e18c3d..32c64becc0 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -9,14 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "PacketHeaders.h" + #include #include -#include "NodeList.h" - -#include "PacketHeaders.h" - int arithmeticCodingValueFromBuffer(const char* checkValue) { if (((uchar) *checkValue) < 255) { return *checkValue; @@ -45,8 +43,8 @@ int packArithmeticallyCodedValue(int value, char* destination) { } } -PacketVersion versionForPacketType(PacketType type) { - switch (type) { +PacketVersion versionForPacketType(PacketType packetType) { + switch (packetType) { case PacketTypeMicrophoneAudioNoEcho: case PacketTypeMicrophoneAudioWithEcho: return 2; @@ -57,7 +55,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeInjectAudio: return 1; case PacketTypeAvatarData: - return 5; + return 6; case PacketTypeAvatarIdentity: return 1; case PacketTypeEnvironmentData: @@ -86,8 +84,8 @@ PacketVersion versionForPacketType(PacketType type) { #define PACKET_TYPE_NAME_LOOKUP(x) case x: return QString(#x); -QString nameForPacketType(PacketType type) { - switch (type) { +QString nameForPacketType(PacketType packetType) { + switch (packetType) { PACKET_TYPE_NAME_LOOKUP(PacketTypeUnknown); PACKET_TYPE_NAME_LOOKUP(PacketTypeStunResponse); PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainList); @@ -132,71 +130,81 @@ QString nameForPacketType(PacketType type) { PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPing); PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPingReply); default: - return QString("Type: ") + QString::number((int)type); + return QString("Type: ") + QString::number((int)packetType); } return QString("unexpected"); } -QByteArray byteArrayWithPopulatedHeader(PacketType type, const QUuid& connectionUUID) { +QByteArray byteArrayWithUUIDPopulatedHeader(PacketType packetType, const QUuid& connectionUUID) { QByteArray freshByteArray(MAX_PACKET_HEADER_BYTES, 0); - freshByteArray.resize(populatePacketHeader(freshByteArray, type, connectionUUID)); + freshByteArray.resize(populatePacketHeaderWithUUID(freshByteArray, packetType, connectionUUID)); return freshByteArray; } -int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID) { - if (packet.size() < numBytesForPacketHeaderGivenPacketType(type)) { - packet.resize(numBytesForPacketHeaderGivenPacketType(type)); +int populatePacketHeaderWithUUID(QByteArray& packet, PacketType packetType, const QUuid& connectionUUID) { + if (packet.size() < numBytesForPacketHeaderGivenPacketType(packetType)) { + packet.resize(numBytesForPacketHeaderGivenPacketType(packetType)); } - return populatePacketHeader(packet.data(), type, connectionUUID); + return populatePacketHeaderWithUUID(packet.data(), packetType, connectionUUID); } -int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID) { - int numTypeBytes = packArithmeticallyCodedValue(type, packet); - packet[numTypeBytes] = versionForPacketType(type); +int populatePacketHeaderWithUUID(char* packet, PacketType packetType, const QUuid& connectionUUID) { + int numTypeBytes = packArithmeticallyCodedValue(packetType, packet); + packet[numTypeBytes] = versionForPacketType(packetType); char* position = packet + numTypeBytes + sizeof(PacketVersion); - QUuid packUUID = connectionUUID.isNull() ? DependencyManager::get()->getSessionUUID() : connectionUUID; - - QByteArray rfcUUID = packUUID.toRfc4122(); + QByteArray rfcUUID = connectionUUID.toRfc4122(); memcpy(position, rfcUUID.constData(), NUM_BYTES_RFC4122_UUID); position += NUM_BYTES_RFC4122_UUID; - if (!NON_VERIFIED_PACKETS.contains(type)) { + if (!NON_VERIFIED_PACKETS.contains(packetType)) { // pack 16 bytes of zeros where the md5 hash will be placed once data is packed memset(position, 0, NUM_BYTES_MD5_HASH); position += NUM_BYTES_MD5_HASH; } - + + if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) { + // Pack zeros for the number of bytes that the sequence number requires. + // The LimitedNodeList will handle packing in the sequence number when sending out the packet. + memset(position, 0, sizeof(PacketSequenceNumber)); + position += sizeof(PacketSequenceNumber); + } + // return the number of bytes written for pointer pushing return position - packet; } int numBytesForPacketHeader(const QByteArray& packet) { - // returns the number of bytes used for the type, version, and UUID - return numBytesArithmeticCodingFromBuffer(packet.data()) - + numHashBytesInPacketHeaderGivenPacketType(packetTypeForPacket(packet)) - + NUM_STATIC_HEADER_BYTES; + PacketType packetType = packetTypeForPacket(packet); + return numBytesForPacketHeaderGivenPacketType(packetType); } int numBytesForPacketHeader(const char* packet) { - // returns the number of bytes used for the type, version, and UUID - return numBytesArithmeticCodingFromBuffer(packet) - + numHashBytesInPacketHeaderGivenPacketType(packetTypeForPacket(packet)) + PacketType packetType = packetTypeForPacket(packet); + return numBytesForPacketHeaderGivenPacketType(packetType); +} + +int numBytesForArithmeticCodedPacketType(PacketType packetType) { + return (int) ceilf((float) packetType / 255); +} + +int numBytesForPacketHeaderGivenPacketType(PacketType packetType) { + return numBytesForArithmeticCodedPacketType(packetType) + + numHashBytesForType(packetType) + + numSequenceNumberBytesForType(packetType) + NUM_STATIC_HEADER_BYTES; } -int numBytesForPacketHeaderGivenPacketType(PacketType type) { - return (int) ceilf((float)type / 255) - + numHashBytesInPacketHeaderGivenPacketType(type) - + NUM_STATIC_HEADER_BYTES; +int numHashBytesForType(PacketType packetType) { + return (NON_VERIFIED_PACKETS.contains(packetType) ? 0 : NUM_BYTES_MD5_HASH); } -int numHashBytesInPacketHeaderGivenPacketType(PacketType type) { - return (NON_VERIFIED_PACKETS.contains(type) ? 0 : NUM_BYTES_MD5_HASH); +int numSequenceNumberBytesForType(PacketType packetType) { + return (SEQUENCE_NUMBERED_PACKETS.contains(packetType) ? sizeof(PacketSequenceNumber) : 0); } QUuid uuidFromPacketHeader(const QByteArray& packet) { @@ -204,8 +212,16 @@ QUuid uuidFromPacketHeader(const QByteArray& packet) { NUM_BYTES_RFC4122_UUID)); } +int hashOffsetForPacketType(PacketType packetType) { + return numBytesForArithmeticCodedPacketType(packetType) + NUM_STATIC_HEADER_BYTES; +} + +int sequenceNumberOffsetForPacketType(PacketType packetType) { + return numBytesForPacketHeaderGivenPacketType(packetType) - sizeof(PacketSequenceNumber); +} + QByteArray hashFromPacketHeader(const QByteArray& packet) { - return packet.mid(numBytesForPacketHeader(packet) - NUM_BYTES_MD5_HASH, NUM_BYTES_MD5_HASH); + return packet.mid(hashOffsetForPacketType(packetTypeForPacket(packet)), NUM_BYTES_MD5_HASH); } QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& connectionUUID) { @@ -213,11 +229,48 @@ QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& QCryptographicHash::Md5); } -void replaceHashInPacketGivenConnectionUUID(QByteArray& packet, const QUuid& connectionUUID) { - packet.replace(numBytesForPacketHeader(packet) - NUM_BYTES_MD5_HASH, NUM_BYTES_MD5_HASH, +PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketType packetType) { + if (packetType == PacketTypeUnknown) { + packetType = packetTypeForPacket(packet); + } + + PacketSequenceNumber result = DEFAULT_SEQUENCE_NUMBER; + + if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) { + memcpy(&result, packet.data() + sequenceNumberOffsetForPacketType(packetType), sizeof(PacketSequenceNumber)); + } + + return result; +} + +void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketType packetType) { + if (packetType == PacketTypeUnknown) { + packetType = packetTypeForPacket(packet); + } + + packet.replace(hashOffsetForPacketType(packetType), NUM_BYTES_MD5_HASH, hashForPacketAndConnectionUUID(packet, connectionUUID)); } +void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequenceNumber, PacketType packetType) { + if (packetType == PacketTypeUnknown) { + packetType = packetTypeForPacket(packet); + } + + packet.replace(sequenceNumberOffsetForPacketType(packetType), + sizeof(PacketSequenceNumber), reinterpret_cast(&sequenceNumber), sizeof(PacketSequenceNumber)); +} + +void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber, + PacketType packetType) { + if (packetType == PacketTypeUnknown) { + packetType = packetTypeForPacket(packet); + } + + replaceHashInPacket(packet, connectionUUID, packetType); + replaceSequenceNumberInPacket(packet, sequenceNumber, packetType); +} + PacketType packetTypeForPacket(const QByteArray& packet) { return (PacketType) arithmeticCodingValueFromBuffer(packet.data()); } diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 38aeed4993..196b9eaaff 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -12,14 +12,19 @@ #ifndef hifi_PacketHeaders_h #define hifi_PacketHeaders_h +#pragma once + +#include +#include + #include #include #include #include "UUID.h" -// NOTE: if adding a new packet type, you can replace one marked usable or add at the end -// NOTE: if you want the name of the packet type to be available for debugging or logging, update nameForPacketType() as well +// NOTE: if adding a new packet packetType, you can replace one marked usable or add at the end +// NOTE: if you want the name of the packet packetType to be available for debugging or logging, update nameForPacketType() as well enum PacketType { PacketTypeUnknown, // 0 PacketTypeStunResponse, @@ -79,6 +84,11 @@ enum PacketType { typedef char PacketVersion; +typedef uint16_t PacketSequenceNumber; +const PacketSequenceNumber DEFAULT_SEQUENCE_NUMBER = 0; + +typedef std::map PacketTypeSequenceMap; + const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied @@ -88,33 +98,53 @@ const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse << PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode; +const QSet SEQUENCE_NUMBERED_PACKETS = QSet() + << PacketTypeAvatarData; + const int NUM_BYTES_MD5_HASH = 16; const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; const int MAX_PACKET_HEADER_BYTES = sizeof(PacketType) + NUM_BYTES_MD5_HASH + NUM_STATIC_HEADER_BYTES; -PacketVersion versionForPacketType(PacketType type); -QString nameForPacketType(PacketType type); +PacketType packetTypeForPacket(const QByteArray& packet); +PacketType packetTypeForPacket(const char* packet); + +PacketVersion versionForPacketType(PacketType packetType); +QString nameForPacketType(PacketType packetType); const QUuid nullUUID = QUuid(); -QByteArray byteArrayWithPopulatedHeader(PacketType type, const QUuid& connectionUUID = nullUUID); -int populatePacketHeader(QByteArray& packet, PacketType type, const QUuid& connectionUUID = nullUUID); -int populatePacketHeader(char* packet, PacketType type, const QUuid& connectionUUID = nullUUID); +QByteArray byteArrayWithUUIDPopulatedHeader(PacketType packetType, const QUuid& connectionUUID); +int populatePacketHeaderWithUUID(QByteArray& packet, PacketType packetType, const QUuid& connectionUUID); +int populatePacketHeaderWithUUID(char* packet, PacketType packetType, const QUuid& connectionUUID); -int numHashBytesInPacketHeaderGivenPacketType(PacketType type); +int numHashBytesForType(PacketType packetType); +int numSequenceNumberBytesForType(PacketType packetType); int numBytesForPacketHeader(const QByteArray& packet); int numBytesForPacketHeader(const char* packet); -int numBytesForPacketHeaderGivenPacketType(PacketType type); +int numBytesForArithmeticCodedPacketType(PacketType packetType); +int numBytesForPacketHeaderGivenPacketType(PacketType packetType); QUuid uuidFromPacketHeader(const QByteArray& packet); +int hashOffsetForPacketType(PacketType packetType); +int sequenceNumberOffsetForPacketType(PacketType packetType); + QByteArray hashFromPacketHeader(const QByteArray& packet); QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid& connectionUUID); -void replaceHashInPacketGivenConnectionUUID(QByteArray& packet, const QUuid& connectionUUID); -PacketType packetTypeForPacket(const QByteArray& packet); -PacketType packetTypeForPacket(const char* packet); +// NOTE: The following four methods accept a PacketType which defaults to PacketTypeUnknown. +// If the caller has already looked at the packet type and can provide it then the methods below won't have to look it up. + +PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketType packetType = PacketTypeUnknown); + +void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketType packetType = PacketTypeUnknown); + +void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequenceNumber, + PacketType packetType = PacketTypeUnknown); + +void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber, + PacketType packetType = PacketTypeUnknown); int arithmeticCodingValueFromBuffer(const char* checkValue); int numBytesArithmeticCodingFromBuffer(const char* checkValue); diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index c050e0bffe..71c4feda96 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -35,10 +35,13 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) { bool JurisdictionListener::queueJurisdictionRequest() { static unsigned char buffer[MAX_PACKET_SIZE]; unsigned char* bufferOut = &buffer[0]; - int sizeOut = populatePacketHeader(reinterpret_cast(bufferOut), PacketTypeJurisdictionRequest); + + auto nodeList = DependencyManager::get(); + + int sizeOut = nodeList->populatePacketHeader(reinterpret_cast(bufferOut), PacketTypeJurisdictionRequest); int nodeCount = 0; - DependencyManager::get()->eachNode([&](const SharedNodePointer& node) { + nodeList->eachNode([&](const SharedNodePointer& node) { if (node->getType() == getNodeType() && node->getActiveSocket()) { _packetSender.queuePacketForSending(node, QByteArray(reinterpret_cast(bufferOut), sizeOut)); nodeCount++; diff --git a/libraries/octree/src/JurisdictionMap.cpp b/libraries/octree/src/JurisdictionMap.cpp index 79c1a96ccc..46e758cb42 100644 --- a/libraries/octree/src/JurisdictionMap.cpp +++ b/libraries/octree/src/JurisdictionMap.cpp @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include @@ -268,8 +270,9 @@ bool JurisdictionMap::writeToFile(const char* filename) { int JurisdictionMap::packEmptyJurisdictionIntoMessage(NodeType_t type, unsigned char* destinationBuffer, int availableBytes) { unsigned char* bufferStart = destinationBuffer; - - int headerLength = populatePacketHeader(reinterpret_cast(destinationBuffer), PacketTypeJurisdiction); + + int headerLength = DependencyManager::get()->populatePacketHeader(reinterpret_cast(destinationBuffer), + PacketTypeJurisdiction); destinationBuffer += headerLength; // Pack the Node Type in first byte @@ -287,7 +290,8 @@ int JurisdictionMap::packEmptyJurisdictionIntoMessage(NodeType_t type, unsigned int JurisdictionMap::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) { unsigned char* bufferStart = destinationBuffer; - int headerLength = populatePacketHeader(reinterpret_cast(destinationBuffer), PacketTypeJurisdiction); + int headerLength = DependencyManager::get()->populatePacketHeader(reinterpret_cast(destinationBuffer), + PacketTypeJurisdiction); destinationBuffer += headerLength; // Pack the Node Type in first byte diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 65fbb0f983..9b1124a197 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -328,7 +328,8 @@ void OctreeEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer) } void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PacketType type, int nodeClockSkew) { - packetBuffer._currentSize = populatePacketHeader(reinterpret_cast(&packetBuffer._currentBuffer[0]), type); + packetBuffer._currentSize = + DependencyManager::get()->populatePacketHeader(reinterpret_cast(&packetBuffer._currentBuffer[0]), type); // skip over sequence number for now; will be packed when packet is ready to be sent out packetBuffer._currentSize += sizeof(quint16); diff --git a/libraries/octree/src/OctreeHeadlessViewer.cpp b/libraries/octree/src/OctreeHeadlessViewer.cpp index ea5c811ce1..63cd6d39a2 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.cpp +++ b/libraries/octree/src/OctreeHeadlessViewer.cpp @@ -221,7 +221,7 @@ void OctreeHeadlessViewer::queryOctree() { unsigned char* endOfQueryPacket = queryPacket; // insert packet type/version and node UUID - endOfQueryPacket += populatePacketHeader(reinterpret_cast(endOfQueryPacket), packetType); + endOfQueryPacket += nodeList->populatePacketHeader(reinterpret_cast(endOfQueryPacket), packetType); // encode the query data... endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket); diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index c5a5678503..d12d4f1ba1 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -385,7 +385,8 @@ void OctreeSceneStats::childBitsRemoved(bool includesExistsBits, bool includesCo int OctreeSceneStats::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) { unsigned char* bufferStart = destinationBuffer; - int headerLength = populatePacketHeader(reinterpret_cast(destinationBuffer), PacketTypeOctreeStats); + int headerLength = DependencyManager::get()->populatePacketHeader(reinterpret_cast(destinationBuffer), + PacketTypeOctreeStats); destinationBuffer += headerLength; memcpy(destinationBuffer, &_start, sizeof(_start)); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index ac2c212001..f02ef3a549 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -493,7 +493,7 @@ void ScriptEngine::run() { / (1000 * 1000)) + 0.5); const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t); - QByteArray avatarPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarData); + QByteArray avatarPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeAvatarData); avatarPacket.append(_avatarData->toByteArray()); nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer); @@ -534,9 +534,9 @@ void ScriptEngine::run() { } } - QByteArray audioPacket = byteArrayWithPopulatedHeader(silentFrame - ? PacketTypeSilentAudioFrame - : PacketTypeMicrophoneAudioNoEcho); + QByteArray audioPacket = nodeList->byteArrayWithPopulatedHeader(silentFrame + ? PacketTypeSilentAudioFrame + : PacketTypeMicrophoneAudioNoEcho); QDataStream packetStream(&audioPacket, QIODevice::Append);