bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) { PacketType checkType = packetTypeForPacket(packet); int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data()); if (packet[numPacketTypeBytes] != versionForPacketType(checkType) && checkType != PacketTypeStunResponse) { PacketType mismatchType = packetTypeForPacket(packet); static QMultiMap<QUuid, PacketType> versionDebugSuppressMap; QUuid senderUUID = uuidFromPacketHeader(packet); if (!versionDebugSuppressMap.contains(senderUUID, checkType)) { qCDebug(networking) << "Packet version mismatch on" << packetTypeForPacket(packet) << "- Sender" << uuidFromPacketHeader(packet) << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but" << qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected."; emit packetVersionMismatch(); versionDebugSuppressMap.insert(senderUUID, checkType); } return false; } if (!NON_VERIFIED_PACKETS.contains(checkType)) { // figure out which node this is from SharedNodePointer sendingNode = sendingNodeForPacket(packet); if (sendingNode) { // check if the md5 hash in the header matches the hash we would expect if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) { return true; } else { static QMultiMap<QUuid, PacketType> hashDebugSuppressMap; QUuid senderUUID = uuidFromPacketHeader(packet); if (!hashDebugSuppressMap.contains(senderUUID, checkType)) { qCDebug(networking) << "Packet hash mismatch on" << checkType << "- Sender" << uuidFromPacketHeader(packet); hashDebugSuppressMap.insert(senderUUID, checkType); } } } else { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ received from unknown node with UUID"); qCDebug(networking) << "Packet of type" << checkType << "received from unknown node with UUID" << qPrintable(uuidStringWithoutCurlyBraces(uuidFromPacketHeader(packet))); } } else { return true; } return false; }
int AvatarAudioRingBuffer::parseData(const QByteArray& packet) { timeGapStatsFrameReceived(); updateDesiredJitterBufferFrames(); _shouldLoopbackForNode = (packetTypeForPacket(packet) == PacketTypeMicrophoneAudioWithEcho); return PositionalAudioRingBuffer::parseData(packet); }
qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode, const HifiSockAddr& overridenSockAddr) { if (destinationNode) { // if we don't have an ovveriden address, assume they want to send to the node's active socket const HifiSockAddr* destinationSockAddr = &overridenSockAddr; if (overridenSockAddr.isNull()) { if (destinationNode->getActiveSocket()) { // use the node's active socket as the destination socket destinationSockAddr = destinationNode->getActiveSocket(); } else { // we don't have a socket to send to, return 0 return 0; } } 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 return 0; }
void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { // pull any new audio data from nodes off of the network stack PacketType mixerPacketType = packetTypeForPacket(dataByteArray); if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho || mixerPacketType == PacketTypeMicrophoneAudioWithEcho || mixerPacketType == PacketTypeInjectAudio) { QUuid nodeUUID; deconstructPacketHeader(dataByteArray, nodeUUID); NodeList* nodeList = NodeList::getInstance(); SharedNodePointer matchingNode = nodeList->nodeWithUUID(nodeUUID); if (matchingNode) { nodeList->updateNodeWithData(matchingNode.data(), senderSockAddr, dataByteArray); if (!matchingNode->getActiveSocket()) { // we don't have an active socket for this node, but they're talking to us // this means they've heard from us and can reply, let's assume public is active matchingNode->activatePublicSocket(); } } } else { // let processNodeData handle it. NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray); } }
void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { PacketType datagramPacketType = packetTypeForPacket(dataByteArray); if (datagramPacketType == PacketTypeJurisdiction) { int headerBytes = numBytesForPacketHeader(dataByteArray); // PacketType_JURISDICTION, first byte is the node type... switch (dataByteArray[headerBytes]) { case NodeType::VoxelServer: _scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, dataByteArray); break; case NodeType::ParticleServer: _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, dataByteArray); break; } } else if (datagramPacketType == PacketTypeParticleAddResponse) { // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse(dataByteArray); // also give our local particle tree a chance to remap any internal locally created particles _particleTree.handleAddParticleResponse(dataByteArray); } else { NodeList::getInstance()->processNodeData(senderSockAddr, dataByteArray); } }
void AssignmentClient::readPendingDatagrams() { NodeList* nodeList = NodeList::getInstance(); QByteArray receivedPacket; HifiSockAddr senderSockAddr; while (nodeList->getNodeSocket().hasPendingDatagrams()) { receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); if (nodeList->packetVersionAndHashMatch(receivedPacket)) { if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket); if (_currentAssignment) { qDebug() << "Received an assignment -" << *_currentAssignment; // switch our nodelist domain IP and port to whoever sent us the assignment nodeList->getDomainInfo().setSockAddr(senderSockAddr); nodeList->getDomainInfo().setAssignmentUUID(_currentAssignment->getUUID()); qDebug() << "Destination IP for assignment is" << nodeList->getDomainInfo().getIP().toString(); // start the deployed assignment QThread* workerThread = new QThread(this); connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); _currentAssignment->moveToThread(workerThread); // move the NodeList to the thread used for the _current assignment nodeList->moveToThread(workerThread); // let the assignment handle the incoming datagrams for its duration disconnect(&nodeList->getNodeSocket(), 0, this, 0); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment, &ThreadedAssignment::readPendingDatagrams); // Starts an event loop, and emits workerThread->started() workerThread->start(); } else { qDebug() << "Received an assignment that could not be unpacked. Re-requesting."; } } else { // have the NodeList attempt to handle it nodeList->processNodeData(senderSockAddr, receivedPacket); } } } }
int AudioMixerClientData::parseData(const QByteArray& packet) { PacketType packetType = packetTypeForPacket(packet); if (packetType == PacketTypeMicrophoneAudioWithEcho || packetType == PacketTypeMicrophoneAudioNoEcho || packetType == PacketTypeSilentAudioFrame) { // grab the AvatarAudioRingBuffer from the vector (or create it if it doesn't exist) AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); // read the first byte after the header to see if this is a stereo or mono buffer quint8 channelFlag = packet.at(numBytesForPacketHeader(packet)); bool isStereo = channelFlag == 1; if (avatarRingBuffer && avatarRingBuffer->isStereo() != isStereo) { // there's a mismatch in the buffer channels for the incoming and current buffer // so delete our current buffer and create a new one _ringBuffers.removeOne(avatarRingBuffer); avatarRingBuffer->deleteLater(); avatarRingBuffer = NULL; } if (!avatarRingBuffer) { // we don't have an AvatarAudioRingBuffer yet, so add it avatarRingBuffer = new AvatarAudioRingBuffer(isStereo); _ringBuffers.push_back(avatarRingBuffer); } // ask the AvatarAudioRingBuffer instance to parse the data avatarRingBuffer->parseData(packet); } else { // this is injected audio // grab the stream identifier for this injected audio QUuid streamIdentifier = QUuid::fromRfc4122(packet.mid(numBytesForPacketHeader(packet), NUM_BYTES_RFC4122_UUID)); InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL; for (unsigned int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector && ((InjectedAudioRingBuffer*) _ringBuffers[i])->getStreamIdentifier() == streamIdentifier) { matchingInjectedRingBuffer = (InjectedAudioRingBuffer*) _ringBuffers[i]; } } if (!matchingInjectedRingBuffer) { // we don't have a matching injected audio ring buffer, so add it matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier); _ringBuffers.push_back(matchingInjectedRingBuffer); } matchingInjectedRingBuffer->parseData(packet); } return 0; }
bool packetVersionMatch(const QByteArray& packet) { // currently this just checks if the version in the packet matches our return from versionForPacketType // may need to be expanded in the future for types and versions that take > than 1 byte if (packet[1] == versionForPacketType(packetTypeForPacket(packet)) || packetTypeForPacket(packet) == PacketTypeStunResponse) { return true; } else { PacketType mismatchType = packetTypeForPacket(packet); int numPacketTypeBytes = arithmeticCodingValueFromBuffer(packet.data()); QUuid nodeUUID; deconstructPacketHeader(packet, nodeUUID); qDebug() << "Packet mismatch on" << packetTypeForPacket(packet) << "- Sender" << nodeUUID << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but" << qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected."; return false; } }
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()) { if (destinationNode->getActiveSocket()) { // use the node's active socket as the destination socket destinationSockAddr = destinationNode->getActiveSocket(); } else { // we don't have a socket to send to, return 0 return 0; } } 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(datagramCopy, *destinationSockAddr); // Keep track of per-destination-node bandwidth destinationNode->recordBytesSent(bytesWritten); return bytesWritten; } // didn't have a destinationNode to send to, return 0 return 0; }
void AvatarHashMap::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) { switch (packetTypeForPacket(datagram)) { case PacketTypeBulkAvatarData: processAvatarDataPacket(datagram, mixerWeakPointer); break; case PacketTypeAvatarIdentity: processAvatarIdentityPacket(datagram, mixerWeakPointer); break; case PacketTypeAvatarBillboard: processAvatarBillboardPacket(datagram, mixerWeakPointer); break; case PacketTypeKillAvatar: processKillAvatar(datagram); break; default: break; } }
int AudioMixerClientData::parseData(const QByteArray& packet) { PacketType packetType = packetTypeForPacket(packet); if (packetType == PacketTypeMicrophoneAudioWithEcho || packetType == PacketTypeMicrophoneAudioNoEcho || packetType == PacketTypeSilentAudioFrame) { // grab the AvatarAudioRingBuffer from the vector (or create it if it doesn't exist) AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); if (!avatarRingBuffer) { // we don't have an AvatarAudioRingBuffer yet, so add it avatarRingBuffer = new AvatarAudioRingBuffer(); _ringBuffers.push_back(avatarRingBuffer); } // ask the AvatarAudioRingBuffer instance to parse the data avatarRingBuffer->parseData(packet); } else { // this is injected audio // grab the stream identifier for this injected audio QUuid streamIdentifier = QUuid::fromRfc4122(packet.mid(numBytesForPacketHeader(packet), NUM_BYTES_RFC4122_UUID)); InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL; for (unsigned int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector && ((InjectedAudioRingBuffer*) _ringBuffers[i])->getStreamIdentifier() == streamIdentifier) { matchingInjectedRingBuffer = (InjectedAudioRingBuffer*) _ringBuffers[i]; } } if (!matchingInjectedRingBuffer) { // we don't have a matching injected audio ring buffer, so add it matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier); _ringBuffers.push_back(matchingInjectedRingBuffer); } matchingInjectedRingBuffer->parseData(packet); } return 0; }
void MetavoxelServer::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; NodeList* nodeList = NodeList::getInstance(); while (readAvailableDatagram(receivedPacket, senderSockAddr)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { switch (packetTypeForPacket(receivedPacket)) { case PacketTypeMetavoxelData: nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket); break; default: nodeList->processNodeData(senderSockAddr, receivedPacket); break; } } } }
QByteArray LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacket, const QUuid& packetHeaderID) { QDataStream pingPacketStream(pingPacket); pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket)); PingType_t typeFromOriginalPing; pingPacketStream >> typeFromOriginalPing; quint64 timeFromOriginalPing; pingPacketStream >> timeFromOriginalPing; PacketType replyType = (packetTypeForPacket(pingPacket) == PacketTypePing) ? PacketTypePingReply : PacketTypeUnverifiedPingReply; QByteArray replyPacket = byteArrayWithPopulatedHeader(replyType, packetHeaderID); QDataStream packetStream(&replyPacket, QIODevice::Append); packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow(); return replyPacket; }
void AudioMixer::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; NodeList* nodeList = NodeList::getInstance(); while (readAvailableDatagram(receivedPacket, senderSockAddr)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { // pull any new audio data from nodes off of the network stack PacketType mixerPacketType = packetTypeForPacket(receivedPacket); if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho || mixerPacketType == PacketTypeMicrophoneAudioWithEcho || mixerPacketType == PacketTypeInjectAudio || mixerPacketType == PacketTypeSilentAudioFrame) { nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket); } else { // let processNodeData handle it. nodeList->processNodeData(senderSockAddr, receivedPacket); } } } }
int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray &packet) { QMutexLocker locker(&matchingNode->getMutex()); 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()); } if (linkedData) { QMutexLocker linkedDataLocker(&linkedData->getMutex()); return linkedData->parseData(packet); } return 0; }
void OctreeServerDatagramProcessor::readPendingDatagrams() { HifiSockAddr senderSockAddr; static QByteArray incomingPacket; // read everything that is available while (_nodeSocket.hasPendingDatagrams()) { incomingPacket.resize(_nodeSocket.pendingDatagramSize()); // just get this packet off the stack _nodeSocket.readDatagram(incomingPacket.data(), incomingPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); PacketType packetType = packetTypeForPacket(incomingPacket); if (packetType == PacketTypePing) { DependencyManager::get<NodeList>()->processNodeData(senderSockAddr, incomingPacket); return; // don't emit } // emit the signal to tell AudioMixer it needs to process a packet emit packetRequiresProcessing(incomingPacket, senderSockAddr); } }
void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) { NodeList* nodeList = NodeList::getInstance(); if (nodeList->packetVersionAndHashMatch(receivedPacket)) { // pull any new audio data from nodes off of the network stack PacketType mixerPacketType = packetTypeForPacket(receivedPacket); if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho || mixerPacketType == PacketTypeMicrophoneAudioWithEcho || mixerPacketType == PacketTypeInjectAudio || mixerPacketType == PacketTypeSilentAudioFrame || mixerPacketType == PacketTypeAudioStreamStats) { nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket); } else if (mixerPacketType == PacketTypeMuteEnvironment) { QByteArray packet = receivedPacket; populatePacketHeader(packet, PacketTypeMuteEnvironment); foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) { nodeList->writeDatagram(packet, packet.size(), node); } } } else {
int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { // skip the packet header (includes the source UUID) int readBytes = numBytesForPacketHeader(packet); readBytes += parsePositionalData(packet.mid(readBytes)); if (packetTypeForPacket(packet) == PacketTypeSilentAudioFrame) { // this source had no audio to send us, but this counts as a packet // write silence equivalent to the number of silent samples they just sent us int16_t numSilentSamples; memcpy(&numSilentSamples, packet.data() + readBytes, sizeof(int16_t)); readBytes += sizeof(int16_t); addSilentFrame(numSilentSamples); } else { // there is audio data to read readBytes += writeData(packet.data() + readBytes, packet.size() - readBytes); } return readBytes; }
void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { bool extraDebugging = false; if (extraDebugging) { qCDebug(octree) << "OctreeRenderer::processDatagram()"; } if (!_tree) { qCDebug(octree) << "OctreeRenderer::processDatagram() called before init, calling init()..."; this->init(); } bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram()",showTimingDetails); unsigned int packetLength = dataByteArray.size(); PacketType command = packetTypeForPacket(dataByteArray); unsigned int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray); QUuid sourceUUID = uuidFromPacketHeader(dataByteArray); PacketType expectedType = getExpectedPacketType(); // packetVersion is the second byte PacketVersion packetVersion = dataByteArray[1]; if(command == expectedType) { PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails); // if we are getting inbound packets, then our tree is also viewing, and we should remember that fact. _tree->setIsViewing(true); const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(dataByteArray.data()) + numBytesPacketHeader; OCTREE_PACKET_FLAGS flags = (*(OCTREE_PACKET_FLAGS*)(dataAt)); dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt); dataAt += sizeof(OCTREE_PACKET_SENT_TIME); bool packetIsColored = oneAtBit(flags, PACKET_IS_COLOR_BIT); bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT); OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int clockSkew = sourceNode ? sourceNode->getClockSkewUsec() : 0; int flightTime = arrivedAt - sentAt + clockSkew; OCTREE_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0; unsigned int dataBytes = packetLength - (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE); if (extraDebugging) { qCDebug(octree, "OctreeRenderer::processDatagram() ... Got Packet Section" " color:%s compressed:%s sequence: %u flight:%d usec size:%u data:%u", debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), sequence, flightTime, packetLength, dataBytes); } int subsection = 1; while (dataBytes > 0) { if (packetIsCompressed) { if (dataBytes > sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE)) { sectionLength = (*(OCTREE_PACKET_INTERNAL_SECTION_SIZE*)dataAt); dataAt += sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); dataBytes -= sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); } else { sectionLength = 0; dataBytes = 0; // stop looping something is wrong } } else { sectionLength = dataBytes; } if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, sourceUUID, sourceNode, false, packetVersion); _tree->lockForWrite(); OctreePacketData packetData(packetIsCompressed); packetData.loadFinalizedContent(dataAt, sectionLength); if (extraDebugging) { qCDebug(octree, "OctreeRenderer::processDatagram() ... Got Packet Section" " color:%s compressed:%s sequence: %u flight:%d usec size:%u data:%u" " subsection:%d sectionLength:%d uncompressed:%d", debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), sequence, flightTime, packetLength, dataBytes, subsection, sectionLength, packetData.getUncompressedSize()); } if (extraDebugging) { qCDebug(octree) << "OctreeRenderer::processDatagram() ******* START _tree->readBitstreamToTree()..."; } _tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args); if (extraDebugging) { qCDebug(octree) << "OctreeRenderer::processDatagram() ******* END _tree->readBitstreamToTree()..."; } _tree->unlock(); dataBytes -= sectionLength; dataAt += sectionLength; } } subsection++; } }
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) { switch (packetTypeForPacket(packet)) { case PacketTypeDomainList: { processDomainServerList(packet); break; } case PacketTypeDomainServerRequireDTLS: { _domainHandler.parseDTLSRequirementPacket(packet); break; } case PacketTypeIceServerHeartbeatResponse: { _domainHandler.processICEResponsePacket(packet); break; } case PacketTypePing: { // send back a reply SharedNodePointer matchingNode = sendingNodeForPacket(packet); if (matchingNode) { matchingNode->setLastHeardMicrostamp(usecTimestampNow()); QByteArray replyPacket = constructPingReplyPacket(packet); writeDatagram(replyPacket, matchingNode, senderSockAddr); // If we don't have a symmetric socket for this node and this socket doesn't match // what we have for public and local then set it as the symmetric. // This allows a server on a reachable port to communicate with nodes on symmetric NATs if (matchingNode->getSymmetricSocket().isNull()) { if (senderSockAddr != matchingNode->getLocalSocket() && senderSockAddr != matchingNode->getPublicSocket()) { matchingNode->setSymmetricSocket(senderSockAddr); } } } break; } case PacketTypePingReply: { SharedNodePointer sendingNode = sendingNodeForPacket(packet); if (sendingNode) { sendingNode->setLastHeardMicrostamp(usecTimestampNow()); // activate the appropriate socket for this node, if not yet updated activateSocketFromNodeCommunication(packet, sendingNode); // set the ping time for this node for stat collection timePingReply(packet, sendingNode); } break; } case PacketTypeUnverifiedPing: { // send back a reply QByteArray replyPacket = constructPingReplyPacket(packet, _domainHandler.getICEClientID()); writeUnverifiedDatagram(replyPacket, senderSockAddr); break; } case PacketTypeUnverifiedPingReply: { qDebug() << "Received reply from domain-server on" << senderSockAddr; // for now we're unsafely assuming this came back from the domain if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) { qDebug() << "Connecting to domain using local socket"; _domainHandler.activateICELocalSocket(); } else if (senderSockAddr == _domainHandler.getICEPeer().getPublicSocket()) { qDebug() << "Conecting to domain using public socket"; _domainHandler.activateICEPublicSocket(); } else { qDebug() << "Reply does not match either local or public socket for domain. Will not connect."; } } case PacketTypeStunResponse: { // a STUN packet begins with 00, we've checked the second zero with packetVersionMatch // pass it along so it can be processed into our public address and port processSTUNResponse(packet); break; } default: LimitedNodeList::processNodeData(senderSockAddr, packet); break; } }
void Agent::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; NodeList* nodeList = NodeList::getInstance(); while (readAvailableDatagram(receivedPacket, senderSockAddr)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { PacketType datagramPacketType = packetTypeForPacket(receivedPacket); if (datagramPacketType == PacketTypeJurisdiction) { int headerBytes = numBytesForPacketHeader(receivedPacket); SharedNodePointer matchedNode = nodeList->sendingNodeForPacket(receivedPacket); if (matchedNode) { // PacketType_JURISDICTION, first byte is the node type... switch (receivedPacket[headerBytes]) { case NodeType::VoxelServer: _scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener()-> queueReceivedPacket(matchedNode,receivedPacket); break; case NodeType::ParticleServer: _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()-> queueReceivedPacket(matchedNode, receivedPacket); break; case NodeType::ModelServer: _scriptEngine.getModelsScriptingInterface()->getJurisdictionListener()-> queueReceivedPacket(matchedNode, receivedPacket); break; } } } else if (datagramPacketType == PacketTypeParticleAddResponse) { // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse(receivedPacket); // also give our local particle tree a chance to remap any internal locally created particles _particleViewer.getTree()->handleAddParticleResponse(receivedPacket); // Make sure our Node and NodeList knows we've heard from this node. SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket); sourceNode->setLastHeardMicrostamp(usecTimestampNow()); } else if (datagramPacketType == PacketTypeModelAddResponse) { // this will keep creatorTokenIDs to IDs mapped correctly ModelItem::handleAddModelResponse(receivedPacket); // also give our local particle tree a chance to remap any internal locally created particles _modelViewer.getTree()->handleAddModelResponse(receivedPacket); // Make sure our Node and NodeList knows we've heard from this node. SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket); sourceNode->setLastHeardMicrostamp(usecTimestampNow()); } else if (datagramPacketType == PacketTypeParticleData || datagramPacketType == PacketTypeParticleErase || datagramPacketType == PacketTypeOctreeStats || datagramPacketType == PacketTypeVoxelData || datagramPacketType == PacketTypeModelData || datagramPacketType == PacketTypeModelErase ) { // Make sure our Node and NodeList knows we've heard from this node. SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket); sourceNode->setLastHeardMicrostamp(usecTimestampNow()); QByteArray mutablePacket = receivedPacket; int messageLength = mutablePacket.size(); if (datagramPacketType == PacketTypeOctreeStats) { int statsMessageLength = OctreeHeadlessViewer::parseOctreeStats(mutablePacket, sourceNode); if (messageLength > statsMessageLength) { mutablePacket = mutablePacket.mid(statsMessageLength); // TODO: this needs to be fixed, the goal is to test the packet version for the piggyback, but // this is testing the version and hash of the original packet // need to use numBytesArithmeticCodingFromBuffer()... if (!NodeList::getInstance()->packetVersionAndHashMatch(receivedPacket)) { return; // bail since piggyback data doesn't match our versioning } } else { return; // bail since no piggyback data } datagramPacketType = packetTypeForPacket(mutablePacket); } // fall through to piggyback message if (datagramPacketType == PacketTypeParticleData || datagramPacketType == PacketTypeParticleErase) { _particleViewer.processDatagram(mutablePacket, sourceNode); } if (datagramPacketType == PacketTypeModelData || datagramPacketType == PacketTypeModelErase) { _modelViewer.processDatagram(mutablePacket, sourceNode); } if (datagramPacketType == PacketTypeVoxelData) { _voxelViewer.processDatagram(mutablePacket, sourceNode); } } else if (datagramPacketType == PacketTypeMixedAudio || datagramPacketType == PacketTypeSilentAudioFrame) { _receivedAudioStream.parseData(receivedPacket); _lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness(); _receivedAudioStream.clearBuffer(); // let this continue through to the NodeList so it updates last heard timestamp // for the sending audio mixer NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket); } else if (datagramPacketType == PacketTypeBulkAvatarData || datagramPacketType == PacketTypeAvatarIdentity || datagramPacketType == PacketTypeAvatarBillboard || datagramPacketType == PacketTypeKillAvatar) { // let the avatar hash map process it _avatarHashMap.processAvatarMixerDatagram(receivedPacket, nodeList->sendingNodeForPacket(receivedPacket)); // let this continue through to the NodeList so it updates last heard timestamp // for the sending avatar-mixer NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket); } else { NodeList::getInstance()->processNodeData(senderSockAddr, receivedPacket); } } } }
void VoxelPacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "VoxelPacketProcessor::processPacket()"); QByteArray mutablePacket = packet; const int WAY_BEHIND = 300; if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) { qDebug("VoxelPacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); } ssize_t messageLength = mutablePacket.size(); Application* app = Application::getInstance(); bool wasStatsPacket = false; // check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that if (app->_wantToKillLocalVoxels) { app->_voxels.killLocalVoxels(); app->_wantToKillLocalVoxels = false; } PacketType voxelPacketType = packetTypeForPacket(mutablePacket); // note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA // immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first // then process any remaining bytes as if it was another packet if (voxelPacketType == PacketTypeOctreeStats) { int statsMessageLength = app->parseOctreeStats(mutablePacket, sendingNode); wasStatsPacket = true; if (messageLength > statsMessageLength) { mutablePacket = mutablePacket.mid(statsMessageLength); // TODO: this does not look correct, the goal is to test the packet version for the piggyback, but // this is testing the version and hash of the original packet if (!NodeList::getInstance()->packetVersionAndHashMatch(packet)) { return; // bail since piggyback data doesn't match our versioning } } else { // Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket() return; // bail since no piggyback data } } // fall through to piggyback message voxelPacketType = packetTypeForPacket(mutablePacket); if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { app->trackIncomingVoxelPacket(mutablePacket, sendingNode, wasStatsPacket); if (sendingNode) { switch(voxelPacketType) { case PacketTypeParticleErase: { app->_particles.processEraseMessage(mutablePacket, sendingNode); } break; case PacketTypeParticleData: { app->_particles.processDatagram(mutablePacket, sendingNode); } break; case PacketTypeEnvironmentData: { app->_environment.parseData(*sendingNode->getActiveSocket(), mutablePacket); } break; default : { app->_voxels.setDataSourceUUID(sendingNode->getUUID()); app->_voxels.parseData(mutablePacket); app->_voxels.setDataSourceUUID(QUuid()); } break; } } } }
void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { if (_shuttingDown) { qDebug() << "OctreeInboundPacketProcessor::processPacket() while shutting down... ignoring incoming packet"; return; } bool debugProcessPacket = _myServer->wantsVerboseDebug(); if (debugProcessPacket) { qDebug("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%d", &packet, packet.size()); } int numBytesPacketHeader = numBytesForPacketHeader(packet); // Ask our tree subclass if it can handle the incoming packet... PacketType packetType = packetTypeForPacket(packet); if (_myServer->getOctree()->handlesEditPacketType(packetType)) { PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE",debugProcessPacket); _receivedPacketCount++; const unsigned char* packetData = reinterpret_cast<const unsigned char*>(packet.data()); unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader))); quint64 sentAt = (*((quint64*)(packetData + numBytesPacketHeader + sizeof(sequence)))); quint64 arrivedAt = usecTimestampNow(); if (sentAt > arrivedAt) { if (debugProcessPacket || _myServer->wantsDebugReceiving()) { qDebug() << "unreasonable sentAt=" << sentAt << " usecs"; qDebug() << "setting sentAt to arrivedAt=" << arrivedAt << " usecs"; } sentAt = arrivedAt; } quint64 transitTime = arrivedAt - sentAt; int editsInPacket = 0; quint64 processTime = 0; quint64 lockWaitTime = 0; if (debugProcessPacket || _myServer->wantsDebugReceiving()) { qDebug() << "PROCESSING THREAD: got '" << packetType << "' packet - " << _receivedPacketCount << " command from client"; qDebug() << " receivedBytes=" << packet.size(); qDebug() << " sequence=" << sequence; qDebug() << " sentAt=" << sentAt << " usecs"; qDebug() << " arrivedAt=" << arrivedAt << " usecs"; qDebug() << " transitTime=" << transitTime << " usecs"; qDebug() << " sendingNode->getClockSkewUsec()=" << sendingNode->getClockSkewUsec() << " usecs"; } if (debugProcessPacket) { qDebug() << " numBytesPacketHeader=" << numBytesPacketHeader; qDebug() << " sizeof(sequence)=" << sizeof(sequence); qDebug() << " sizeof(sentAt)=" << sizeof(sentAt); } int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt); if (debugProcessPacket) { qDebug() << " atByte=" << atByte; qDebug() << " packet.size()=" << packet.size(); if (atByte >= packet.size()) { qDebug() << " ----- UNEXPECTED ---- got a packet without any edit details!!!! --------"; } } unsigned char* editData = (unsigned char*)&packetData[atByte]; while (atByte < packet.size()) { int maxSize = packet.size() - atByte; if (debugProcessPacket) { qDebug() << " --- inside while loop ---"; qDebug() << " maxSize=" << maxSize; qDebug("OctreeInboundPacketProcessor::processPacket() %c " "packetData=%p packetLength=%d editData=%p atByte=%d maxSize=%d", packetType, packetData, packet.size(), editData, atByte, maxSize); } quint64 startLock = usecTimestampNow(); _myServer->getOctree()->lockForWrite(); quint64 startProcess = usecTimestampNow(); int editDataBytesRead = _myServer->getOctree()->processEditPacketData(packetType, reinterpret_cast<const unsigned char*>(packet.data()), packet.size(), editData, maxSize, sendingNode); if (debugProcessPacket) { qDebug() << "OctreeInboundPacketProcessor::processPacket() after processEditPacketData()..." << "editDataBytesRead=" << editDataBytesRead; } _myServer->getOctree()->unlock(); quint64 endProcess = usecTimestampNow(); editsInPacket++; quint64 thisProcessTime = endProcess - startProcess; quint64 thisLockWaitTime = startProcess - startLock; processTime += thisProcessTime; lockWaitTime += thisLockWaitTime; // skip to next edit record in the packet editData += editDataBytesRead; atByte += editDataBytesRead; if (debugProcessPacket) { qDebug() << " editDataBytesRead=" << editDataBytesRead; qDebug() << " AFTER processEditPacketData atByte=" << atByte; qDebug() << " AFTER processEditPacketData packet.size()=" << packet.size(); } } if (debugProcessPacket) { qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %c " "packetData=%p packetLength=%d editData=%p atByte=%d", packetType, packetData, packet.size(), editData, atByte); } // Make sure our Node and NodeList knows we've heard from this node. QUuid& nodeUUID = DEFAULT_NODE_ID_REF; if (sendingNode) { sendingNode->setLastHeardMicrostamp(usecTimestampNow()); nodeUUID = sendingNode->getUUID(); if (debugProcessPacket) { qDebug() << "sender has uuid=" << nodeUUID; } } else { if (debugProcessPacket) { qDebug() << "sender has no known nodeUUID."; } } trackInboundPacket(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime); } else { qDebug("unknown packet ignored... packetType=%d", packetType); } }
int InboundAudioStream::parseData(const QByteArray& packet) { PacketType packetType = packetTypeForPacket(packet); QUuid senderUUID = uuidFromPacketHeader(packet); // parse header int numBytesHeader = numBytesForPacketHeader(packet); const char* sequenceAt = packet.constData() + numBytesHeader; int readBytes = numBytesHeader; // parse sequence number and track it quint16 sequence = *(reinterpret_cast<const quint16*>(sequenceAt)); readBytes += sizeof(quint16); SequenceNumberStats::ArrivalInfo arrivalInfo = _incomingSequenceNumberStats.sequenceNumberReceived(sequence, senderUUID); frameReceivedUpdateTimingStats(); // TODO: handle generalized silent packet here????? // parse the info after the seq number and before the audio data.(the stream properties) int numAudioSamples; readBytes += parseStreamProperties(packetType, packet.mid(readBytes), numAudioSamples); // handle this packet based on its arrival status. // For now, late packets are ignored. It may be good in the future to insert the late audio frame // into the ring buffer to fill in the missing frame if it hasn't been mixed yet. switch (arrivalInfo._status) { case SequenceNumberStats::Early: { int packetsDropped = arrivalInfo._seqDiffFromExpected; writeSamplesForDroppedPackets(packetsDropped * numAudioSamples); // fall through to OnTime case } case SequenceNumberStats::OnTime: { readBytes += parseAudioData(packetType, packet.mid(readBytes), numAudioSamples); break; } default: { break; } } int framesAvailable = _ringBuffer.framesAvailable(); // if this stream was starved, check if we're still starved. if (_isStarved && framesAvailable >= _desiredJitterBufferFrames) { _isStarved = false; } // if the ringbuffer exceeds the desired size by more than the threshold specified, // drop the oldest frames so the ringbuffer is down to the desired size. if (framesAvailable > _desiredJitterBufferFrames + _maxFramesOverDesired) { int framesToDrop = framesAvailable - (_desiredJitterBufferFrames + DESIRED_JITTER_BUFFER_FRAMES_PADDING); _ringBuffer.shiftReadPosition(framesToDrop * _ringBuffer.getNumFrameSamples()); _framesAvailableStat.reset(); _currentJitterBufferFrames = 0; _oldFramesDropped += framesToDrop; } framesAvailableChanged(); return readBytes; }
int numBytesForPacketHeader(const char* packet) { // returns the number of bytes used for the type, version, and UUID return numBytesArithmeticCodingFromBuffer(packet) + numHashBytesInPacketHeaderGivenPacketType(packetTypeForPacket(packet)) + NUM_STATIC_HEADER_BYTES; }
int AudioMixerClientData::parseData(const QByteArray& packet) { // parse sequence number for this packet int numBytesPacketHeader = numBytesForPacketHeader(packet); const char* sequenceAt = packet.constData() + numBytesPacketHeader; quint16 sequence = *(reinterpret_cast<const quint16*>(sequenceAt)); PacketType packetType = packetTypeForPacket(packet); if (packetType == PacketTypeMicrophoneAudioWithEcho || packetType == PacketTypeMicrophoneAudioNoEcho || packetType == PacketTypeSilentAudioFrame) { _incomingAvatarAudioSequenceNumberStats.sequenceNumberReceived(sequence); // grab the AvatarAudioRingBuffer from the vector (or create it if it doesn't exist) AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); // read the first byte after the header to see if this is a stereo or mono buffer quint8 channelFlag = packet.at(numBytesForPacketHeader(packet) + sizeof(quint16)); bool isStereo = channelFlag == 1; if (avatarRingBuffer && avatarRingBuffer->isStereo() != isStereo) { // there's a mismatch in the buffer channels for the incoming and current buffer // so delete our current buffer and create a new one _ringBuffers.removeOne(avatarRingBuffer); avatarRingBuffer->deleteLater(); avatarRingBuffer = NULL; } if (!avatarRingBuffer) { // we don't have an AvatarAudioRingBuffer yet, so add it avatarRingBuffer = new AvatarAudioRingBuffer(isStereo, AudioMixer::getUseDynamicJitterBuffers()); _ringBuffers.push_back(avatarRingBuffer); } // ask the AvatarAudioRingBuffer instance to parse the data avatarRingBuffer->parseData(packet); } else if (packetType == PacketTypeInjectAudio) { // this is injected audio // grab the stream identifier for this injected audio QUuid streamIdentifier = QUuid::fromRfc4122(packet.mid(numBytesForPacketHeader(packet) + sizeof(quint16), NUM_BYTES_RFC4122_UUID)); if (!_incomingInjectedAudioSequenceNumberStatsMap.contains(streamIdentifier)) { _incomingInjectedAudioSequenceNumberStatsMap.insert(streamIdentifier, SequenceNumberStats(INCOMING_SEQ_STATS_HISTORY_LENGTH)); } _incomingInjectedAudioSequenceNumberStatsMap[streamIdentifier].sequenceNumberReceived(sequence); InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL; for (int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector && ((InjectedAudioRingBuffer*) _ringBuffers[i])->getStreamIdentifier() == streamIdentifier) { matchingInjectedRingBuffer = (InjectedAudioRingBuffer*) _ringBuffers[i]; } } if (!matchingInjectedRingBuffer) { // we don't have a matching injected audio ring buffer, so add it matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier, AudioMixer::getUseDynamicJitterBuffers()); _ringBuffers.push_back(matchingInjectedRingBuffer); } matchingInjectedRingBuffer->parseData(packet); } else if (packetType == PacketTypeAudioStreamStats) { const char* dataAt = packet.data(); // skip over header, appendFlag, and num stats packed dataAt += (numBytesPacketHeader + sizeof(quint8) + sizeof(quint16)); // read the downstream audio stream stats memcpy(&_downstreamAudioStreamStats, dataAt, sizeof(AudioStreamStats)); } return 0; }
void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "OctreePacketProcessor::processPacket()"); QByteArray mutablePacket = packet; const int WAY_BEHIND = 300; if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) { qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); } int messageLength = mutablePacket.size(); Application* app = Application::getInstance(); bool wasStatsPacket = false; PacketType voxelPacketType = packetTypeForPacket(mutablePacket); // note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA // immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first // then process any remaining bytes as if it was another packet if (voxelPacketType == PacketTypeOctreeStats) { int statsMessageLength = app->parseOctreeStats(mutablePacket, sendingNode); wasStatsPacket = true; if (messageLength > statsMessageLength) { mutablePacket = mutablePacket.mid(statsMessageLength); // TODO: this does not look correct, the goal is to test the packet version for the piggyback, but // this is testing the version and hash of the original packet if (!DependencyManager::get<NodeList>()->packetVersionAndHashMatch(packet)) { return; // bail since piggyback data doesn't match our versioning } } else { // Note... stats packets don't have sequence numbers, so we don't want to send those to trackIncomingVoxelPacket() return; // bail since no piggyback data } } // fall through to piggyback message voxelPacketType = packetTypeForPacket(mutablePacket); PacketVersion packetVersion = mutablePacket[1]; PacketVersion expectedVersion = versionForPacketType(voxelPacketType); // check version of piggyback packet against expected version if (packetVersion != expectedVersion) { static QMultiMap<QUuid, PacketType> versionDebugSuppressMap; QUuid senderUUID = uuidFromPacketHeader(packet); if (!versionDebugSuppressMap.contains(senderUUID, voxelPacketType)) { qDebug() << "Packet version mismatch on" << voxelPacketType << "- Sender" << senderUUID << "sent" << (int)packetVersion << "but" << (int)expectedVersion << "expected."; emit packetVersionMismatch(); versionDebugSuppressMap.insert(senderUUID, voxelPacketType); } return; // bail since piggyback version doesn't match } app->trackIncomingOctreePacket(mutablePacket, sendingNode, wasStatsPacket); if (sendingNode) { switch(voxelPacketType) { case PacketTypeEntityErase: { if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) { app->_entities.processEraseMessage(mutablePacket, sendingNode); } } break; case PacketTypeEntityData: { if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) { app->_entities.processDatagram(mutablePacket, sendingNode); } } break; case PacketTypeEnvironmentData: { app->_environment.parseData(*sendingNode->getActiveSocket(), mutablePacket); } break; default: { // nothing to do } break; } } }
void DatagramProcessor::processDatagrams() { if (_isShuttingDown) { return; // bail early... we're shutting down. } PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "DatagramProcessor::processDatagrams()"); HifiSockAddr senderSockAddr; static QByteArray incomingPacket; Application* application = Application::getInstance(); auto nodeList = DependencyManager::get<NodeList>(); while (DependencyManager::get<NodeList>()->getNodeSocket().hasPendingDatagrams()) { incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->readDatagram(incomingPacket, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); _inPacketCount++; _inByteCount += incomingPacket.size(); if (nodeList->packetVersionAndHashMatch(incomingPacket)) { PacketType incomingType = packetTypeForPacket(incomingPacket); // only process this packet if we have a match on the packet version switch (incomingType) { case PacketTypeAudioEnvironment: case PacketTypeAudioStreamStats: case PacketTypeMixedAudio: case PacketTypeSilentAudioFrame: { if (incomingType == PacketTypeAudioStreamStats) { QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioStreamStatsPacket", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else if (incomingType == PacketTypeAudioEnvironment) { QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioEnvironmentData", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else { QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "addReceivedAudioToStream", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } // update having heard from the audio-mixer and record the bytes received SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket); if (audioMixer) { audioMixer->setLastHeardMicrostamp(usecTimestampNow()); } break; } case PacketTypeEntityData: case PacketTypeEntityErase: case PacketTypeOctreeStats: case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _octreeProcessor.queueReceivedPacket()"); SharedNodePointer matchedNode = DependencyManager::get<NodeList>()->sendingNodeForPacket(incomingPacket); if (matchedNode) { // add this packet to our list of octree packets and process them on the octree data processing application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket); } break; } case PacketTypeBulkAvatarData: case PacketTypeKillAvatar: case PacketTypeAvatarIdentity: case PacketTypeAvatarBillboard: { // update having heard from the avatar-mixer and record the bytes received SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket); if (avatarMixer) { avatarMixer->setLastHeardMicrostamp(usecTimestampNow()); QMetaObject::invokeMethod(DependencyManager::get<AvatarManager>().data(), "processAvatarMixerDatagram", Q_ARG(const QByteArray&, incomingPacket), Q_ARG(const QWeakPointer<Node>&, avatarMixer)); } break; } case PacketTypeDomainConnectionDenied: { int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainConnectionDenied); QDataStream packetStream(QByteArray(incomingPacket.constData() + headerSize, incomingPacket.size() - headerSize)); QString reason; packetStream >> reason; // output to the log so the user knows they got a denied connection request // and check and signal for an access token so that we can make sure they are logged in qCDebug(interfaceapp) << "The domain-server denied a connection request: " << reason; qCDebug(interfaceapp) << "You may need to re-log to generate a keypair so you can provide a username signature."; application->domainConnectionDenied(reason); AccountManager::getInstance().checkAndSignalForAccessToken(); break; } case PacketTypeNoisyMute: case PacketTypeMuteEnvironment: { bool mute = !DependencyManager::get<AudioClient>()->isMuted(); if (incomingType == PacketTypeMuteEnvironment) { glm::vec3 position; float radius; int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment); memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3)); memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float)); float distance = glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), position); mute = mute && (distance < radius); } if (mute) { DependencyManager::get<AudioClient>()->toggleMute(); if (incomingType == PacketTypeMuteEnvironment) { AudioScriptingInterface::getInstance().environmentMuted(); } else { AudioScriptingInterface::getInstance().mutedByMixer(); } } break; } case PacketTypeEntityEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_entityEditSender.processNackPacket(incomingPacket); } break; default: nodeList->processNodeData(senderSockAddr, incomingPacket); break; } } }
void DataServer::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; while (_socket.hasPendingDatagrams()) { receivedPacket.resize(_socket.pendingDatagramSize()); _socket.readDatagram(receivedPacket.data(), _socket.pendingDatagramSize(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); PacketType requestType = packetTypeForPacket(receivedPacket); if ((requestType == PacketTypeDataServerPut || requestType == PacketTypeDataServerGet) && receivedPacket[numBytesArithmeticCodingFromBuffer(receivedPacket.data())] == versionForPacketType(requestType)) { QDataStream packetStream(receivedPacket); int numReceivedHeaderBytes = numBytesForPacketHeader(receivedPacket); packetStream.skipRawData(numReceivedHeaderBytes); // pull the sequence number used for this packet quint8 sequenceNumber = 0; packetStream >> sequenceNumber; // pull the UUID that we will need as part of the key QString userString; packetStream >> userString; QUuid parsedUUID(userString); if (parsedUUID.isNull()) { // we failed to parse a UUID, this means the user has sent us a username // ask redis for the UUID for this user redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(userString)); if (reply->type == REDIS_REPLY_STRING) { parsedUUID = QUuid(QString(reply->str)); } if (!parsedUUID.isNull()) { qDebug() << "Found UUID" << parsedUUID << "for username" << userString; } else { qDebug() << "Failed UUID lookup for username" << userString; } freeReplyObject(reply); reply = NULL; } if (!parsedUUID.isNull()) { if (requestType == PacketTypeDataServerPut) { // pull the key and value that specifies the data the user is putting/getting QString dataKey, dataValue; packetStream >> dataKey >> dataValue; qDebug("Sending command to redis: SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey), qPrintable(dataValue)); redisReply* reply = (redisReply*) redisCommand(_redis, "SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey), qPrintable(dataValue)); if (reply->type == REDIS_REPLY_STATUS && strcmp("OK", reply->str) == 0) { // if redis stored the value successfully reply back with a confirm // which is a reply packet with the sequence number QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm, _uuid); replyPacket.append(sequenceNumber); _socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } freeReplyObject(reply); } else { // setup a send packet with the returned data // leverage the packetData sent by overwriting and appending QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend, _uuid); QDataStream sendPacketStream(&sendPacket, QIODevice::Append); sendPacketStream << sequenceNumber; // pull the key list that specifies the data the user is putting/getting QString keyListString; packetStream >> keyListString; if (keyListString != "uuid") { // copy the parsed UUID sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID); const char MULTI_KEY_VALUE_SEPARATOR = '|'; // append the keyListString back to the sendPacket sendPacketStream << keyListString; QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR); QStringList valueList; foreach (const QString& dataKey, keyList) { qDebug("Sending command to redis: GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); redisReply* reply = (redisReply*) redisCommand(_redis, "GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); if (reply->len) { // copy the value that redis returned valueList << QString(reply->str); } else { // didn't find a value - insert a space valueList << QChar(' '); } freeReplyObject(reply); } // append the value QStringList using the right separator sendPacketStream << valueList.join(MULTI_KEY_VALUE_SEPARATOR); } else {
void DatagramProcessor::processDatagrams() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "DatagramProcessor::processDatagrams()"); HifiSockAddr senderSockAddr; static QByteArray incomingPacket; Application* application = Application::getInstance(); NodeList* nodeList = NodeList::getInstance(); while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams()) { incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); _packetCount++; _byteCount += incomingPacket.size(); if (nodeList->packetVersionAndHashMatch(incomingPacket)) { PacketType incomingType = packetTypeForPacket(incomingPacket); // only process this packet if we have a match on the packet version switch (incomingType) { case PacketTypeAudioEnvironment: case PacketTypeAudioStreamStats: case PacketTypeMixedAudio: case PacketTypeSilentAudioFrame: { if (incomingType == PacketTypeAudioStreamStats) { QMetaObject::invokeMethod(&application->_audio, "parseAudioStreamStatsPacket", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else if (incomingType == PacketTypeAudioEnvironment) { QMetaObject::invokeMethod(&application->_audio, "parseAudioEnvironmentData", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else { QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToStream", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } // update having heard from the audio-mixer and record the bytes received SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket); if (audioMixer) { audioMixer->setLastHeardMicrostamp(usecTimestampNow()); audioMixer->recordBytesReceived(incomingPacket.size()); } break; } case PacketTypeEntityAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly EntityItemID::handleAddEntityResponse(incomingPacket); application->getEntities()->getTree()->handleAddEntityResponse(incomingPacket); break; case PacketTypeEntityData: case PacketTypeEntityErase: case PacketTypeVoxelData: case PacketTypeVoxelErase: case PacketTypeOctreeStats: case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _octreeProcessor.queueReceivedPacket()"); bool wantExtraDebugging = application->getLogger()->extraDebugging(); if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) { int numBytesPacketHeader = numBytesForPacketHeader(incomingPacket); unsigned char* dataAt = reinterpret_cast<unsigned char*>(incomingPacket.data()) + numBytesPacketHeader; dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt); dataAt += sizeof(OCTREE_PACKET_SENT_TIME); OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int flightTime = arrivedAt - sentAt; qDebug("got an Octree data or erase message, sequence:%d flightTime:%d", sequence, flightTime); } SharedNodePointer matchedNode = NodeList::getInstance()->sendingNodeForPacket(incomingPacket); if (matchedNode) { // add this packet to our list of voxel packets and process them on the voxel processing application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket); } break; } case PacketTypeMetavoxelData: nodeList->findNodeAndUpdateWithDataFromPacket(incomingPacket); break; case PacketTypeBulkAvatarData: case PacketTypeKillAvatar: case PacketTypeAvatarIdentity: case PacketTypeAvatarBillboard: { // update having heard from the avatar-mixer and record the bytes received SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket); if (avatarMixer) { avatarMixer->setLastHeardMicrostamp(usecTimestampNow()); avatarMixer->recordBytesReceived(incomingPacket.size()); QMetaObject::invokeMethod(&application->getAvatarManager(), "processAvatarMixerDatagram", Q_ARG(const QByteArray&, incomingPacket), Q_ARG(const QWeakPointer<Node>&, avatarMixer)); } application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size()); break; } case PacketTypeDomainConnectionDenied: { // output to the log so the user knows they got a denied connection request // and check and signal for an access token so that we can make sure they are logged in qDebug() << "The domain-server denied a connection request."; qDebug() << "You may need to re-log to generate a keypair so you can provide a username signature."; AccountManager::getInstance().checkAndSignalForAccessToken(); break; } case PacketTypeNoisyMute: case PacketTypeMuteEnvironment: { bool mute = !Application::getInstance()->getAudio()->getMuted(); if (incomingType == PacketTypeMuteEnvironment) { glm::vec3 position; float radius, distance; int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment); memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3)); memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float)); distance = glm::distance(Application::getInstance()->getAvatar()->getPosition(), position); mute = mute && (distance < radius); } if (mute) { Application::getInstance()->getAudio()->toggleMute(); if (incomingType == PacketTypeMuteEnvironment) { AudioScriptingInterface::getInstance().environmentMuted(); } else { AudioScriptingInterface::getInstance().mutedByMixer(); } } break; } case PacketTypeVoxelEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_voxelEditSender.processNackPacket(incomingPacket); } break; case PacketTypeEntityEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_entityEditSender.processNackPacket(incomingPacket); } break; default: nodeList->processNodeData(senderSockAddr, incomingPacket); break; } } }