diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index f19f4ff86b..5ec4b91fa6 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -116,6 +117,11 @@ void Agent::handleAudioPacket(QSharedPointer packet) { const QString AGENT_LOGGING_NAME = "agent"; void Agent::run() { + + // make sure we request our script once the agent connects to the domain + auto nodeList = DependencyManager::get(); + connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript); + ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); // Setup MessagesClient @@ -125,72 +131,86 @@ void Agent::run() { messagesClient->moveToThread(messagesThread); connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); messagesThread->start(); + + nodeList->addSetOfNodeTypesToNodeInterestSet({ + NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer + }); +} - +void Agent::requestScript() { auto nodeList = DependencyManager::get(); - nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() - << NodeType::AudioMixer - << NodeType::AvatarMixer - << NodeType::EntityServer - << NodeType::MessagesMixer - ); - + disconnect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript); + // 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(DependencyManager::get()->getDomainHandler().getIP().toString()) - .arg(DOMAIN_SERVER_HTTP_PORT) - .arg(uuidStringWithoutCurlyBraces(_uuid))); + scriptURL = QUrl(QString("http://%1:%2/assignment/%3/") + .arg(nodeList->getDomainHandler().getIP().toString()) + .arg(DOMAIN_SERVER_HTTP_PORT) + .arg(uuidStringWithoutCurlyBraces(nodeList->getSessionUUID()))); } else { scriptURL = QUrl(_payload); } - + + // setup a network access manager and QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest = QNetworkRequest(scriptURL); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - QNetworkReply* reply = networkAccessManager.get(networkRequest); - + QNetworkDiskCache* cache = new QNetworkDiskCache(); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache"); networkAccessManager.setCache(cache); - + + QNetworkRequest networkRequest = QNetworkRequest(scriptURL); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + qDebug() << "Downloading script at" << scriptURL.toString(); + QNetworkReply* reply = networkAccessManager.get(networkRequest); + connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished); +} - QEventLoop loop; - QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); +void Agent::scriptRequestFinished() { + auto reply = qobject_cast(sender()); + + if (reply->error() != QNetworkReply::NoError) { + _scriptContents = reply->readAll(); + qDebug() << "Downloaded script:" << _scriptContents; + + // we could just call executeScript directly - we use a QueuedConnect to allow scriptRequestFinished + // to return before calling executeScript + QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection); + } else { + qDebug() << "Failed to download script at" << reply->url().toString() << " - bailing on assignment."; + qDebug() << "QNetworkReply error was" << reply->errorString(); + setFinished(true); + } + + reply->deleteLater(); +} - loop.exec(); - - QString scriptContents(reply->readAll()); - delete reply; - - qDebug() << "Downloaded script:" << scriptContents; - - _scriptEngine = std::unique_ptr(new ScriptEngine(scriptContents, _payload)); +void Agent::executeScript() { + _scriptEngine = std::unique_ptr(new ScriptEngine(_scriptContents, _payload)); _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do - + // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection); scriptedAvatar->setForceFaceTrackerConnected(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->registerGlobalObject("Avatar", scriptedAvatar.data()); - - + + using namespace recording; static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME); - // FIXME how to deal with driving multiple avatars locally? + // FIXME how to deal with driving multiple avatars locally? Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) { AvatarData::fromFrame(frame->data, *scriptedAvatar); }); - - + + using namespace recording; static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::AUDIO_FRAME_NAME); Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) { @@ -201,32 +221,30 @@ void Agent::run() { audioTransform.setRotation(scriptedAvatar->getOrientation()); AbstractAudioInterface::emitAudioPacket(audio.data(), audio.size(), audioSequenceNumber, audioTransform, PacketType::MicrophoneAudioNoEcho); }); - - - + auto avatarHashMap = DependencyManager::set(); _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); - + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::AvatarBillboard, avatarHashMap.data(), "processAvatarBillboardPacket"); - + // register ourselves to the script engine _scriptEngine->registerGlobalObject("Agent", this); - - // FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why + + // FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why // viewers would need this called. //_scriptEngine->init(); // must be done before we set up the viewers - + _scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); - + QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor); _scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); - + auto entityScriptingInterface = DependencyManager::get(); - + _scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); // we need to make sure that init has been called for our EntityScriptingInterface @@ -237,15 +255,15 @@ void Agent::run() { _entityViewer.init(); entityScriptingInterface->setEntityTree(_entityViewer.getTree()); - + // wire up our additional agent related processing to the update signal QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio); - + _scriptEngine->run(); - + Frame::clearFrameHandler(AUDIO_FRAME_TYPE); Frame::clearFrameHandler(AVATAR_FRAME_TYPE); - + setFinished(true); } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index d643b65267..205d4d4a80 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -55,6 +55,10 @@ public slots: void playAvatarSound(Sound* avatarSound) { setAvatarSound(avatarSound); } private slots: + void requestScript(); + void scriptRequestFinished(); + void executeScript(); + void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); @@ -73,6 +77,7 @@ private: void sendAvatarIdentityPacket(); void sendAvatarBillboardPacket(); + QString _scriptContents; bool _isListeningToAudioStream = false; Sound* _avatarSound = nullptr; int _numAvatarSoundSentBytes = 0; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b4243ef8a0..ddc0003eaa 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1097,29 +1097,37 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url if (connection->requestOperation() == QNetworkAccessManager::GetOperation && assignmentRegex.indexIn(url.path()) != -1) { - QUuid matchingUUID = QUuid(assignmentRegex.cap(1)); - - SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID); - if (!matchingAssignment) { - // check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment - QUuid assignmentUUID = _gatekeeper.assignmentUUIDForPendingAssignment(matchingUUID); - if (!assignmentUUID.isNull()) { - matchingAssignment = _allAssignments.value(assignmentUUID); - - if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) { - // we have a matching assignment and it is for the right type, have the HTTP manager handle it - // via correct URL for the script so the client can download - - QUrl scriptURL = url; - scriptURL.setPath(URI_ASSIGNMENT + "/scripts/" - + uuidStringWithoutCurlyBraces(assignmentUUID)); - - // have the HTTPManager serve the appropriate script file - return _httpManager.handleHTTPRequest(connection, scriptURL, true); - } - } + QUuid nodeUUID = QUuid(assignmentRegex.cap(1)); + + auto matchingNode = nodeList->nodeWithUUID(nodeUUID); + + // don't handle if we don't have a matching node + if (!matchingNode) { + return false; } - + + auto nodeData = dynamic_cast(matchingNode->getLinkedData()); + + // don't handle if we don't have node data for this node + if (!nodeData) { + return false; + } + + SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID()); + + // check if we have an assignment that matches this temp UUID, and it is a scripted assignment + if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) { + // we have a matching assignment and it is for the right type, have the HTTP manager handle it + // via correct URL for the script so the client can download + + QUrl scriptURL = url; + scriptURL.setPath(URI_ASSIGNMENT + "/scripts/" + + uuidStringWithoutCurlyBraces(matchingAssignment->getUUID())); + + // have the HTTPManager serve the appropriate script file + return _httpManager.handleHTTPRequest(connection, scriptURL, true); + } + // request not handled return false; }