void Agent::run() { NodeList::getInstance()->setOwnerType(NODE_TYPE_AGENT); NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1); QNetworkAccessManager manager; // 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(), this->getUUIDStringWithoutCurlyBraces()); QUrl scriptURL(scriptURLString); qDebug() << "Attemping download of " << scriptURL << "\n"; QNetworkReply* reply = manager.get(QNetworkRequest(scriptURL)); QEventLoop loop; QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); QString scriptString = QString(reply->readAll()); QScriptEngine engine; QScriptValue agentValue = engine.newQObject(this); engine.globalObject().setProperty("Agent", agentValue); VoxelScriptingInterface voxelScripter; QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter); engine.globalObject().setProperty("Voxels", voxelScripterValue); qDebug() << "Downloaded script:" << scriptString << "\n"; qDebug() << "Evaluated script:" << engine.evaluate(scriptString).toString() << "\n"; timeval thisSend; timeval lastDomainServerCheckIn = {}; int numMicrosecondsSleep = 0; const long long DATA_SEND_INTERVAL_USECS = (1 / 60.0f) * 1000 * 1000; sockaddr_in senderAddress; unsigned char receivedData[MAX_PACKET_SIZE]; ssize_t receivedBytes; while (!_shouldStop) { // update the thisSend timeval to the current time gettimeofday(&thisSend, NULL); // if we're not hearing from the domain-server we should stop running if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { break; } // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); NodeList::getInstance()->sendDomainServerCheckIn(); } // allow the scripter's call back to setup visual data emit preSendCallback(); // flush the voxel packet queue voxelScripter.getVoxelPacketSender()->process(); if (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) { NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); } // sleep for the correct amount of time to have data send be consistently timed if ((numMicrosecondsSleep = DATA_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) { usleep(numMicrosecondsSleep); } } }
void Agent::run() { NodeList* nodeList = NodeList::getInstance(); nodeList->setOwnerType(NODE_TYPE_AGENT); const char AGENT_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AUDIO_MIXER }; nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST)); nodeList->getNodeSocket()->setBlocking(false); // 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)); // setup curl for script download CURLcode curlResult; CURL* curlHandle = curl_easy_init(); // tell curl which file to grab curl_easy_setopt(curlHandle, CURLOPT_URL, scriptURLString.toStdString().c_str()); // send the data to the WriteMemoryCallback function curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, writeScriptDataToString); QString scriptContents; // pass the scriptContents QString to append data to curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *)&scriptContents); // send a user agent since some servers will require it curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); // make sure CURL fails on a 400 code curl_easy_setopt(curlHandle, CURLOPT_FAILONERROR, true); qDebug() << "Downloading script at" << scriptURLString << "\n"; // blocking get for JS file curlResult = curl_easy_perform(curlHandle); if (curlResult == CURLE_OK) { // cleanup curl curl_easy_cleanup(curlHandle); curl_global_cleanup(); QScriptEngine engine; // register meta-type for glm::vec3 conversions qScriptRegisterMetaType(&engine, vec3toScriptValue, vec3FromScriptValue); QScriptValue agentValue = engine.newQObject(this); engine.globalObject().setProperty("Agent", agentValue); VoxelScriptingInterface voxelScripter; QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter); engine.globalObject().setProperty("Voxels", voxelScripterValue); QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); // let the VoxelPacketSender know how frequently we plan to call it voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(INJECT_INTERVAL_USECS); // hook in a constructor for audio injectorss AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL); QScriptValue audioInjectorValue = engine.newQObject(&scriptedAudioInjector); engine.globalObject().setProperty("AudioInjector", audioInjectorValue); qDebug() << "Downloaded script:" << scriptContents << "\n"; QScriptValue result = engine.evaluate(scriptContents); qDebug() << "Evaluated script.\n"; if (engine.hasUncaughtException()) { int line = engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; } timeval startTime; gettimeofday(&startTime, NULL); timeval lastDomainServerCheckIn = {}; sockaddr_in senderAddress; unsigned char receivedData[MAX_PACKET_SIZE]; ssize_t receivedBytes; int thisFrame = 0; NodeList::getInstance()->startSilentNodeRemovalThread(); while (!_shouldStop) { // if we're not hearing from the domain-server we should stop running if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { break; } // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); NodeList::getInstance()->sendDomainServerCheckIn(); } // find the audio-mixer in the NodeList so we can inject audio at it Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); if (audioMixer && audioMixer->getActiveSocket()) { emit willSendAudioDataCallback(); } int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); } if (audioMixer && audioMixer->getActiveSocket() && scriptedAudioInjector.hasSamplesToInject()) { // we have an audio mixer and samples to inject, send those off scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getActiveSocket()); // clear out the audio injector so that it doesn't re-send what we just sent scriptedAudioInjector.clear(); } if (audioMixer && !audioMixer->getActiveSocket()) { // don't have an active socket for the audio-mixer, ping it now NodeList::getInstance()->pingPublicAndLocalSocketsForInactiveNode(audioMixer); } if (voxelScripter.getVoxelPacketSender()->voxelServersExist()) { // allow the scripter's call back to setup visual data emit willSendVisualDataCallback(); // release the queue of edit voxel messages. voxelScripter.getVoxelPacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent voxelScripter.getVoxelPacketSender()->process(); } if (engine.hasUncaughtException()) { int line = engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; } while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes) && packetVersionMatch(receivedData)) { if (receivedData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { voxelScripter.getJurisdictionListener()->queueReceivedPacket((sockaddr&) senderAddress, receivedData, receivedBytes); } else { NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); } } } NodeList::getInstance()->stopSilentNodeRemovalThread(); } else { // error in curl_easy_perform qDebug() << "curl_easy_perform for JS failed:" << curl_easy_strerror(curlResult) << "\n"; // cleanup curl curl_easy_cleanup(curlHandle); curl_global_cleanup(); } }