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(); } }
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); } }
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."; } }
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."; } }
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."; } }
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); }
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."; } }
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); } }
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; }
QString Profile::getUserString() const { if (_uuid.isNull()) { return _username; } else { return uuidStringWithoutCurlyBraces(_uuid); } }
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(); }
// 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); }
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(); }
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; }
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); }
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); } }
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; }
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(); } }
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); }
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); }
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(); } }
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"); }
void DomainHandler::setUUID(const QUuid& uuid) { if (uuid != _uuid) { _uuid = uuid; qCDebug(networking) << "Domain ID changed to" << uuidStringWithoutCurlyBraces(_uuid); } }
void Node::activateLocalSocket() { qDebug() << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); _activeSocket = &_localSocket; }
void Node::activateSymmetricSocket() { qDebug() << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); _activeSocket = &_symmetricSocket; }
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); }
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; } }
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()); }
void DataServer::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; while (_socket.hasPendingDatagrams()) { receivedPacket.resize(_socket.pendingDatagramSize()); _socket.readDatagram(receivedPacket.data(), _socket.pendingDatagramSize(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); PacketType requestType = packetTypeForPacket(receivedPacket); if ((requestType == PacketTypeDataServerPut || requestType == PacketTypeDataServerGet) && receivedPacket[numBytesArithmeticCodingFromBuffer(receivedPacket.data())] == versionForPacketType(requestType)) { QDataStream packetStream(receivedPacket); int numReceivedHeaderBytes = numBytesForPacketHeader(receivedPacket); packetStream.skipRawData(numReceivedHeaderBytes); // pull the sequence number used for this packet quint8 sequenceNumber = 0; packetStream >> sequenceNumber; // pull the UUID that we will need as part of the key QString userString; packetStream >> userString; QUuid parsedUUID(userString); if (parsedUUID.isNull()) { // we failed to parse a UUID, this means the user has sent us a username // ask redis for the UUID for this user redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(userString)); if (reply->type == REDIS_REPLY_STRING) { parsedUUID = QUuid(QString(reply->str)); } if (!parsedUUID.isNull()) { qDebug() << "Found UUID" << parsedUUID << "for username" << userString; } else { qDebug() << "Failed UUID lookup for username" << userString; } freeReplyObject(reply); reply = NULL; } if (!parsedUUID.isNull()) { if (requestType == PacketTypeDataServerPut) { // pull the key and value that specifies the data the user is putting/getting QString dataKey, dataValue; packetStream >> dataKey >> dataValue; qDebug("Sending command to redis: SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey), qPrintable(dataValue)); redisReply* reply = (redisReply*) redisCommand(_redis, "SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey), qPrintable(dataValue)); if (reply->type == REDIS_REPLY_STATUS && strcmp("OK", reply->str) == 0) { // if redis stored the value successfully reply back with a confirm // which is a reply packet with the sequence number QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm, _uuid); replyPacket.append(sequenceNumber); _socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } freeReplyObject(reply); } else { // setup a send packet with the returned data // leverage the packetData sent by overwriting and appending QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend, _uuid); QDataStream sendPacketStream(&sendPacket, QIODevice::Append); sendPacketStream << sequenceNumber; // pull the key list that specifies the data the user is putting/getting QString keyListString; packetStream >> keyListString; if (keyListString != "uuid") { // copy the parsed UUID sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID); const char MULTI_KEY_VALUE_SEPARATOR = '|'; // append the keyListString back to the sendPacket sendPacketStream << keyListString; QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR); QStringList valueList; foreach (const QString& dataKey, keyList) { qDebug("Sending command to redis: GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); redisReply* reply = (redisReply*) redisCommand(_redis, "GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); if (reply->len) { // copy the value that redis returned valueList << QString(reply->str); } else { // didn't find a value - insert a space valueList << QChar(' '); } freeReplyObject(reply); } // append the value QStringList using the right separator sendPacketStream << valueList.join(MULTI_KEY_VALUE_SEPARATOR); } else {
void 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); }