Merge pull request #1394 from ZappoMan/agent_script_engine_refactor

make Agent class use common ScriptEngine class
This commit is contained in:
Stephen Birarda 2013-12-17 14:36:35 -08:00
commit 82f035c018
11 changed files with 58 additions and 108 deletions

View file

@ -30,6 +30,7 @@ link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree-server ${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(particle-server ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(script-engine ${TARGET_NAME} ${ROOT_DIR})
#testing #testing
include_directories(${ROOT_DIR}/externals/civetweb/include) include_directories(${ROOT_DIR}/externals/civetweb/include)

View file

@ -24,8 +24,6 @@
Agent::Agent(const unsigned char* dataBuffer, int numBytes) : Agent::Agent(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes) ThreadedAssignment(dataBuffer, numBytes)
{ {
_particleScriptingInterface.init();
_voxelScriptingInterface.init();
} }
void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { 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... // PACKET_TYPE_JURISDICTION, first byte is the node type...
switch (dataByteArray[headerBytes]) { switch (dataByteArray[headerBytes]) {
case NODE_TYPE_VOXEL_SERVER: case NODE_TYPE_VOXEL_SERVER:
_voxelScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr, _scriptEngine.getVoxelScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
(unsigned char*) dataByteArray.data(), (unsigned char*) dataByteArray.data(),
dataByteArray.size()); dataByteArray.size());
break; break;
case NODE_TYPE_PARTICLE_SERVER: case NODE_TYPE_PARTICLE_SERVER:
_particleScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr, _scriptEngine.getParticleScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
(unsigned char*) dataByteArray.data(), (unsigned char*) dataByteArray.data(),
dataByteArray.size()); dataByteArray.size());
break; break;
@ -76,43 +74,12 @@ void Agent::run() {
loop.exec(); loop.exec();
QString scriptContents(reply->readAll()); 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"; 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; timeval startTime;
gettimeofday(&startTime, NULL); gettimeofday(&startTime, NULL);
int thisFrame = 0;
QTimer* domainServerTimer = new QTimer(this); QTimer* domainServerTimer = new QTimer(this);
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
@ -124,50 +91,7 @@ void Agent::run() {
QTimer* pingNodesTimer = new QTimer(this); QTimer* pingNodesTimer = new QTimer(this);
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); 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()) { _scriptEngine.setScriptContents(scriptContents);
// allow the scripter's call back to setup visual data _scriptEngine.run();
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";
}
}
} }

View file

@ -15,6 +15,7 @@
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QUrl> #include <QtCore/QUrl>
#include <ScriptEngine.h>
#include <ThreadedAssignment.h> #include <ThreadedAssignment.h>
#include <VoxelScriptingInterface.h> #include <VoxelScriptingInterface.h>
@ -33,8 +34,7 @@ signals:
void willSendAudioDataCallback(); void willSendAudioDataCallback();
void willSendVisualDataCallback(); void willSendVisualDataCallback();
private: private:
VoxelScriptingInterface _voxelScriptingInterface; ScriptEngine _scriptEngine;
ParticleScriptingInterface _particleScriptingInterface;
}; };
#endif /* defined(__hifi__Agent__) */ #endif /* defined(__hifi__Agent__) */

View file

@ -96,7 +96,7 @@ link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(audio ${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 required libraries
find_package(Faceshift) find_package(Faceshift)

View file

@ -689,10 +689,10 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString&
QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
const QString actionName, const QString actionName,
const QKeySequence& shortcut, const QKEYSEQUENCE& shortcut,
const QObject* receiver, const QObject* receiver,
const char* member, const char* member,
QAction::MenuRole role) { QACTION_MENUROLE role) {
QAction* action; QAction* action;
if (receiver && member) { if (receiver && member) {
@ -701,7 +701,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
action = destinationMenu->addAction(actionName); action = destinationMenu->addAction(actionName);
action->setShortcut(shortcut); action->setShortcut(shortcut);
} }
action->setMenuRole(role); action->setMenuRole((QAction::MenuRole)role);
_actionHash.insert(actionName, action); _actionHash.insert(actionName, action);
@ -714,7 +714,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
const bool checked, const bool checked,
const QObject* receiver, const QObject* receiver,
const char* member) { const char* member) {
QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member); QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, (QKEYSEQUENCE&)shortcut, receiver, member);
action->setCheckable(true); action->setCheckable(true);
action->setChecked(checked); action->setChecked(checked);

View file

@ -76,10 +76,10 @@ public:
virtual QMenu* getActiveScriptsMenu() { return _activeScriptsMenu;} virtual QMenu* getActiveScriptsMenu() { return _activeScriptsMenu;}
virtual QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, virtual QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu,
const QString actionName, const QString actionName,
const QKeySequence& shortcut = 0, const QKEYSEQUENCE& shortcut = 0,
const QObject* receiver = NULL, const QObject* receiver = NULL,
const char* member = NULL, const char* member = NULL,
QAction::MenuRole role = QAction::NoRole); QACTION_MENUROLE role = NO_ROLE);
virtual void removeAction(QMenu* menu, const QString& actionName); virtual void removeAction(QMenu* menu, const QString& actionName);
public slots: public slots:

View file

@ -6,7 +6,7 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
# setup for find modules # setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/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) find_package(Qt5Widgets REQUIRED)

View file

@ -24,10 +24,13 @@
int ScriptEngine::_scriptNumber = 1; int ScriptEngine::_scriptNumber = 1;
ScriptEngine::ScriptEngine(QString scriptContents, bool wantMenuItems, ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems,
const char* scriptMenuName, AbstractMenuInterface* menu) { const char* scriptMenuName, AbstractMenuInterface* menu) {
_scriptContents = scriptContents; _scriptContents = scriptContents;
_isFinished = false; _isFinished = false;
_isRunning = false;
// some clients will use these menu features
_wantMenuItems = wantMenuItems; _wantMenuItems = wantMenuItems;
if (scriptMenuName) { if (scriptMenuName) {
_scriptMenuName = "Stop "; _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; QScriptEngine engine;
_voxelScriptingInterface.init(); _voxelScriptingInterface.init();
@ -107,16 +116,14 @@ void ScriptEngine::run() {
if (usecToSleep > 0) { if (usecToSleep > 0) {
usleep(usecToSleep); usleep(usecToSleep);
} }
if (_isFinished) { if (_isFinished) {
//qDebug() << "line: " << __LINE__ << " _isFinished... breaking loop\n";
break; break;
} }
QCoreApplication::processEvents(); QCoreApplication::processEvents();
if (_isFinished) { if (_isFinished) {
//qDebug() << "line: " << __LINE__ << " _isFinished... breaking loop\n";
break; break;
} }
@ -129,7 +136,9 @@ void ScriptEngine::run() {
_voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); _voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages();
// since we're in non-threaded mode, call process so that the packets are sent // 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()) { if (_particleScriptingInterface.getParticlePacketSender()->serversExist()) {
@ -140,14 +149,15 @@ void ScriptEngine::run() {
_particleScriptingInterface.getParticlePacketSender()->releaseQueuedMessages(); _particleScriptingInterface.getParticlePacketSender()->releaseQueuedMessages();
// since we're in non-threaded mode, call process so that the packets are sent // 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) { if (willSendVisualDataCallBack) {
emit willSendVisualDataCallback(); emit willSendVisualDataCallback();
} }
if (engine.hasUncaughtException()) { if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber(); int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
@ -161,9 +171,11 @@ void ScriptEngine::run() {
} }
emit finished(); emit finished();
_isRunning = false;
} }
void ScriptEngine::stop() { void ScriptEngine::stop() {
_isFinished = true; _isFinished = true;
} }

View file

@ -19,10 +19,12 @@
#include <ParticleScriptingInterface.h> #include <ParticleScriptingInterface.h>
#include <VoxelScriptingInterface.h> #include <VoxelScriptingInterface.h>
const QString NO_SCRIPT("");
class ScriptEngine : public QObject { class ScriptEngine : public QObject {
Q_OBJECT Q_OBJECT
public: public:
ScriptEngine(QString scriptContents, bool wantMenuItems = false, ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false,
const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL); const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL);
~ScriptEngine(); ~ScriptEngine();
@ -33,6 +35,9 @@ public:
/// Access the ParticleScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener /// Access the ParticleScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
ParticleScriptingInterface* getParticleScriptingInterface() { return &_particleScriptingInterface; } 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 setupMenuItems();
void cleanMenuItems(); void cleanMenuItems();
@ -47,6 +52,7 @@ signals:
protected: protected:
QString _scriptContents; QString _scriptContents;
bool _isFinished; bool _isFinished;
bool _isRunning;
private: private:

View file

@ -10,19 +10,26 @@
#ifndef __hifi__AbstractMenuInterface__ #ifndef __hifi__AbstractMenuInterface__
#define __hifi__AbstractMenuInterface__ #define __hifi__AbstractMenuInterface__
#include <QMenuBar> class QMenu;
//#include <QHash> class QString;
//#include <QKeySequence> 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 { class AbstractMenuInterface {
public: public:
virtual QMenu* getActiveScriptsMenu() = 0; virtual QMenu* getActiveScriptsMenu() = 0;
virtual QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, virtual QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu,
const QString actionName, const QString actionName,
const QKeySequence& shortcut = 0, const QKEYSEQUENCE& shortcut = 0,
const QObject* receiver = NULL, const QObject* receiver = NULL,
const char* member = 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; virtual void removeAction(QMenu* menu, const QString& actionName) = 0;
}; };

View file

@ -33,6 +33,8 @@ public:
/// Override this function to do whatever your class actually does, return false to exit thread early. /// Override this function to do whatever your class actually does, return false to exit thread early.
virtual bool process() = 0; virtual bool process() = 0;
bool isThreaded() const { return _isThreaded; }
protected: protected:
/// Locks all the resources of the thread. /// Locks all the resources of the thread.
@ -43,8 +45,6 @@ protected:
bool isStillRunning() const { return !_stopThread; } bool isStillRunning() const { return !_stopThread; }
bool isThreaded() const { return _isThreaded; }
private: private:
pthread_mutex_t _mutex; pthread_mutex_t _mutex;