Ejemplo n.º 1
0
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;
    }
}
Ejemplo n.º 2
0
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();
        }
    }
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
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();
}
Ejemplo n.º 6
0
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;
            }
        }
    }
Ejemplo n.º 7
0
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();
    }
}
Ejemplo n.º 8
0
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);
            }
        }
    }
}