bool ThreadedAssignment::readAvailableDatagram(QByteArray& destinationByteArray, HifiSockAddr& senderSockAddr) { auto nodeList = DependencyManager::get<NodeList>(); 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; } }
void ThreadedAssignment::setFinished(bool isFinished) { if (_isFinished != isFinished) { _isFinished = isFinished; if (_isFinished) { qDebug() << "ThreadedAssignment::setFinished(true) called - finishing up."; if (_domainServerTimer) { _domainServerTimer->stop(); } if (_statsTimer) { _statsTimer->stop(); } // stop processing datagrams from the node socket // this ensures we won't process a domain list while we are going down auto nodeList = DependencyManager::get<NodeList>(); disconnect(&nodeList->getNodeSocket(), 0, this, 0); // call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup aboutToFinish(); // if we have a datagram processing thread, quit it and wait on it to make sure that // the node socket is back on the same thread as the NodeList if (_datagramProcessingThread) { // tell the datagram processing thread to quit and wait until it is done, // then return the node socket to the NodeList _datagramProcessingThread->quit(); _datagramProcessingThread->wait(); // set node socket parent back to NodeList nodeList->getNodeSocket().setParent(nodeList.data()); } // move the NodeList back to the QCoreApplication instance's thread nodeList->moveToThread(QCoreApplication::instance()->thread()); emit finished(); } } }
qint64 LimitedNodeList::readDatagram(QByteArray& incomingPacket, QHostAddress* address = 0, quint16 * port = 0) { qint64 result = getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(), address, port); SharedNodePointer sendingNode = sendingNodeForPacket(incomingPacket); if (sendingNode) { emit dataReceived(sendingNode->getType(), incomingPacket.size()); } else { emit dataReceived(NodeType::Unassigned, incomingPacket.size()); } return result; }
AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks, const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType, QString assignmentPool, QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort) : _numAssignmentClientForks(numAssignmentClientForks), _minAssignmentClientForks(minAssignmentClientForks), _maxAssignmentClientForks(maxAssignmentClientForks), _requestAssignmentType(requestAssignmentType), _assignmentPool(assignmentPool), _walletUUID(walletUUID), _assignmentServerHostname(assignmentServerHostname), _assignmentServerPort(assignmentServerPort) { qDebug() << "_requestAssignmentType =" << _requestAssignmentType; // start the Logging class with the parent's target name LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME); // make sure we output process IDs for a monitor otherwise it's insane to parse LogHandler::getInstance().setShouldOutputPID(true); // create a NodeList so we can receive stats from children DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); auto addressManager = DependencyManager::set<AddressManager>(); auto nodeList = DependencyManager::set<LimitedNodeList>(); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClientMonitor::readPendingDatagrams); // use QProcess to fork off a process for each of the child assignment clients for (unsigned int i = 0; i < _numAssignmentClientForks; i++) { spawnChildClient(); } connect(&_checkSparesTimer, &QTimer::timeout, this, &AssignmentClientMonitor::checkSpares); _checkSparesTimer.start(NODE_SILENCE_THRESHOLD_MSECS * 3); }
void AssignmentClient::assignmentCompleted() { // we expect that to be here the previous assignment has completely cleaned up assert(_currentAssignment.isNull()); // reset our current assignment pointer to NULL now that it has been deleted _currentAssignment = NULL; // reset the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); qDebug() << "Assignment finished or never started - waiting for new assignment."; auto nodeList = DependencyManager::get<NodeList>(); // have us handle incoming NodeList datagrams again, and make sure our ThreadedAssignment isn't handling them connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); // reset our NodeList by switching back to unassigned and clearing the list nodeList->setOwnerType(NodeType::Unassigned); nodeList->reset(); nodeList->resetNodeInterestSet(); }
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; } } }
AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool, QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort, quint16 assignmentMonitorPort) : _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME) { LogUtils::init(); QSettings::setDefaultFormat(QSettings::IniFormat); // create a NodeList as an unassigned client DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); auto addressManager = DependencyManager::set<AddressManager>(); auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned); // Order is important auto animationCache = DependencyManager::set<AnimationCache>(); auto avatarHashMap = DependencyManager::set<AvatarHashMap>(); auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(); // make up a uuid for this child so the parent can tell us apart. This id will be changed // when the domain server hands over an assignment. QUuid nodeUUID = QUuid::createUuid(); nodeList->setSessionUUID(nodeUUID); // set the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); // make sure we output process IDs for a child AC otherwise it's insane to parse LogHandler::getInstance().setShouldOutputPID(true); // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); // check for a wallet UUID on the command line or in the config // this would represent where the user running AC wants funds sent to if (!walletUUID.isNull()) { qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } // check for an overriden assignment server hostname if (assignmentServerHostname != "") { // change the hostname for our assignment server _assignmentServerHostname = assignmentServerHostname; } _assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true); nodeList->setAssignmentServerSocket(_assignmentServerSocket); qDebug() << "Assignment server socket is" << _assignmentServerSocket; // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required qDebug() << "Waiting for assignment -" << _requestAssignment; if (_assignmentServerHostname != "localhost") { qDebug () << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort(); } connect(&_requestTimer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); _requestTimer.start(ASSIGNMENT_REQUEST_INTERVAL_MSECS); // connect our readPendingDatagrams method to the readyRead() signal of the socket connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); // connections to AccountManager for authentication connect(&AccountManager::getInstance(), &AccountManager::authRequired, this, &AssignmentClient::handleAuthenticationRequest); // Create Singleton objects on main thread NetworkAccessManager::getInstance(); // did we get an assignment-client monitor port? if (assignmentMonitorPort > 0) { _assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort); qDebug() << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket; // Hook up a timer to send this child's status to the Monitor once per second setUpStatsToMonitor(); } }
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); } } } }