Example #1
0
void NodeList::pingPunchForDomainServer() {
    // make sure if we're here that we actually still need to ping the domain-server
    if (_domainHandler.getIP().isNull() && _domainHandler.getICEPeer().hasSockets()) {

        // check if we've hit the number of pings we'll send to the DS before we consider it a fail
        const int NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET = 2000 / UDP_PUNCH_PING_INTERVAL_MS;

        if (_domainHandler.getICEPeer().getConnectionAttempts() == 0) {
            qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID"
                << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
        } else {
            if (_domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) {
                // if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat
                qCDebug(networking) << "No ping replies received from domain-server with ID"
                    << uuidStringWithoutCurlyBraces(_domainHandler.getICEClientID()) << "-" << "re-sending ICE query.";

                _domainHandler.getICEPeer().softReset();
                handleICEConnectionToDomainServer();

                return;
            }
        }

        flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendPingsToDS);

        // send the ping packet to the local and public sockets for this node
        auto localPingPacket = constructICEPingPacket(PingType::Local, _sessionUUID);
        sendPacket(std::move(localPingPacket), _domainHandler.getICEPeer().getLocalSocket());

        auto publicPingPacket = constructICEPingPacket(PingType::Public, _sessionUUID);
        sendPacket(std::move(publicPingPacket), _domainHandler.getICEPeer().getPublicSocket());

        _domainHandler.getICEPeer().incrementConnectionAttempts();
    }
}
Example #2
0
void LimitedNodeList::setSessionUUID(const QUuid& sessionUUID) {
    QUuid oldUUID = _sessionUUID;
    _sessionUUID = sessionUUID;

    if (sessionUUID != oldUUID) {
        qCDebug(networking) << "NodeList UUID changed from" <<  uuidStringWithoutCurlyBraces(oldUUID)
                            << "to" << uuidStringWithoutCurlyBraces(_sessionUUID);
        emit uuidChanged(sessionUUID, oldUUID);
    }
}
Example #3
0
void Node::addIgnoredNode(const QUuid& otherNodeID) {
    if (!otherNodeID.isNull() && otherNodeID != _uuid) {
        qCDebug(networking) << "Adding" << uuidStringWithoutCurlyBraces(otherNodeID) << "to ignore set for"
        << uuidStringWithoutCurlyBraces(_uuid);

        // add the session UUID to the set of ignored ones for this listening node
        _ignoredNodeIDSet.insert(otherNodeID);
    } else {
        qCWarning(networking) << "Node::addIgnoredNode called with null ID or ID of ignoring node.";
    }
}
Example #4
0
void Node::removeIgnoredNode(const QUuid& otherNodeID) {
    if (!otherNodeID.isNull() && otherNodeID != _uuid) {
        // insert/find are read locked concurrently. unsafe_erase is not concurrent, and needs a write lock.
        QWriteLocker lock { &_ignoredNodeIDSetLock };
        qCDebug(networking) << "Removing" << uuidStringWithoutCurlyBraces(otherNodeID) << "from ignore set for"
        << uuidStringWithoutCurlyBraces(_uuid);

        // remove the session UUID from the set of ignored ones for this listening node
        _ignoredNodeIDSet.unsafe_erase(otherNodeID);
    } else {
        qCWarning(networking) << "Node::removeIgnoredNode called with null ID or ID of ignoring node.";
    }
}
Example #5
0
void Node::addIgnoredNode(const QUuid& otherNodeID) {
    if (!otherNodeID.isNull() && otherNodeID != _uuid) {
        QWriteLocker lock { &_ignoredNodeIDSetLock };
        qCDebug(networking) << "Adding" << uuidStringWithoutCurlyBraces(otherNodeID) << "to ignore set for"
            << uuidStringWithoutCurlyBraces(_uuid);

        // add the session UUID to the set of ignored ones for this listening node
        if (std::find(_ignoredNodeIDs.begin(), _ignoredNodeIDs.end(), otherNodeID) == _ignoredNodeIDs.end()) {
            _ignoredNodeIDs.push_back(otherNodeID);
        }
    } else {
        qCWarning(networking) << "Node::addIgnoredNode called with null ID or ID of ignoring node.";
    }
}
Example #6
0
void MessagesMixer::sendStatsPacket() {
    QJsonObject statsObject, messagesMixerObject;

    // add stats for each listerner
    DependencyManager::get<NodeList>()->eachNode([&](const SharedNodePointer& node) {
        QJsonObject clientStats;
        clientStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
        clientStats["outbound_kbps"] = node->getOutboundBandwidth();
        clientStats["inbound_kbps"] = node->getInboundBandwidth();
        messagesMixerObject[uuidStringWithoutCurlyBraces(node->getUUID())] = clientStats;
    });

    statsObject["messages"] = messagesMixerObject;
    ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
}
Example #7
0
void Node::removeIgnoredNode(const QUuid& otherNodeID) {
    if (!otherNodeID.isNull() && otherNodeID != _uuid) {
        QWriteLocker lock { &_ignoredNodeIDSetLock };
        qCDebug(networking) << "Removing" << uuidStringWithoutCurlyBraces(otherNodeID) << "from ignore set for"
            << uuidStringWithoutCurlyBraces(_uuid);

        // remove the session UUID from the set of ignored ones for this listening node, if it exists
        auto it = std::remove(_ignoredNodeIDs.begin(), _ignoredNodeIDs.end(), otherNodeID);
        if (it != _ignoredNodeIDs.end()) {
            _ignoredNodeIDs.erase(it);
        }
    } else {
        qCWarning(networking) << "Node::removeIgnoredNode called with null ID or ID of ignoring node.";
    }
}
Example #8
0
void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
    
    if (senderNode->getCanRez()) {
        qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID());
        
        auto task = new UploadAssetTask(packetList, senderNode, _resourcesDirectory);
        _taskPool.start(task);
    } else {
        // this is a node the domain told us is not allowed to rez entities
        // for now this also means it isn't allowed to add assets
        // so return a packet with error that indicates that
        
        auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError));
        
        MessageID messageID;
        packetList->readPrimitive(&messageID);
        
        // write the message ID and a permission denied error
        permissionErrorPacket->writePrimitive(messageID);
        permissionErrorPacket->writePrimitive(AssetServerError::PermissionDenied);
        
        // send off the packet
        auto nodeList = DependencyManager::get<NodeList>();
        nodeList->sendPacket(std::move(permissionErrorPacket), *senderNode);
    }
}
Example #9
0
QNetworkRequest createNetworkRequest() {

    QNetworkRequest request;

    QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
    requestURL.setPath(USER_ACTIVITY_URL);

    request.setUrl(requestURL);

    auto accountManager = DependencyManager::get<AccountManager>();

    if (accountManager->hasValidAccessToken()) {
        request.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
                             accountManager->getAccountInfo().getAccessToken().authorizationHeaderValue());
    }

    request.setRawHeader(METAVERSE_SESSION_ID_HEADER,
                         uuidStringWithoutCurlyBraces(accountManager->getSessionID()).toLocal8Bit());

    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

    request.setPriority(QNetworkRequest::HighPriority);

    return request;
}
Example #10
0
QString Profile::getUserString() const {
    if (_uuid.isNull()) {
        return _username;
    } else {
        return uuidStringWithoutCurlyBraces(_uuid);
    }
}
Example #11
0
void DomainServer::civetwebUploadHandler(struct mg_connection *connection, const char *path) {
    
    // create an assignment for this saved script, for now make it local only
    Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand,
                                                  Assignment::AgentType,
                                                  NULL,
                                                  Assignment::LocalLocation);
    
    // check how many instances of this assignment the user wants by checking the ASSIGNMENT-INSTANCES header
    const char ASSIGNMENT_INSTANCES_HTTP_HEADER[] = "ASSIGNMENT-INSTANCES";
    const char* requestInstancesHeader = mg_get_header(connection, ASSIGNMENT_INSTANCES_HTTP_HEADER);
    
    if (requestInstancesHeader) {
        // the user has requested a number of instances greater than 1
        // so set that on the created assignment
        scriptAssignment->setNumberOfInstances(atoi(requestInstancesHeader));
    }
    
    QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION);
    newPath += "/";
    // append the UUID for this script as the new filename, remove the curly braces
    newPath += uuidStringWithoutCurlyBraces(scriptAssignment->getUUID());
    
    // rename the saved script to the GUID of the assignment and move it to the script host locaiton
    rename(path, newPath.toLocal8Bit().constData());
    
    qDebug("Saved a script for assignment at %s\n", newPath.toLocal8Bit().constData());
    
    // add the script assigment to the assignment queue
    // lock the assignment queue mutex since we're operating on a different thread than DS main
    domainServerInstance->_assignmentQueueMutex.lock();
    domainServerInstance->_assignmentQueue.push_back(scriptAssignment);
    domainServerInstance->_assignmentQueueMutex.unlock();
}
Example #12
0
// FIXME - make these stats relevant
void MessagesMixer::sendStatsPacket() {
    QJsonObject statsObject;
    QJsonObject messagesObject;
    auto nodeList = DependencyManager::get<NodeList>();
    // add stats for each listerner
    nodeList->eachNode([&](const SharedNodePointer& node) {
        QJsonObject messagesStats;

        // add the key to ask the domain-server for a username replacement, if it has it
        messagesStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
        messagesStats["outbound_kbps"] = node->getOutboundBandwidth();
        messagesStats["inbound_kbps"] = node->getInboundBandwidth();

        messagesObject[uuidStringWithoutCurlyBraces(node->getUUID())] = messagesStats;
    });

    statsObject["messages"] = messagesObject;
    ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
}
Example #13
0
void Agent::run() {
    NodeList* nodeList = NodeList::getInstance();
    nodeList->setOwnerType(NodeType::Agent);
    
    nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer);
    
    // figure out the URL for the script for this agent assignment
    QString scriptURLString("http://%1:8080/assignment/%2");
    scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainIP().toString(),
                                          uuidStringWithoutCurlyBraces(_uuid));
    
    QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
    QNetworkReply *reply = networkManager->get(QNetworkRequest(QUrl(scriptURLString)));
    
    qDebug() << "Downloading script at" << scriptURLString;
    
    QEventLoop loop;
    QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
    
    loop.exec();
    
    QString scriptContents(reply->readAll());
    
    qDebug() << "Downloaded script:" << scriptContents;
    
    timeval startTime;
    gettimeofday(&startTime, NULL);
    
    QTimer* domainServerTimer = new QTimer(this);
    connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
    domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
    
    QTimer* silentNodeTimer = new QTimer(this);
    connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
    silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
    
    QTimer* pingNodesTimer = new QTimer(this);
    connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
    pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
    
    // tell our script engine about our local particle tree
    _scriptEngine.getParticlesScriptingInterface()->setParticleTree(&_particleTree);
    
    // setup an Avatar for the script to use
    AvatarData scriptedAvatar;
    
    // give this AvatarData object to the script engine
    _scriptEngine.setAvatarData(&scriptedAvatar, "Avatar");
    
    // register ourselves to the script engine
    _scriptEngine.registerGlobalObject("Agent", this);

    _scriptEngine.setScriptContents(scriptContents);
    _scriptEngine.run();    
}
Example #14
0
bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
    PacketType checkType = packetTypeForPacket(packet);
    int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data());

    if (packet[numPacketTypeBytes] != versionForPacketType(checkType)
            && checkType != PacketTypeStunResponse) {
        PacketType mismatchType = packetTypeForPacket(packet);

        static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;

        QUuid senderUUID = uuidFromPacketHeader(packet);
        if (!versionDebugSuppressMap.contains(senderUUID, checkType)) {
            qCDebug(networking) << "Packet version mismatch on" << packetTypeForPacket(packet) << "- Sender"
                                << uuidFromPacketHeader(packet) << "sent" << qPrintable(QString::number(packet[numPacketTypeBytes])) << "but"
                                << qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected.";

            emit packetVersionMismatch();

            versionDebugSuppressMap.insert(senderUUID, checkType);
        }

        return false;
    }

    if (!NON_VERIFIED_PACKETS.contains(checkType)) {
        // figure out which node this is from
        SharedNodePointer sendingNode = sendingNodeForPacket(packet);
        if (sendingNode) {
            // check if the md5 hash in the header matches the hash we would expect
            if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) {
                return true;
            } else {
                static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;

                QUuid senderUUID = uuidFromPacketHeader(packet);
                if (!hashDebugSuppressMap.contains(senderUUID, checkType)) {
                    qCDebug(networking) << "Packet hash mismatch on" << checkType << "- Sender"
                                        << uuidFromPacketHeader(packet);

                    hashDebugSuppressMap.insert(senderUUID, checkType);
                }
            }
        } else {
            static QString repeatedMessage
                = LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ received from unknown node with UUID");

            qCDebug(networking) << "Packet of type" << checkType << "received from unknown node with UUID"
                                << qPrintable(uuidStringWithoutCurlyBraces(uuidFromPacketHeader(packet)));
        }
    } else {
        return true;
    }

    return false;
}
Example #15
0
void AudioMixer::sendStatsPacket() {
    static QJsonObject statsObject;

    statsObject["useDynamicJitterBuffers"] = _streamSettings._dynamicJitterBuffers;
    statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100.0f;
    statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio;

    statsObject["avg_listeners_per_frame"] = (float) _sumListeners / (float) _numStatFrames;

    QJsonObject mixStats;
    mixStats["%_hrtf_mixes"] = percentageForMixStats(_hrtfRenders);
    mixStats["%_hrtf_silent_mixes"] = percentageForMixStats(_hrtfSilentRenders);
    mixStats["%_hrtf_struggle_mixes"] = percentageForMixStats(_hrtfStruggleRenders);
    mixStats["%_manual_stereo_mixes"] = percentageForMixStats(_manualStereoMixes);
    mixStats["%_manual_echo_mixes"] = percentageForMixStats(_manualEchoMixes);

    mixStats["total_mixes"] = _totalMixes;
    mixStats["avg_mixes_per_block"] = _totalMixes / _numStatFrames;

    statsObject["mix_stats"] = mixStats;

    _sumListeners = 0;
    _hrtfRenders = 0;
    _hrtfSilentRenders = 0;
    _hrtfStruggleRenders = 0;
    _manualStereoMixes = 0;
    _manualEchoMixes = 0;
    _totalMixes = 0;
    _numStatFrames = 0;

    // add stats for each listerner
    auto nodeList = DependencyManager::get<NodeList>();
    QJsonObject listenerStats;

    nodeList->eachNode([&](const SharedNodePointer& node) {
        AudioMixerClientData* clientData = static_cast<AudioMixerClientData*>(node->getLinkedData());
        if (clientData) {
            QJsonObject nodeStats;
            QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());

            nodeStats["outbound_kbps"] = node->getOutboundBandwidth();
            nodeStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidString;

            nodeStats["jitter"] = clientData->getAudioStreamStats();

            listenerStats[uuidString] = nodeStats;
        }
    });

    // add the listeners object to the root object
    statsObject["z_listeners"] = listenerStats;

    // send off the stats packets
    ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
}
Example #16
0
void AudioMixerClientData::setupCodecForReplicatedAgent(QSharedPointer<ReceivedMessage> message) {
    // hop past the sequence number that leads the packet
    message->seek(sizeof(quint16));

    // pull the codec string from the packet
    auto codecString = message->readString();

    if (codecString != _selectedCodecName) {
        qCDebug(audio) << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())
        << "-" << codecString;

        const std::pair<QString, CodecPluginPointer> codec = AudioMixer::negotiateCodec({ codecString });
        setupCodec(codec.second, codec.first);

        // seek back to the beginning of the message so other readers are in the right place
        message->seek(0);
    }
}
Example #17
0
bool LimitedNodeList::packetSourceAndHashMatch(const NLPacket& packet, SharedNodePointer& matchingNode) {
    
    if (NON_SOURCED_PACKETS.contains(packet.getType())) {
        return true;
    } else {
        
        // figure out which node this is from
        matchingNode = nodeWithUUID(packet.getSourceID());
        
        if (matchingNode) {
            if (!NON_VERIFIED_PACKETS.contains(packet.getType())) {
                // check if the md5 hash in the header matches the hash we would expect
                if (packet.getVerificationHash() != packet.payloadHashWithConnectionUUID(matchingNode->getConnectionSecret())) {
                    static QMultiMap<QUuid, PacketType::Value> hashDebugSuppressMap;
                    
                    const QUuid& senderID = packet.getSourceID();

                    if (!hashDebugSuppressMap.contains(senderID, packet.getType())) {
                        qCDebug(networking) << "Packet hash mismatch on" << packet.getType() << "- Sender" << senderID;

                        hashDebugSuppressMap.insert(senderID, packet.getType());
                    }

                    return false;
                }
            }
            
            return true;
            
        } else {
            static QString repeatedMessage
                = LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ \\([\\sa-zA-Z]+\\) received from unknown node with UUID");

            qCDebug(networking) << "Packet of type" << packet.getType() << "(" << qPrintable(nameForPacketType(packet.getType())) << ")"
                << "received from unknown node with UUID" << qPrintable(uuidStringWithoutCurlyBraces(packet.getSourceID()));
        }
    }

    return false;
}
Example #18
0
void NodeList::handleICEConnectionToDomainServer() {
    if (_domainHandler.getICEPeer().isNull()
        || _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {
        
        _domainHandler.getICEPeer().resetConnectionAttemps();
        
        LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(),
                                                  _domainHandler.getICEClientID(),
                                                  _domainHandler.getICEDomainID());
    } else {
        qDebug() << "Sending ping packets to establish connectivity with domain-server with ID"
            << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
        
        // send the ping packet to the local and public sockets for this nodfe
        QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID());
        writeUnverifiedDatagram(localPingPacket, _domainHandler.getICEPeer().getLocalSocket());
        
        QByteArray publicPingPacket = constructPingPacket(PingType::Public, false, _domainHandler.getICEClientID());
        writeUnverifiedDatagram(publicPingPacket, _domainHandler.getICEPeer().getPublicSocket());
        
        _domainHandler.getICEPeer().incrementConnectionAttempts();
    }
}
Example #19
0
void AssetServer::sendStatsPacket() {
    QJsonObject serverStats;
    
    auto stats = DependencyManager::get<NodeList>()->sampleStatsForAllConnections();
    
    for (const auto& stat : stats) {
        QJsonObject nodeStats;
        auto endTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(stat.second.endTime);
        QDateTime date = QDateTime::fromMSecsSinceEpoch(endTimeMs.count());
        
        static const float USEC_PER_SEC = 1000000.0f;
        static const float MEGABITS_PER_BYTE = 8.0f / 1000000.0f; // Bytes => Mbits
        float elapsed = (float)(stat.second.endTime - stat.second.startTime).count() / USEC_PER_SEC; // sec
        float megabitsPerSecPerByte = MEGABITS_PER_BYTE / elapsed; // Bytes => Mb/s
        
        QJsonObject connectionStats;
        connectionStats["1. Last Heard"] = date.toString();
        connectionStats["2. Est. Max (P/s)"] = stat.second.estimatedBandwith;
        connectionStats["3. RTT (ms)"] = stat.second.rtt;
        connectionStats["4. CW (P)"] = stat.second.congestionWindowSize;
        connectionStats["5. Period (us)"] = stat.second.packetSendPeriod;
        connectionStats["6. Up (Mb/s)"] = stat.second.sentBytes * megabitsPerSecPerByte;
        connectionStats["7. Down (Mb/s)"] = stat.second.receivedBytes * megabitsPerSecPerByte;
        nodeStats["Connection Stats"] = connectionStats;
        
        using Events = udt::ConnectionStats::Stats::Event;
        const auto& events = stat.second.events;
        
        QJsonObject upstreamStats;
        upstreamStats["1. Sent (P/s)"] = stat.second.sendRate;
        upstreamStats["2. Sent Packets"] = stat.second.sentPackets;
        upstreamStats["3. Recvd ACK"] = events[Events::ReceivedACK];
        upstreamStats["4. Procd ACK"] = events[Events::ProcessedACK];
        upstreamStats["5. Recvd LACK"] = events[Events::ReceivedLightACK];
        upstreamStats["6. Recvd NAK"] = events[Events::ReceivedNAK];
        upstreamStats["7. Recvd TNAK"] = events[Events::ReceivedTimeoutNAK];
        upstreamStats["8. Sent ACK2"] = events[Events::SentACK2];
        upstreamStats["9. Retransmitted"] = events[Events::Retransmission];
        nodeStats["Upstream Stats"] = upstreamStats;
        
        QJsonObject downstreamStats;
        downstreamStats["1. Recvd (P/s)"] = stat.second.receiveRate;
        downstreamStats["2. Recvd Packets"] = stat.second.receivedPackets;
        downstreamStats["3. Sent ACK"] = events[Events::SentACK];
        downstreamStats["4. Sent LACK"] = events[Events::SentLightACK];
        downstreamStats["5. Sent NAK"] = events[Events::SentNAK];
        downstreamStats["6. Sent TNAK"] = events[Events::SentTimeoutNAK];
        downstreamStats["7. Recvd ACK2"] = events[Events::ReceivedACK2];
        downstreamStats["8. Duplicates"] = events[Events::Duplicate];
        nodeStats["Downstream Stats"] = downstreamStats;
        
        QString uuid;
        auto nodelist = DependencyManager::get<NodeList>();
        if (stat.first == nodelist->getDomainHandler().getSockAddr()) {
            uuid = uuidStringWithoutCurlyBraces(nodelist->getDomainHandler().getUUID());
            nodeStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = "DomainServer";
        } else {
            auto node = nodelist->findNodeWithAddr(stat.first);
            uuid = uuidStringWithoutCurlyBraces(node ? node->getUUID() : QUuid());
            nodeStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuid;
        }
        
        serverStats[uuid] = nodeStats;
    }
    
    // send off the stats packets
    ThreadedAssignment::addPacketStatsAndSendStatsPacket(serverStats);
}
Example #20
0
void NodeList::processDomainServerRemovedNode(QSharedPointer<ReceivedMessage> message) {
    // read the UUID from the packet, remove it if it exists
    QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
    qDebug() << "Received packet from domain-server to remove node with UUID" << uuidStringWithoutCurlyBraces(nodeUUID);
    killNodeWithUUID(nodeUUID);
}
Example #21
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();
    }
}
Example #22
0
AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
                                   quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
                                   quint16 assignmentServerPort, quint16 assignmentMonitorPort) :
    _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME)
{
    LogUtils::init();

    QSettings::setDefaultFormat(QSettings::IniFormat);

    DependencyManager::set<AccountManager>();
 
    auto scriptableAvatar = DependencyManager::set<ScriptableAvatar>();
    auto addressManager = DependencyManager::set<AddressManager>();
    auto scriptEngines = DependencyManager::set<ScriptEngines>();

    // create a NodeList as an unassigned client, must be after addressManager
    auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);

    auto animationCache = DependencyManager::set<AnimationCache>();
    auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);

    DependencyManager::registerInheritance<EntityActionFactoryInterface, AssignmentActionFactory>();
    auto actionFactory = DependencyManager::set<AssignmentActionFactory>();
    DependencyManager::set<ResourceScriptingInterface>();

    // setup a thread for the NodeList and its PacketReceiver
    QThread* nodeThread = new QThread(this);
    nodeThread->setObjectName("NodeList Thread");
    nodeThread->start();

    // make sure the node thread is given highest priority
    nodeThread->setPriority(QThread::TimeCriticalPriority);

    // put the NodeList on the node thread
    nodeList->moveToThread(nodeThread);

    // 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().setShouldOutputProcessID(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()) {
        qCDebug(assigmnentclient) << "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);
    _assignmentServerSocket.setObjectName("AssigmentServer");
    nodeList->setAssignmentServerSocket(_assignmentServerSocket);

    qCDebug(assigmnentclient) << "Assignment server socket is" << _assignmentServerSocket;

    // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required
    qCDebug(assigmnentclient) << "Waiting for assignment -" << _requestAssignment;

    if (_assignmentServerHostname != "localhost") {
        qCDebug(assigmnentclient) << "- will attempt to connect to domain-server on" << _assignmentServerSocket.getPort();
    }

    connect(&_requestTimer, SIGNAL(timeout()), SLOT(sendAssignmentRequest()));
    _requestTimer.start(ASSIGNMENT_REQUEST_INTERVAL_MSECS);

    // connections to AccountManager for authentication
    connect(DependencyManager::get<AccountManager>().data(), &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);
        _assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor");

        qCDebug(assigmnentclient) << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;

        // Hook up a timer to send this child's status to the Monitor once per second
        setUpStatusToMonitor();
    }
    auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
    packetReceiver.registerListener(PacketType::CreateAssignment, this, "handleCreateAssignmentPacket");
    packetReceiver.registerListener(PacketType::StopNode, this, "handleStopNodePacket");
}
Example #23
0
void DomainHandler::setUUID(const QUuid& uuid) {
    if (uuid != _uuid) {
        _uuid = uuid;
        qCDebug(networking) << "Domain ID changed to" << uuidStringWithoutCurlyBraces(_uuid);
    }
}
Example #24
0
void Node::activateLocalSocket() {
    qDebug() << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
    _activeSocket = &_localSocket;
}
Example #25
0
void Node::activateSymmetricSocket() {
    qDebug() << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid);
    _activeSocket = &_symmetricSocket;
}
Example #26
0
void UploadAssetTask::run() {
    auto data = _receivedMessage->getMessage();
    
    QBuffer buffer { &data };
    buffer.open(QIODevice::ReadOnly);
    
    MessageID messageID;
    buffer.read(reinterpret_cast<char*>(&messageID), sizeof(messageID));
    
    uint64_t fileSize;
    buffer.read(reinterpret_cast<char*>(&fileSize), sizeof(fileSize));
    
    qDebug() << "UploadAssetTask reading a file of " << fileSize << "bytes from"
        << uuidStringWithoutCurlyBraces(_senderNode->getUUID());
    
    auto replyPacket = NLPacket::create(PacketType::AssetUploadReply);
    replyPacket->writePrimitive(messageID);
    
    if (fileSize > MAX_UPLOAD_SIZE) {
        replyPacket->writePrimitive(AssetServerError::AssetTooLarge);
    } else {
        QByteArray fileData = buffer.read(fileSize);
        
        auto hash = hashData(fileData);
        auto hexHash = hash.toHex();
        
        qDebug() << "Hash for uploaded file from" << uuidStringWithoutCurlyBraces(_senderNode->getUUID())
            << "is: (" << hexHash << ") ";
        
        QFile file { _resourcesDir.filePath(QString(hexHash)) };

        bool existingCorrectFile = false;
        
        if (file.exists()) {
            // check if the local file has the correct contents, otherwise we overwrite
            if (file.open(QIODevice::ReadOnly) && hashData(file.readAll()) == hash) {
                qDebug() << "Not overwriting existing verified file: " << hexHash;

                existingCorrectFile = true;

                replyPacket->writePrimitive(AssetServerError::NoError);
                replyPacket->write(hash);
            } else {
                qDebug() << "Overwriting an existing file whose contents did not match the expected hash: " << hexHash;
                file.close();
            }
        }

        if (!existingCorrectFile) {
            if (file.open(QIODevice::WriteOnly) && file.write(fileData) == qint64(fileSize)) {
                qDebug() << "Wrote file" << hexHash << "to disk. Upload complete";
                file.close();

                replyPacket->writePrimitive(AssetServerError::NoError);
                replyPacket->write(hash);
            } else {
                qWarning() << "Failed to upload or write to file" << hexHash << " - upload failed.";

                // upload has failed - remove the file and return an error
                auto removed = file.remove();

                if (!removed) {
                    qWarning() << "Removal of failed upload file" << hexHash << "failed.";
                }
                
                replyPacket->writePrimitive(AssetServerError::FileOperationFailed);
            }
        }


    }
    
    auto nodeList = DependencyManager::get<NodeList>();
    nodeList->sendPacket(std::move(replyPacket), *_senderNode);
}
Example #27
0
int DomainServer::civetwebRequestHandler(struct mg_connection *connection) {
    const struct mg_request_info* ri = mg_get_request_info(connection);
    
    const char RESPONSE_200[] = "HTTP/1.0 200 OK\r\n\r\n";
    const char RESPONSE_400[] = "HTTP/1.0 400 Bad Request\r\n\r\n";
    
    const char URI_ASSIGNMENT[] = "/assignment";
    const char URI_NODE[] = "/node";
    
    if (strcmp(ri->request_method, "GET") == 0) {
        if (strcmp(ri->uri, "/assignments.json") == 0) {
            // user is asking for json list of assignments
            
            // start with a 200 response
            mg_printf(connection, "%s", RESPONSE_200);
            
            // setup the JSON
            QJsonObject assignmentJSON;
            QJsonObject assignedNodesJSON;
            
            // enumerate the NodeList to find the assigned nodes
            NodeList* nodeList = NodeList::getInstance();
            
            for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
                if (node->getLinkedData()) {
                    // add the node using the UUID as the key
                    QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());
                    assignedNodesJSON[uuidString] = jsonObjectForNode(&(*node));
                }
            }
            
            assignmentJSON["fulfilled"] = assignedNodesJSON;
            
            QJsonObject queuedAssignmentsJSON;
            
            // add the queued but unfilled assignments to the json
            std::deque<Assignment*>::iterator assignment = domainServerInstance->_assignmentQueue.begin();
            
            while (assignment != domainServerInstance->_assignmentQueue.end()) {
                QJsonObject queuedAssignmentJSON;
                
                QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID());
                queuedAssignmentJSON[JSON_KEY_TYPE] = QString((*assignment)->getTypeName());
                
                // if the assignment has a pool, add it
                if ((*assignment)->hasPool()) {
                    queuedAssignmentJSON[JSON_KEY_POOL] = QString((*assignment)->getPool());
                }
                
                // add this queued assignment to the JSON
                queuedAssignmentsJSON[uuidString] = queuedAssignmentJSON;
                
                // push forward the iterator to check the next assignment
                assignment++;
            }
            
            assignmentJSON["queued"] = queuedAssignmentsJSON;
            
            // print out the created JSON
            QJsonDocument assignmentDocument(assignmentJSON);
            mg_printf(connection, "%s", assignmentDocument.toJson().constData());
            
            // we've processed this request
            return 1;
        } else if (strcmp(ri->uri, "/nodes.json") == 0) {
            // start with a 200 response
            mg_printf(connection, "%s", RESPONSE_200);
            
            // setup the JSON
            QJsonObject rootJSON;
            QJsonObject nodesJSON;
            
            // enumerate the NodeList to find the assigned nodes
            NodeList* nodeList = NodeList::getInstance();
            
            for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
                // add the node using the UUID as the key
                QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());
                nodesJSON[uuidString] = jsonObjectForNode(&(*node));
            }
            
            rootJSON["nodes"] = nodesJSON;
            
            // print out the created JSON
            QJsonDocument nodesDocument(rootJSON);
            mg_printf(connection, "%s", nodesDocument.toJson().constData());
            
            // we've processed this request
            return 1;
        }
        
        // not processed, pass to document root
        return 0;
    } else if (strcmp(ri->request_method, "POST") == 0) {
        if (strcmp(ri->uri, URI_ASSIGNMENT) == 0) {
            // return a 200
            mg_printf(connection, "%s", RESPONSE_200);
            // upload the file
            mg_upload(connection, "/tmp");
            
            return 1;
        }
        
        return 0;
    } else if (strcmp(ri->request_method, "DELETE") == 0) {
        // this is a DELETE request
        
        // check if it is for an assignment
        if (memcmp(ri->uri, URI_NODE, strlen(URI_NODE)) == 0) {
            // pull the UUID from the url
            QUuid deleteUUID = QUuid(QString(ri->uri + strlen(URI_NODE) + sizeof('/')));
            
            if (!deleteUUID.isNull()) {
                Node *nodeToKill = NodeList::getInstance()->nodeWithUUID(deleteUUID);
                
                if (nodeToKill) {
                    // start with a 200 response
                    mg_printf(connection, "%s", RESPONSE_200);
                    
                    // we have a valid UUID and node - kill the node that has this assignment
                    NodeList::getInstance()->killNode(nodeToKill);
                    
                    // successfully processed request
                    return 1;
                }
            }
        }
        
        // request not processed - bad request
        mg_printf(connection, "%s", RESPONSE_400);
        
        // this was processed by civetweb
        return 1;
    } else {
        // have mongoose process this request from the document_root
        return 0;
    }
}
Example #28
0
void DiscoverabilityManager::updateLocation() {
    auto accountManager = DependencyManager::get<AccountManager>();
    auto addressManager = DependencyManager::get<AddressManager>();
    auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();


    if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
        // construct a QJsonObject given the user's current address information
        QJsonObject rootObject;

        QJsonObject locationObject;

        QString pathString = addressManager->currentPath();

        const QString PATH_KEY_IN_LOCATION = "path";
        locationObject.insert(PATH_KEY_IN_LOCATION, pathString);

        const QString CONNECTED_KEY_IN_LOCATION = "connected";
        locationObject.insert(CONNECTED_KEY_IN_LOCATION, domainHandler.isConnected());

        if (!addressManager->getRootPlaceID().isNull()) {
            const QString PLACE_ID_KEY_IN_LOCATION = "place_id";
            locationObject.insert(PLACE_ID_KEY_IN_LOCATION,
                                  uuidStringWithoutCurlyBraces(addressManager->getRootPlaceID()));
        }

        if (!domainHandler.getUUID().isNull()) {
            const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
            locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION,
                                  uuidStringWithoutCurlyBraces(domainHandler.getUUID()));
        }

        // in case the place/domain isn't in the database, we send the network address and port
        auto& domainSockAddr = domainHandler.getSockAddr();
        const QString NETWORK_ADRESS_KEY_IN_LOCATION = "network_address";
        locationObject.insert(NETWORK_ADRESS_KEY_IN_LOCATION, domainSockAddr.getAddress().toString());

        const QString NETWORK_ADDRESS_PORT_IN_LOCATION = "network_port";
        locationObject.insert(NETWORK_ADDRESS_PORT_IN_LOCATION, domainSockAddr.getPort());

        const QString FRIENDS_ONLY_KEY_IN_LOCATION = "friends_only";
        locationObject.insert(FRIENDS_ONLY_KEY_IN_LOCATION, (_mode.get() == Discoverability::Friends));

        JSONCallbackParameters callbackParameters;
        callbackParameters.jsonCallbackReceiver = this;
        callbackParameters.jsonCallbackMethod = "handleHeartbeatResponse";

        // figure out if we'll send a fresh location or just a simple heartbeat
        auto apiPath = API_USER_HEARTBEAT_PATH;

        if (locationObject != _lastLocationObject) {
            // we have a changed location, send it now
            _lastLocationObject = locationObject;

            const QString LOCATION_KEY_IN_ROOT = "location";
            rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);

            apiPath = API_USER_LOCATION_PATH;
        }

        accountManager->sendRequest(apiPath, AccountManagerAuth::Required,
                                   QNetworkAccessManager::PutOperation,
                                   callbackParameters, QJsonDocument(rootObject).toJson());

    } else if (UserActivityLogger::getInstance().isEnabled()) {
        // we still send a heartbeat to the metaverse server for stats collection

        JSONCallbackParameters callbackParameters;
        callbackParameters.jsonCallbackReceiver = this;
        callbackParameters.jsonCallbackMethod = "handleHeartbeatResponse";

        accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional,
                                   QNetworkAccessManager::PutOperation, callbackParameters);
    }

    // Update Steam
    SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentFacingShareableAddress());
}
Example #29
0
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 {
Example #30
0
void Agent::run() {
    ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
    
    NodeList* nodeList = NodeList::getInstance();
    nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet()
                                                 << NodeType::AudioMixer
                                                 << NodeType::AvatarMixer
                                                 << NodeType::VoxelServer
                                                 << NodeType::ParticleServer
                                                 << NodeType::ModelServer
                                                );
    
    // figure out the URL for the script for this agent assignment
    QUrl scriptURL;
    if (_payload.isEmpty())  {
        scriptURL = QUrl(QString("http://%1:%2/assignment/%3")
            .arg(NodeList::getInstance()->getDomainHandler().getIP().toString())
            .arg(DOMAIN_SERVER_HTTP_PORT)
            .arg(uuidStringWithoutCurlyBraces(_uuid)));
    } else {
        scriptURL = QUrl(_payload);
    }
   
    NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
    QNetworkReply *reply = networkAccessManager.get(QNetworkRequest(scriptURL));
    
    QNetworkDiskCache* cache = new QNetworkDiskCache();
    QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
    cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache");
    networkAccessManager.setCache(cache);
    
    qDebug() << "Downloading script at" << scriptURL.toString();
    
    QEventLoop loop;
    QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
    
    loop.exec();
    
    QString scriptContents(reply->readAll());
    
    qDebug() << "Downloaded script:" << scriptContents;
    
    // setup an Avatar for the script to use
    ScriptableAvatar scriptedAvatar(&_scriptEngine);
    scriptedAvatar.setForceFaceshiftConnected(true);

    // call model URL setters with empty URLs so our avatar, if user, will have the default models
    scriptedAvatar.setFaceModelURL(QUrl());
    scriptedAvatar.setSkeletonModelURL(QUrl());
    
    // give this AvatarData object to the script engine
    _scriptEngine.setAvatarData(&scriptedAvatar, "Avatar");
    _scriptEngine.setAvatarHashMap(&_avatarHashMap, "AvatarList");
    
    // register ourselves to the script engine
    _scriptEngine.registerGlobalObject("Agent", this);

    _scriptEngine.init(); // must be done before we set up the viewers

    _scriptEngine.registerGlobalObject("VoxelViewer", &_voxelViewer);
    // connect the VoxelViewer and the VoxelScriptingInterface to each other
    JurisdictionListener* voxelJL = _scriptEngine.getVoxelsScriptingInterface()->getJurisdictionListener();
    _voxelViewer.setJurisdictionListener(voxelJL);
    _voxelViewer.init();
    _scriptEngine.getVoxelsScriptingInterface()->setVoxelTree(_voxelViewer.getTree());
    
    _scriptEngine.registerGlobalObject("ParticleViewer", &_particleViewer);
    JurisdictionListener* particleJL = _scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener();
    _particleViewer.setJurisdictionListener(particleJL);
    _particleViewer.init();
    _scriptEngine.getParticlesScriptingInterface()->setParticleTree(_particleViewer.getTree());

    _scriptEngine.registerGlobalObject("ModelViewer", &_modelViewer);
    JurisdictionListener* modelJL = _scriptEngine.getModelsScriptingInterface()->getJurisdictionListener();
    _modelViewer.setJurisdictionListener(modelJL);
    _modelViewer.init();
    _scriptEngine.getModelsScriptingInterface()->setModelTree(_modelViewer.getTree());

    _scriptEngine.setScriptContents(scriptContents);
    _scriptEngine.run();
    setFinished(true);
}