uint qHash(const HifiSockAddr& sockAddr) { if (sockAddr.getAddress().isNull()) { return 0; // shouldn't happen, but if it does, zero is a perfectly valid hash } quint32 address = sockAddr.getAddress().toIPv4Address(); return sockAddr.getPort() + qHash(QByteArray::fromRawData((char*) &address, sizeof(address))); }
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); } } } }
void IceServer::processDatagrams() { HifiSockAddr sendingSockAddr; while (_serverSocket.hasPendingDatagrams()) { // setup a buffer to read the packet into int packetSizeWithHeader = _serverSocket.pendingDatagramSize(); auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]); _serverSocket.readDatagram(buffer.get(), packetSizeWithHeader, sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer()); // make sure that this packet at least looks like something we can read if (packetSizeWithHeader >= Packet::localHeaderSize(PacketType::ICEServerHeartbeat)) { auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, sendingSockAddr); PacketType::Value packetType = packet->getType(); if (packetType == PacketType::ICEServerHeartbeat) { SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(*packet); // so that we can send packets to the heartbeating peer when we need, we need to activate a socket now peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr); } else if (packetType == PacketType::ICEServerQuery) { QDataStream heartbeatStream(packet.get()); // this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer? QUuid senderUUID; heartbeatStream >> senderUUID; // pull the public and private sock addrs for this peer HifiSockAddr publicSocket, localSocket; heartbeatStream >> publicSocket >> localSocket; // check if this node also included a UUID that they would like to connect to QUuid connectRequestID; heartbeatStream >> connectRequestID; SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID); if (matchingPeer) { qDebug() << "Sending information for peer" << connectRequestID << "to peer" << senderUUID; // we have the peer they want to connect to - send them pack the information for that peer sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr); // we also need to send them to the active peer they are hoping to connect to // create a dummy peer object we can pass to sendPeerInformationPacket NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket); sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket()); } else { qDebug() << "Peer" << senderUUID << "asked for" << connectRequestID << "but no matching peer found"; } } }
void HifiSockAddr::swap(HifiSockAddr& otherSockAddr) { using std::swap; swap(_address, otherSockAddr._address); swap(_port, otherSockAddr._port); // Swap objects name auto temp = otherSockAddr.objectName(); otherSockAddr.setObjectName(objectName()); setObjectName(temp); }
bool ThreadedAssignment::readAvailableDatagram(QByteArray& destinationByteArray, HifiSockAddr& senderSockAddr) { NodeList* nodeList = NodeList::getInstance(); if (nodeList->getNodeSocket().hasPendingDatagrams()) { destinationByteArray.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(destinationByteArray.data(), destinationByteArray.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); return true; } else { return false; } }
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode, const HifiSockAddr& overridenSockAddr) { if (overridenSockAddr.isNull() && !destinationNode.getActiveSocket()) { qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node. Not sending."; return 0; } // use the node's active socket as the destination socket if there is no overriden socket address auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket() : overridenSockAddr; return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret()); }
int NodeList::processDomainServerList(const QByteArray& packet) { // this is a packet from the domain server, reset the count of un-replied check-ins _numNoReplyDomainCheckIns = 0; // if this was the first domain-server list from this domain, we've now connected if (!_domainHandler.isConnected()) { _domainHandler.setUUID(uuidFromPacketHeader(packet)); _domainHandler.setIsConnected(true); } int readNodes = 0; // setup variables to read into from QDataStream qint8 nodeType; QUuid nodeUUID, connectionUUID; HifiSockAddr nodePublicSocket; HifiSockAddr nodeLocalSocket; QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); // pull our owner UUID from the packet, it's always the first thing QUuid newUUID; packetStream >> newUUID; setSessionUUID(newUUID); // pull each node in the packet while(packetStream.device()->pos() < packet.size()) { packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket; // if the public socket address is 0 then it's reachable at the same IP // as the domain server if (nodePublicSocket.getAddress().isNull()) { nodePublicSocket.setAddress(_domainHandler.getIP()); } SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket); packetStream >> connectionUUID; node->setConnectionSecret(connectionUUID); } // ping inactive nodes in conjunction with receipt of list from domain-server // this makes it happen every second and also pings any newly added nodes pingInactiveNodes(); return readNodes; }
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(datagram, destinationSockAddr.getAddress(), destinationSockAddr.getPort()); if (bytesWritten < 0) { qCDebug(networking) << "ERROR in writeDatagram:" << _nodeSocket.error() << "-" << _nodeSocket.errorString(); } return bytesWritten; }
HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) : QObject(), _address(otherSockAddr._address), _port(otherSockAddr._port) { setObjectName(otherSockAddr.objectName()); }
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode, const HifiSockAddr& overridenSockAddr) { // use the node's active socket as the destination socket if there is no overriden socket address auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket() : overridenSockAddr; return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret()); }
AssignmentClient::AssignmentClient(int &argc, char **argv, Assignment::Type requestAssignmentType, const HifiSockAddr& customAssignmentServerSocket, const char* requestAssignmentPool) : QCoreApplication(argc, argv), _requestAssignment(Assignment::RequestCommand, requestAssignmentType, requestAssignmentPool), _currentAssignment(NULL) { // register meta type is required for queued invoke method on Assignment subclasses // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); // create a NodeList as an unassigned client NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED); // set the custom assignment socket if we have it if (!customAssignmentServerSocket.isNull()) { nodeList->setAssignmentServerSocket(customAssignmentServerSocket); } // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required qDebug() << "Waiting for assignment -" << _requestAssignment << "\n"; QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS); // connect our readPendingDatagrams method to the readyRead() signal of the socket connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); }
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 AudioMixerDatagramProcessor::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()); // emit the signal to tell AudioMixer it needs to process a packet emit packetRequiresProcessing(incomingPacket, senderSockAddr); } }
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 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); } }
std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destination) { Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*"); auto queue = std::unique_ptr<SendQueue>(new SendQueue(socket, destination)); // Setup queue private thread QThread* thread = new QThread; thread->setObjectName("Networking: SendQueue " + destination.objectName()); // Name thread for easier debug connect(thread, &QThread::started, queue.get(), &SendQueue::run); connect(queue.get(), &QObject::destroyed, thread, &QThread::quit); // Thread auto cleanup connect(thread, &QThread::finished, thread, &QThread::deleteLater); // Thread auto cleanup // Move queue to private thread and start it queue->moveToThread(thread); thread->start(); return queue; }
qint64 LimitedNodeList::writeDatagram(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; } } return writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret()); } // didn't have a destinationNode to send to, return 0 return 0; }
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); } // stat collection for packets ++_numCollectedPackets; _numCollectedBytes += datagram.size(); qint64 bytesWritten = _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr.getAddress(), destinationSockAddr.getPort()); if (bytesWritten < 0) { qDebug() << "ERROR in writeDatagram:" << _nodeSocket.error() << "-" << _nodeSocket.errorString(); } return bytesWritten; }
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 (packetVersionMatch(receivedPacket)) { if (_currentAssignment) { // have the threaded current assignment handle this datagram QMetaObject::invokeMethod(_currentAssignment, "processDatagram", Qt::QueuedConnection, Q_ARG(QByteArray, receivedPacket), Q_ARG(HifiSockAddr, senderSockAddr)); } else if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { if (_currentAssignment) { qDebug() << "Dropping received assignment since we are currently running one."; } else { // 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->setDomainSockAddr(senderSockAddr); nodeList->setOwnerUUID(_currentAssignment->getUUID()); qDebug() << "Destination IP for assignment is" << nodeList->getDomainIP().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); // 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 main(int argc, const char* argv[]) { serverSocket.bind(QHostAddress::LocalHost, PAIRING_SERVER_LISTEN_PORT); HifiSockAddr senderSockAddr; char senderData[MAX_PACKET_SIZE_BYTES] = {}; while (true) { if (::serverSocket.hasPendingDatagrams() && ::serverSocket.readDatagram(senderData, MAX_PACKET_SIZE_BYTES, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer())) { if (senderData[0] == 'A') { // this is a device reporting itself as available PairableDevice tempDevice = {}; char deviceAddress[INET_ADDRSTRLEN] = {}; int socketPort = 0; int numMatches = sscanf(senderData, "Available %s %[^:]:%d %s", tempDevice.identifier, deviceAddress, &socketPort, tempDevice.name); if (numMatches >= 3) { // if we have fewer than 3 matches the packet wasn't properly formatted // setup the localSocket for the pairing device tempDevice.localSocket.setAddress(QHostAddress(deviceAddress)); tempDevice.localSocket.setPort(socketPort); // store this device's sending socket so we can talk back to it tempDevice.sendingSocket = senderSockAddr; // push this new device into the vector printf("New last device is %s (%s) at %s:%d\n", tempDevice.identifier, tempDevice.name, deviceAddress, socketPort); // copy the tempDevice to the persisting lastDevice ::lastDevice = new PairableDevice(tempDevice); if (::lastClient) { sendLastClientToLastDevice(); } } } else if (senderData[0] == 'F') { // this is a client looking to pair with a device // send the most recent device this address so it can attempt to pair RequestingClient tempClient = {}; int requestorMatches = sscanf(senderData, "Find %[^:]:%d", tempClient.address, &tempClient.port); if (requestorMatches == 2) { // good data, copy the tempClient to the persisting lastInterfaceClient ::lastClient = new RequestingClient(tempClient); printf("New last client is at %s:%d\n", ::lastClient->address, ::lastClient->port); if (::lastDevice) { sendLastClientToLastDevice(); } } } } } }
void NodeBounds::draw() { if (!(_showVoxelNodes || _showModelNodes || _showParticleNodes)) { _overlayText[0] = '\0'; return; } NodeToJurisdictionMap& voxelServerJurisdictions = Application::getInstance()->getVoxelServerJurisdictions(); NodeToJurisdictionMap& modelServerJurisdictions = Application::getInstance()->getModelServerJurisdictions(); NodeToJurisdictionMap& particleServerJurisdictions = Application::getInstance()->getParticleServerJurisdictions(); NodeToJurisdictionMap* serverJurisdictions; // Compute ray to find selected nodes later on. We can't use the pre-computed ray in Application because it centers // itself after the cursor disappears. Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); float mouseX = application->getMouseX() / (float)glWidget->width(); float mouseY = application->getMouseY() / (float)glWidget->height(); glm::vec3 mouseRayOrigin; glm::vec3 mouseRayDirection; application->getViewFrustum()->computePickRay(mouseX, mouseY, mouseRayOrigin, mouseRayDirection); // Variables to keep track of the selected node and properties to draw the cube later if needed Node* selectedNode = NULL; float selectedDistance = FLT_MAX; bool selectedIsInside = true; glm::vec3 selectedCenter; float selectedScale = 0; NodeList* nodeList = NodeList::getInstance(); foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { NodeType_t nodeType = node->getType(); if (nodeType == NodeType::VoxelServer && _showVoxelNodes) { serverJurisdictions = &voxelServerJurisdictions; } else if (nodeType == NodeType::ModelServer && _showModelNodes) { serverJurisdictions = &modelServerJurisdictions; } else if (nodeType == NodeType::ParticleServer && _showParticleNodes) { serverJurisdictions = &particleServerJurisdictions; } else { continue; } QUuid nodeUUID = node->getUUID(); if (serverJurisdictions->find(nodeUUID) != serverJurisdictions->end()) { const JurisdictionMap& map = serverJurisdictions->value(nodeUUID); unsigned char* rootCode = map.getRootOctalCode(); if (rootCode) { VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode, rootDetails); glm::vec3 location(rootDetails.x, rootDetails.y, rootDetails.z); location *= (float)TREE_SCALE; AABox serverBounds(location, rootDetails.s * TREE_SCALE); glm::vec3 center = serverBounds.getVertex(BOTTOM_RIGHT_NEAR) + ((serverBounds.getVertex(TOP_LEFT_FAR) - serverBounds.getVertex(BOTTOM_RIGHT_NEAR)) / 2.0f); const float VOXEL_NODE_SCALE = 1.00f; const float MODEL_NODE_SCALE = 0.99f; const float PARTICLE_NODE_SCALE = 0.98f; float scaleFactor = rootDetails.s * TREE_SCALE; // Scale by 0.92 - 1.00 depending on the scale of the node. This allows smaller nodes to scale in // a bit and not overlap larger nodes. scaleFactor *= 0.92 + (rootDetails.s * 0.08); // Scale different node types slightly differently because it's common for them to overlap. if (nodeType == NodeType::VoxelServer) { scaleFactor *= VOXEL_NODE_SCALE; } else if (nodeType == NodeType::ModelServer) { scaleFactor *= MODEL_NODE_SCALE; } else { scaleFactor *= PARTICLE_NODE_SCALE; } float red, green, blue; getColorForNodeType(nodeType, red, green, blue); drawNodeBorder(center, scaleFactor, red, green, blue); float distance; BoxFace face; bool inside = serverBounds.contains(mouseRayOrigin); bool colliding = serverBounds.findRayIntersection(mouseRayOrigin, mouseRayDirection, distance, face); // If the camera is inside a node it will be "selected" if you don't have your cursor over another node // that you aren't inside. if (colliding && (!selectedNode || (!inside && (distance < selectedDistance || selectedIsInside)))) { selectedNode = node.data(); selectedDistance = distance; selectedIsInside = inside; selectedCenter = center; selectedScale = scaleFactor; } } } } if (selectedNode) { glPushMatrix(); glTranslatef(selectedCenter.x, selectedCenter.y, selectedCenter.z); glScalef(selectedScale, selectedScale, selectedScale); NodeType_t selectedNodeType = selectedNode->getType(); float red, green, blue; getColorForNodeType(selectedNode->getType(), red, green, blue); glColor4f(red, green, blue, 0.2); glutSolidCube(1.0); glPopMatrix(); HifiSockAddr addr = selectedNode->getPublicSocket(); QString overlay = QString("%1:%2 %3ms") .arg(addr.getAddress().toString()) .arg(addr.getPort()) .arg(selectedNode->getPingMs()) .left(MAX_OVERLAY_TEXT_LENGTH); // Ideally we'd just use a QString, but I ran into weird blinking issues using // constData() directly, as if the data was being overwritten. strcpy(_overlayText, overlay.toLocal8Bit().constData()); } else { _overlayText[0] = '\0'; } }
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; } } }
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 AssignmentClient::readPendingDatagrams() { auto nodeList = DependencyManager::get<NodeList>(); 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) { qDebug() << "Received a PacketTypeCreateAssignment - attempting to unpack."; // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket); if (_currentAssignment) { qDebug() << "Received an assignment -" << *_currentAssignment; // switch our DomainHandler hostname and port to whoever sent us the assignment nodeList->getDomainHandler().setSockAddr(senderSockAddr, _assignmentServerHostname); nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID()); qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString(); // start the deployed assignment QThread* workerThread = new QThread; workerThread->setObjectName("ThreadedAssignment Worker"); connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run); // Once the ThreadedAssignment says it is finished - we ask it to deleteLater // This is a queued connection so that it is put into the event loop to be processed by the worker // thread when it is ready. connect(_currentAssignment.data(), &ThreadedAssignment::finished, _currentAssignment.data(), &ThreadedAssignment::deleteLater, Qt::QueuedConnection); // once it is deleted, we quit the worker thread connect(_currentAssignment.data(), &ThreadedAssignment::destroyed, workerThread, &QThread::quit); // have the worker thread remove itself once it is done connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); // once the worker thread says it is done, we consider the assignment completed connect(workerThread, &QThread::destroyed, this, &AssignmentClient::assignmentCompleted); _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.data(), &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 if (packetTypeForPacket(receivedPacket) == PacketTypeStopNode) { if (senderSockAddr.getAddress() == QHostAddress::LocalHost || senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) { qDebug() << "AssignmentClientMonitor at" << senderSockAddr << "requested stop via PacketTypeStopNode."; QCoreApplication::quit(); } else { qDebug() << "Got a stop packet from other than localhost."; } } else { // have the NodeList attempt to handle it nodeList->processNodeData(senderSockAddr, receivedPacket); } } } }
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)) { // only process this packet if we have a match on the packet version switch (packetTypeForPacket(incomingPacket)) { case PacketTypeTransmitterData: // V2 = IOS transmitter app application->getAvatar()->getTransmitter().processIncomingData(reinterpret_cast<unsigned char*>(incomingPacket.data()), incomingPacket.size()); break; case PacketTypeMixedAudio: QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); break; case PacketTypeParticleAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse(incomingPacket); application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket); break; case PacketTypeParticleData: case PacketTypeParticleErase: case PacketTypeVoxelData: case PacketTypeVoxelErase: case PacketTypeOctreeStats: case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _voxelProcessor.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; printf("got PacketType_VOXEL_DATA, sequence:%d flightTime:%d\n", 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->_voxelProcessor.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 PacketTypeDataServerGet: case PacketTypeDataServerPut: case PacketTypeDataServerSend: case PacketTypeDataServerConfirm: DataServerClient::processMessageFromDataServer(incomingPacket); break; default: nodeList->processNodeData(senderSockAddr, incomingPacket); break; } } } }
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)) { // only process this packet if we have a match on the packet version switch (packetTypeForPacket(incomingPacket)) { case PacketTypeMixedAudio: case PacketTypeSilentAudioFrame: QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToStream", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); break; case PacketTypeAudioStreamStats: QMetaObject::invokeMethod(&application->_audio, "parseAudioStreamStatsPacket", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); break; case PacketTypeParticleAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse(incomingPacket); application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket); break; case PacketTypeModelAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly ModelItem::handleAddModelResponse(incomingPacket); application->getModels()->getTree()->handleAddModelResponse(incomingPacket); break; case PacketTypeParticleData: case PacketTypeParticleErase: case PacketTypeModelData: case PacketTypeModelErase: 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 PacketType_VOXEL_DATA, 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 PacketTypeDomainOAuthRequest: { QDataStream readStream(incomingPacket); readStream.skipRawData(numBytesForPacketHeader(incomingPacket)); QUrl authorizationURL; readStream >> authorizationURL; QMetaObject::invokeMethod(&OAuthWebViewHandler::getInstance(), "displayWebviewForAuthorizationURL", Q_ARG(const QUrl&, authorizationURL)); break; } case 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)); if (glm::distance(Application::getInstance()->getAvatar()->getPosition(), position) < radius && !Application::getInstance()->getAudio()->getMuted()) { Application::getInstance()->getAudio()->toggleMute(); } break; } case PacketTypeVoxelEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_voxelEditSender.processNackPacket(incomingPacket); } break; case PacketTypeParticleEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_particleEditSender.processNackPacket(incomingPacket); } break; case PacketTypeModelEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_modelEditSender.processNackPacket(incomingPacket); } break; default: nodeList->processNodeData(senderSockAddr, incomingPacket); break; } } } }
void AssignmentClient::readPendingDatagrams() { NodeList* nodeList = NodeList::getInstance(); static unsigned char packetData[1500]; static qint64 receivedBytes = 0; static HifiSockAddr senderSockAddr; while (nodeList->getNodeSocket().hasPendingDatagrams()) { if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer())) && packetVersionMatch(packetData)) { if (_currentAssignment) { // have the threaded current assignment handle this datagram QMetaObject::invokeMethod(_currentAssignment, "processDatagram", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray((char*) packetData, receivedBytes)), Q_ARG(HifiSockAddr, senderSockAddr)); } else if (packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { if (_currentAssignment) { qDebug() << "Dropping received assignment since we are currently running one.\n"; } else { // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes); qDebug() << "Received an assignment -" << *_currentAssignment << "\n"; // switch our nodelist domain IP and port to whoever sent us the assignment if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { nodeList->setDomainSockAddr(senderSockAddr); nodeList->setOwnerUUID(_currentAssignment->getUUID()); qDebug("Destination IP for assignment is %s\n", nodeList->getDomainIP().toString().toStdString().c_str()); // 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); // Starts an event loop, and emits workerThread->started() workerThread->start(); } else { qDebug("Received a bad destination socket for assignment.\n"); } } } else { // have the NodeList attempt to handle it nodeList->processNodeData(senderSockAddr, packetData, receivedBytes); } } } }
AssignmentClient::AssignmentClient(int &argc, char **argv) : QCoreApplication(argc, argv), _currentAssignment(NULL) { // register meta type is required for queued invoke method on Assignment subclasses // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); const char ASSIGNMENT_TYPE_OVVERIDE_OPTION[] = "-t"; const char* assignmentTypeString = getCmdOption(argc, (const char**)argv, ASSIGNMENT_TYPE_OVVERIDE_OPTION); Assignment::Type requestAssignmentType = Assignment::AllTypes; if (assignmentTypeString) { // the user is asking to only be assigned to a particular type of assignment // so set that as the ::overridenAssignmentType to be used in requests requestAssignmentType = (Assignment::Type) atoi(assignmentTypeString); } const char ASSIGNMENT_POOL_OPTION[] = "--pool"; const char* requestAssignmentPool = getCmdOption(argc, (const char**) argv, ASSIGNMENT_POOL_OPTION); // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, requestAssignmentPool); // create a NodeList as an unassigned client NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned); const char CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION[] = "-a"; const char CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION[] = "-p"; // grab the overriden assignment-server hostname from argv, if it exists const char* customAssignmentServerHostname = getCmdOption(argc, (const char**)argv, CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION); const char* customAssignmentServerPortString = getCmdOption(argc,(const char**)argv, CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION); HifiSockAddr customAssignmentSocket; if (customAssignmentServerHostname || customAssignmentServerPortString) { // set the custom port or default if it wasn't passed unsigned short assignmentServerPort = customAssignmentServerPortString ? atoi(customAssignmentServerPortString) : DEFAULT_DOMAIN_SERVER_PORT; // set the custom hostname or default if it wasn't passed if (!customAssignmentServerHostname) { customAssignmentServerHostname = DEFAULT_ASSIGNMENT_SERVER_HOSTNAME; } customAssignmentSocket = HifiSockAddr(customAssignmentServerHostname, assignmentServerPort); } // set the custom assignment socket if we have it if (!customAssignmentSocket.isNull()) { nodeList->setAssignmentServerSocket(customAssignmentSocket); } // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required qDebug() << "Waiting for assignment -" << _requestAssignment; QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS); // connect our readPendingDatagrams method to the readyRead() signal of the socket connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams, Qt::QueuedConnection); }
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 IceServer::processDatagrams() { HifiSockAddr sendingSockAddr; QByteArray incomingPacket; while (_serverSocket.hasPendingDatagrams()) { incomingPacket.resize(_serverSocket.pendingDatagramSize()); _serverSocket.readDatagram(incomingPacket.data(), incomingPacket.size(), sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer()); if (packetTypeForPacket(incomingPacket) == PacketTypeIceServerHeartbeat) { QUuid senderUUID = uuidFromPacketHeader(incomingPacket); // pull the public and private sock addrs for this peer HifiSockAddr publicSocket, localSocket; QDataStream hearbeatStream(incomingPacket); hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket)); hearbeatStream >> publicSocket >> localSocket; // make sure we have this sender in our peer hash SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID); if (!matchingPeer) { // if we don't have this sender we need to create them now matchingPeer = SharedNetworkPeer(new NetworkPeer(senderUUID, publicSocket, localSocket)); _activePeers.insert(senderUUID, matchingPeer); qDebug() << "Added a new network peer" << *matchingPeer; } else { // we already had the peer so just potentially update their sockets matchingPeer->setPublicSocket(publicSocket); matchingPeer->setLocalSocket(localSocket); qDebug() << "Matched hearbeat to existing network peer" << *matchingPeer; } // update our last heard microstamp for this network peer to now matchingPeer->setLastHeardMicrostamp(usecTimestampNow()); // check if this node also included a UUID that they would like to connect to QUuid connectRequestID; hearbeatStream >> connectRequestID; // get the peers asking for connections with this peer QSet<QUuid>& requestingConnections = _currentConnections[senderUUID]; if (!connectRequestID.isNull()) { qDebug() << "Peer wants to connect to peer with ID" << uuidStringWithoutCurlyBraces(connectRequestID); // ensure this peer is in the set of current connections for the peer with ID it wants to connect with _currentConnections[connectRequestID].insert(senderUUID); // add the ID of the node they have said they would like to connect to requestingConnections.insert(connectRequestID); } if (requestingConnections.size() > 0) { // send a heartbeart response based on the set of connections qDebug() << "Sending a heartbeat response to" << senderUUID << "who has" << requestingConnections.size() << "potential connections"; sendHeartbeatResponse(sendingSockAddr, requestingConnections); } } }