diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 5bc4829117..464728f7e0 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -30,6 +30,7 @@ link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particle-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(script-engine ${TARGET_NAME} ${ROOT_DIR}) #testing include_directories(${ROOT_DIR}/externals/civetweb/include) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 8f02ed9a2e..e8bffc8eb2 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -24,8 +24,6 @@ Agent::Agent(const unsigned char* dataBuffer, int numBytes) : ThreadedAssignment(dataBuffer, numBytes) { - _particleScriptingInterface.init(); - _voxelScriptingInterface.init(); } void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { @@ -34,12 +32,12 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& // PACKET_TYPE_JURISDICTION, first byte is the node type... switch (dataByteArray[headerBytes]) { case NODE_TYPE_VOXEL_SERVER: - _voxelScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr, + _scriptEngine.getVoxelScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); break; case NODE_TYPE_PARTICLE_SERVER: - _particleScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr, + _scriptEngine.getParticleScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); break; @@ -76,43 +74,12 @@ void Agent::run() { loop.exec(); QString scriptContents(reply->readAll()); - QScriptEngine engine; - - // register meta-type for glm::vec3 conversions - registerMetaTypes(&engine); - - QScriptValue agentValue = engine.newQObject(this); - engine.globalObject().setProperty("Agent", agentValue); - - QScriptValue voxelScripterValue = engine.newQObject(&_voxelScriptingInterface); - engine.globalObject().setProperty("Voxels", voxelScripterValue); - - QScriptValue particleScripterValue = engine.newQObject(&_particleScriptingInterface); - engine.globalObject().setProperty("Particles", particleScripterValue); - - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); - - const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; - - // let the VoxelPacketSender know how frequently we plan to call it - _voxelScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); - _particleScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); 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); - int thisFrame = 0; - QTimer* domainServerTimer = new QTimer(this); connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); @@ -124,50 +91,7 @@ void Agent::run() { QTimer* pingNodesTimer = new QTimer(this); connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - - while (!_isFinished) { - - int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow(); - if (usecToSleep > 0) { - usleep(usecToSleep); - } - - QCoreApplication::processEvents(); - - bool willSendVisualDataCallBack = false; - - if (_voxelScriptingInterface.getVoxelPacketSender()->voxelServersExist()) { - // allow the scripter's call back to setup visual data - willSendVisualDataCallBack = true; - - // release the queue of edit voxel messages. - _voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); - - // since we're in non-threaded mode, call process so that the packets are sent - _voxelScriptingInterface.getVoxelPacketSender()->process(); - } - if (_particleScriptingInterface.getParticlePacketSender()->serversExist()) { - // allow the scripter's call back to setup visual data - willSendVisualDataCallBack = true; - - // release the queue of edit voxel messages. - _particleScriptingInterface.getParticlePacketSender()->releaseQueuedMessages(); - - // since we're in non-threaded mode, call process so that the packets are sent - _particleScriptingInterface.getParticlePacketSender()->process(); - } - - if (willSendVisualDataCallBack) { - emit willSendVisualDataCallback(); - } - - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; - } - - - } + _scriptEngine.setScriptContents(scriptContents); + _scriptEngine.run(); } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 1c2be08662..0bcd1af78e 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -33,8 +34,7 @@ signals: void willSendAudioDataCallback(); void willSendVisualDataCallback(); private: - VoxelScriptingInterface _voxelScriptingInterface; - ParticleScriptingInterface _particleScriptingInterface; + ScriptEngine _scriptEngine; }; #endif /* defined(__hifi__Agent__) */ diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c447bfa1a5..9d56764ffa 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -96,7 +96,7 @@ link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR}) -link_hifi_library(scriptengine ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(script-engine ${TARGET_NAME} ${ROOT_DIR}) # find required libraries find_package(Faceshift) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d81e22d818..20afe38e67 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -689,10 +689,10 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, const QString actionName, - const QKeySequence& shortcut, + const QKEYSEQUENCE& shortcut, const QObject* receiver, const char* member, - QAction::MenuRole role) { + QACTION_MENUROLE role) { QAction* action; if (receiver && member) { @@ -701,7 +701,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, action = destinationMenu->addAction(actionName); action->setShortcut(shortcut); } - action->setMenuRole(role); + action->setMenuRole((QAction::MenuRole)role); _actionHash.insert(actionName, action); @@ -714,7 +714,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, const bool checked, const QObject* receiver, const char* member) { - QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member); + QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, (QKEYSEQUENCE&)shortcut, receiver, member); action->setCheckable(true); action->setChecked(checked); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b62f49abe4..f896423b00 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -76,10 +76,10 @@ public: virtual QMenu* getActiveScriptsMenu() { return _activeScriptsMenu;} virtual QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, const QString actionName, - const QKeySequence& shortcut = 0, + const QKEYSEQUENCE& shortcut = 0, const QObject* receiver = NULL, const char* member = NULL, - QAction::MenuRole role = QAction::NoRole); + QACTION_MENUROLE role = NO_ROLE); virtual void removeAction(QMenu* menu, const QString& actionName); public slots: diff --git a/libraries/scriptengine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt similarity index 96% rename from libraries/scriptengine/CMakeLists.txt rename to libraries/script-engine/CMakeLists.txt index 593feab014..59f41b8cbe 100644 --- a/libraries/scriptengine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -6,7 +6,7 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros) # setup for find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") -set(TARGET_NAME scriptengine) +set(TARGET_NAME script-engine) find_package(Qt5Widgets REQUIRED) diff --git a/libraries/scriptengine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp similarity index 87% rename from libraries/scriptengine/src/ScriptEngine.cpp rename to libraries/script-engine/src/ScriptEngine.cpp index 7e5f414dee..35585ad6ef 100644 --- a/libraries/scriptengine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -24,10 +24,13 @@ int ScriptEngine::_scriptNumber = 1; -ScriptEngine::ScriptEngine(QString scriptContents, bool wantMenuItems, +ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const char* scriptMenuName, AbstractMenuInterface* menu) { _scriptContents = scriptContents; _isFinished = false; + _isRunning = false; + + // some clients will use these menu features _wantMenuItems = wantMenuItems; if (scriptMenuName) { _scriptMenuName = "Stop "; @@ -57,10 +60,16 @@ void ScriptEngine::cleanMenuItems() { } } -void ScriptEngine::run() { +bool ScriptEngine::setScriptContents(const QString& scriptContents) { + if (_isRunning) { + return false; + } + _scriptContents = scriptContents; + return true; +} - //setupMenuItems(); - +void ScriptEngine::run() { + _isRunning = true; QScriptEngine engine; _voxelScriptingInterface.init(); @@ -107,16 +116,14 @@ void ScriptEngine::run() { if (usecToSleep > 0) { usleep(usecToSleep); } - + if (_isFinished) { - //qDebug() << "line: " << __LINE__ << " _isFinished... breaking loop\n"; break; } QCoreApplication::processEvents(); if (_isFinished) { - //qDebug() << "line: " << __LINE__ << " _isFinished... breaking loop\n"; break; } @@ -129,7 +136,9 @@ void ScriptEngine::run() { _voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent - //_voxelScriptingInterface.getVoxelPacketSender()->process(); + if (!_voxelScriptingInterface.getVoxelPacketSender()->isThreaded()) { + _voxelScriptingInterface.getVoxelPacketSender()->process(); + } } if (_particleScriptingInterface.getParticlePacketSender()->serversExist()) { @@ -140,14 +149,15 @@ void ScriptEngine::run() { _particleScriptingInterface.getParticlePacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent - //_particleScriptingInterface.getParticlePacketSender()->process(); + if (!_particleScriptingInterface.getParticlePacketSender()->isThreaded()) { + _particleScriptingInterface.getParticlePacketSender()->process(); + } } if (willSendVisualDataCallBack) { emit willSendVisualDataCallback(); } - if (engine.hasUncaughtException()) { int line = engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; @@ -161,9 +171,11 @@ void ScriptEngine::run() { } emit finished(); + _isRunning = false; } void ScriptEngine::stop() { _isFinished = true; } + diff --git a/libraries/scriptengine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h similarity index 84% rename from libraries/scriptengine/src/ScriptEngine.h rename to libraries/script-engine/src/ScriptEngine.h index 772b0a146f..2e0d7fcc40 100644 --- a/libraries/scriptengine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -19,10 +19,12 @@ #include #include +const QString NO_SCRIPT(""); + class ScriptEngine : public QObject { Q_OBJECT public: - ScriptEngine(QString scriptContents, bool wantMenuItems = false, + ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false, const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL); ~ScriptEngine(); @@ -33,6 +35,9 @@ public: /// Access the ParticleScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener ParticleScriptingInterface* getParticleScriptingInterface() { return &_particleScriptingInterface; } + /// sets the script contents, will return false if failed, will fail if script is already running + bool setScriptContents(const QString& scriptContents); + void setupMenuItems(); void cleanMenuItems(); @@ -47,6 +52,7 @@ signals: protected: QString _scriptContents; bool _isFinished; + bool _isRunning; private: diff --git a/libraries/shared/src/AbstractMenuInterface.h b/libraries/shared/src/AbstractMenuInterface.h index 6af0ea2d00..39c378f40b 100644 --- a/libraries/shared/src/AbstractMenuInterface.h +++ b/libraries/shared/src/AbstractMenuInterface.h @@ -10,19 +10,26 @@ #ifndef __hifi__AbstractMenuInterface__ #define __hifi__AbstractMenuInterface__ -#include -//#include -//#include +class QMenu; +class QString; +class QObject; +class QKeySequence; +class QAction; + +// these are actually class scoped enums, but we don't want to depend on the class for this abstract interface +const int NO_ROLE = 0; +typedef int QACTION_MENUROLE; +typedef int QKEYSEQUENCE; class AbstractMenuInterface { public: virtual QMenu* getActiveScriptsMenu() = 0; virtual QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, const QString actionName, - const QKeySequence& shortcut = 0, + const QKEYSEQUENCE& shortcut = 0, const QObject* receiver = NULL, const char* member = NULL, - QAction::MenuRole role = QAction::NoRole) = 0; + QACTION_MENUROLE role = NO_ROLE) = 0; virtual void removeAction(QMenu* menu, const QString& actionName) = 0; }; diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index 013b7a7936..2de9112204 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -33,6 +33,8 @@ public: /// Override this function to do whatever your class actually does, return false to exit thread early. virtual bool process() = 0; + bool isThreaded() const { return _isThreaded; } + protected: /// Locks all the resources of the thread. @@ -43,8 +45,6 @@ protected: bool isStillRunning() const { return !_stopThread; } - bool isThreaded() const { return _isThreaded; } - private: pthread_mutex_t _mutex;