From f220d40bb855c90c59614a6737a5a0c652c2e23a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 10 Jun 2015 13:55:21 -0700 Subject: [PATCH 1/9] Re-download scripts upon Reload All --- interface/src/Application.cpp | 3 +++ libraries/script-engine/src/ScriptCache.cpp | 9 ++++++++- libraries/script-engine/src/ScriptCache.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 651e343087..882997d38a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4344,6 +4344,8 @@ void Application::scriptFinished(const QString& scriptName) { } void Application::stopAllScripts(bool restart) { + auto scriptCache = DependencyManager::get(); + // stops all current running scripts for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); it != _scriptEnginesHash.constEnd(); it++) { @@ -4351,6 +4353,7 @@ void Application::stopAllScripts(bool restart) { continue; } if (restart && it.value()->isUserLoaded()) { + scriptCache->deleteScript(it.key()); connect(it.value(), SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&))); } it.value()->stop(); diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 8f854cfdc3..6b7e03df33 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -44,7 +44,7 @@ QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& is QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - qCDebug(scriptengine) << "Downloading script at" << url.toString(); + qCDebug(scriptengine) << "Downloading script at:" << url.toString(); QNetworkReply* reply = networkAccessManager.get(networkRequest); connect(reply, &QNetworkReply::finished, this, &ScriptCache::scriptDownloaded); } @@ -52,6 +52,13 @@ QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& is return scriptContents; } +void ScriptCache::deleteScript(const QUrl& url) { + if (_scriptCache.contains(url)) { + qCDebug(scriptengine) << "Delete script from cache:" << url.toString(); + _scriptCache.remove(url); + } +} + void ScriptCache::scriptDownloaded() { QNetworkReply* reply = qobject_cast(sender()); QUrl url = reply->url(); diff --git a/libraries/script-engine/src/ScriptCache.h b/libraries/script-engine/src/ScriptCache.h index 16bf04c53e..820c11b073 100644 --- a/libraries/script-engine/src/ScriptCache.h +++ b/libraries/script-engine/src/ScriptCache.h @@ -27,6 +27,7 @@ class ScriptCache : public QObject, public Dependency { public: QString getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending); + void deleteScript(const QUrl& url); void addScriptToBadScriptList(const QUrl& url) { _badScripts.insert(url); } bool isInBadScriptList(const QUrl& url) { return _badScripts.contains(url); } From 5dab977c286a098e3a075f765acc8eed0d694fe1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 10 Jun 2015 15:34:38 -0700 Subject: [PATCH 2/9] Add a reload script button after each script in list of running scripts --- interface/resources/images/reload-script.svg | 50 ++++++++++++++++++++ interface/src/Application.cpp | 13 +++++ interface/src/Application.h | 1 + interface/src/ui/RunningScriptsWidget.cpp | 23 +++++++-- interface/src/ui/RunningScriptsWidget.h | 3 +- 5 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 interface/resources/images/reload-script.svg diff --git a/interface/resources/images/reload-script.svg b/interface/resources/images/reload-script.svg new file mode 100644 index 0000000000..a33de448dc --- /dev/null +++ b/interface/resources/images/reload-script.svg @@ -0,0 +1,50 @@ + +image/svg+xml \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 882997d38a..0bf245368c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4317,6 +4317,19 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser return scriptEngine; } +void Application::reloadScript(const QString& scriptFilename) { + DependencyManager::get()->deleteScript(scriptFilename); + + ScriptEngine* scriptEngine = _scriptEnginesHash.value(scriptFilename); + connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&))); + scriptEngine->stop(); + + // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities + // whenever a script stops in case it happened to have been setting joint rotations. + // TODO: expose animation priorities and provide a layered animation control system. + _myAvatar->clearScriptableSettings(); +} + void Application::handleScriptEngineLoaded(const QString& scriptFilename) { ScriptEngine* scriptEngine = qobject_cast(sender()); diff --git a/interface/src/Application.h b/interface/src/Application.h index c9e5bea76e..39b71cc6a1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -400,6 +400,7 @@ public slots: bool askToLoadScript(const QString& scriptFilenameOrURL); ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false); + void reloadScript(const QString& scriptFilename); void scriptFinished(const QString& scriptName); void stopAllScripts(bool restart = false); void stopScript(const QString& scriptName); diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 31c9271c22..9afed34886 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -32,7 +32,8 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : QWidget(parent, Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint), ui(new Ui::RunningScriptsWidget), - _signalMapper(this), + _reloadSignalMapper(this), + _stopSignalMapper(this), _scriptsModelFilter(this), _scriptsModel(this) { ui->setupUi(this); @@ -64,7 +65,8 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : Application::getInstance(), &Application::loadDialog); connect(ui->loadScriptFromURLButton, &QPushButton::clicked, Application::getInstance(), &Application::loadScriptURLDialog); - connect(&_signalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(stopScript(const QString&))); + connect(&_reloadSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(reloadScript(const QString&))); + connect(&_stopSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(stopScript(const QString&))); UIUtil::scaleWidgetFontSizes(this); } @@ -115,6 +117,17 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { name->setText(name->text() + "(" + QString::number(hash.find(list.at(i)).value()) + ")"); } ++hash[list.at(i)]; + + QPushButton* reloadButton = new QPushButton(row); + reloadButton->setFlat(true); + reloadButton->setIcon( + QIcon(QPixmap(PathUtils::resourcesPath() + "images/reload-script.svg").scaledToHeight(CLOSE_ICON_HEIGHT))); + reloadButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + reloadButton->setStyleSheet("border: 0;"); + reloadButton->setCursor(Qt::PointingHandCursor); + connect(reloadButton, SIGNAL(clicked()), &_reloadSignalMapper, SLOT(map())); + _reloadSignalMapper.setMapping(reloadButton, url.toString()); + QPushButton* closeButton = new QPushButton(row); closeButton->setFlat(true); closeButton->setIcon( @@ -122,9 +135,8 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { closeButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); closeButton->setStyleSheet("border: 0;"); closeButton->setCursor(Qt::PointingHandCursor); - - connect(closeButton, SIGNAL(clicked()), &_signalMapper, SLOT(map())); - _signalMapper.setMapping(closeButton, url.toString()); + connect(closeButton, SIGNAL(clicked()), &_stopSignalMapper, SLOT(map())); + _stopSignalMapper.setMapping(closeButton, url.toString()); row->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); @@ -132,6 +144,7 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { row->layout()->setSpacing(0); row->layout()->addWidget(name); + row->layout()->addWidget(reloadButton); row->layout()->addWidget(closeButton); row->setToolTip(url.toString()); diff --git a/interface/src/ui/RunningScriptsWidget.h b/interface/src/ui/RunningScriptsWidget.h index 5d3f6843af..1686d24a03 100644 --- a/interface/src/ui/RunningScriptsWidget.h +++ b/interface/src/ui/RunningScriptsWidget.h @@ -59,7 +59,8 @@ private slots: private: Ui::RunningScriptsWidget* ui; - QSignalMapper _signalMapper; + QSignalMapper _reloadSignalMapper; + QSignalMapper _stopSignalMapper; ScriptsModelFilter _scriptsModelFilter; ScriptsModel _scriptsModel; ScriptsTableWidget* _recentlyLoadedScriptsTable; From 17f7d76025ee49785014022580fbce08fa30672b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 11 Jun 2015 16:24:06 -0700 Subject: [PATCH 3/9] Redownload scripts that are loaded via Script.load() when reloading all --- interface/src/Application.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0bf245368c..b332204e79 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4357,16 +4357,24 @@ void Application::scriptFinished(const QString& scriptName) { } void Application::stopAllScripts(bool restart) { - auto scriptCache = DependencyManager::get(); + if (restart) { + // Delete all running scripts from cache so that they are re-downloaded when they are restarted + auto scriptCache = DependencyManager::get(); + for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); + it != _scriptEnginesHash.constEnd(); it++) { + if (!it.value()->isFinished()) { + scriptCache->deleteScript(it.key()); + } + } + } - // stops all current running scripts + // Stop and possibly restart all currently running scripts for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); it != _scriptEnginesHash.constEnd(); it++) { if (it.value()->isFinished()) { continue; } if (restart && it.value()->isUserLoaded()) { - scriptCache->deleteScript(it.key()); connect(it.value(), SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&))); } it.value()->stop(); From 2a00586e2134987711729a0bd2daaddd079d25b2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 15 Jun 2015 10:30:30 -0700 Subject: [PATCH 4/9] Fix missing hack Matches hack in stopScript(). --- interface/src/Application.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b332204e79..2bc41029f3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4383,6 +4383,7 @@ void Application::stopAllScripts(bool restart) { // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities // whenever a script stops in case it happened to have been setting joint rotations. // TODO: expose animation priorities and provide a layered animation control system. + _myAvatar->clearJointAnimationPriorities(); _myAvatar->clearScriptableSettings(); } From 67206332e6ef628b5924708a58992423a6a691ea Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 15 Jun 2015 17:10:00 -0700 Subject: [PATCH 5/9] Make individual reload buttons reload scripts --- interface/src/Application.cpp | 34 +++++++++++--------- interface/src/Application.h | 9 ++++-- interface/src/ui/RunningScriptsWidget.cpp | 2 +- interface/src/ui/RunningScriptsWidget.h | 2 +- libraries/script-engine/src/ScriptCache.cpp | 15 +++++++-- libraries/script-engine/src/ScriptCache.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 15 ++++++--- libraries/script-engine/src/ScriptEngine.h | 4 ++- 8 files changed, 53 insertions(+), 30 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2bc41029f3..b6c417534e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4062,6 +4062,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(scriptFinished(const QString&))); connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool))); + connect(scriptEngine, SIGNAL(reloadScript(const QString&, bool)), this, SLOT(reloadScript(const QString&, bool))); scriptEngine->registerGlobalObject("Overlays", &_overlays); qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); @@ -4277,7 +4278,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { } ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, - bool loadScriptFromEditor, bool activateMainWindow) { + bool loadScriptFromEditor, bool activateMainWindow, bool reload) { if (isAboutToQuit()) { return NULL; @@ -4306,7 +4307,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError); // get the script engine object to load the script at the designated script URL - scriptEngine->loadURL(scriptUrl); + scriptEngine->loadURL(scriptUrl, reload); } // restore the main window's active state @@ -4317,17 +4318,8 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser return scriptEngine; } -void Application::reloadScript(const QString& scriptFilename) { - DependencyManager::get()->deleteScript(scriptFilename); - - ScriptEngine* scriptEngine = _scriptEnginesHash.value(scriptFilename); - connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&))); - scriptEngine->stop(); - - // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities - // whenever a script stops in case it happened to have been setting joint rotations. - // TODO: expose animation priorities and provide a layered animation control system. - _myAvatar->clearScriptableSettings(); +void Application::reloadScript(const QString& scriptName, bool isUserLoaded) { + loadScript(scriptName, isUserLoaded, false, false, true); } void Application::handleScriptEngineLoaded(const QString& scriptFilename) { @@ -4375,7 +4367,7 @@ void Application::stopAllScripts(bool restart) { continue; } if (restart && it.value()->isUserLoaded()) { - connect(it.value(), SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&))); + connect(it.value(), SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&))); } it.value()->stop(); qCDebug(interfaceapp) << "stopping script..." << it.key(); @@ -4387,10 +4379,16 @@ void Application::stopAllScripts(bool restart) { _myAvatar->clearScriptableSettings(); } -void Application::stopScript(const QString &scriptName) { +void Application::stopScript(const QString &scriptName, bool restart) { const QString& scriptURLString = QUrl(scriptName).toString(); if (_scriptEnginesHash.contains(scriptURLString)) { - _scriptEnginesHash.value(scriptURLString)->stop(); + ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURLString]; + if (restart) { + auto scriptCache = DependencyManager::get(); + scriptCache->deleteScript(scriptName); + connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&))); + } + scriptEngine->stop(); qCDebug(interfaceapp) << "stopping script..." << scriptName; // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities // whenever a script stops in case it happened to have been setting joint rotations. @@ -4406,6 +4404,10 @@ void Application::reloadAllScripts() { stopAllScripts(true); } +void Application::reloadOneScript(const QString& scriptName) { + stopScript(scriptName, true); +} + void Application::loadDefaultScripts() { if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) { loadScript(DEFAULT_SCRIPTS_JS_URL); diff --git a/interface/src/Application.h b/interface/src/Application.h index 39b71cc6a1..e0b4946b06 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -398,16 +398,19 @@ public slots: bool acceptSnapshot(const QString& urlString); bool askToSetAvatarUrl(const QString& url); bool askToLoadScript(const QString& scriptFilenameOrURL); + ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true, - bool loadScriptFromEditor = false, bool activateMainWindow = false); - void reloadScript(const QString& scriptFilename); + bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false); + void reloadScript(const QString& scriptName, bool isUserLoaded = true); void scriptFinished(const QString& scriptName); void stopAllScripts(bool restart = false); - void stopScript(const QString& scriptName); + void stopScript(const QString& scriptName, bool restart = false); void reloadAllScripts(); + void reloadOneScript(const QString& scriptName); void loadDefaultScripts(); void toggleRunningScriptsWidget(); void saveScripts(); + void showFriendsWindow(); void friendsWindowClosed(); diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 9afed34886..1165de7592 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -65,7 +65,7 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : Application::getInstance(), &Application::loadDialog); connect(ui->loadScriptFromURLButton, &QPushButton::clicked, Application::getInstance(), &Application::loadScriptURLDialog); - connect(&_reloadSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(reloadScript(const QString&))); + connect(&_reloadSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(reloadOneScript(const QString&))); connect(&_stopSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(stopScript(const QString&))); UIUtil::scaleWidgetFontSizes(this); diff --git a/interface/src/ui/RunningScriptsWidget.h b/interface/src/ui/RunningScriptsWidget.h index 1686d24a03..c09bce5443 100644 --- a/interface/src/ui/RunningScriptsWidget.h +++ b/interface/src/ui/RunningScriptsWidget.h @@ -36,7 +36,7 @@ public: const ScriptsModel* getScriptsModel() { return &_scriptsModel; } signals: - void stopScriptName(const QString& name); + void stopScriptName(const QString& name, bool restart = false); protected: virtual bool eventFilter(QObject* sender, QEvent* event); diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 6b7e03df33..ae16494af3 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -16,16 +16,20 @@ #include #include +#include #include #include -#include "ScriptEngineLogging.h" + #include "ScriptCache.h" +#include "ScriptEngineLogging.h" ScriptCache::ScriptCache(QObject* parent) { // nothing to do here... } -QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending) { +QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending, bool redownload) { + assert(!_scriptCache.contains(url) || !redownload); + QString scriptContents; if (_scriptCache.contains(url)) { qCDebug(scriptengine) << "Found script in cache:" << url.toString(); @@ -43,8 +47,13 @@ QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& is QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + if (redownload) { + networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); + qCDebug(scriptengine) << "Redownloading script at:" << url.toString(); + } else { + qCDebug(scriptengine) << "Downloading script at:" << url.toString(); + } - qCDebug(scriptengine) << "Downloading script at:" << url.toString(); QNetworkReply* reply = networkAccessManager.get(networkRequest); connect(reply, &QNetworkReply::finished, this, &ScriptCache::scriptDownloaded); } diff --git a/libraries/script-engine/src/ScriptCache.h b/libraries/script-engine/src/ScriptCache.h index 820c11b073..25a36c04d8 100644 --- a/libraries/script-engine/src/ScriptCache.h +++ b/libraries/script-engine/src/ScriptCache.h @@ -26,7 +26,7 @@ class ScriptCache : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - QString getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending); + QString getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending, bool redownload = false); void deleteScript(const QUrl& url); void addScriptToBadScriptList(const QUrl& url) { _badScripts.insert(url); } bool isInBadScriptList(const QUrl& url) { return _badScripts.contains(url); } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 182a0aea8d..c0ba9aa32b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -94,6 +94,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _vec3Library(), _uuidLibrary(), _isUserLoaded(false), + _isReloading(false), _arrayBufferClass(new ArrayBufferClass(this)) { _allScriptsMutex.lock(); @@ -251,12 +252,13 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents, const QStrin return true; } -void ScriptEngine::loadURL(const QUrl& scriptURL) { +void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { if (_isRunning) { return; } _fileNameString = scriptURL.toString(); + _isReloading = reload; QUrl url(scriptURL); @@ -283,8 +285,7 @@ void ScriptEngine::loadURL(const QUrl& scriptURL) { } else { bool isPending; auto scriptCache = DependencyManager::get(); - scriptCache->getScript(url, this, isPending); - + scriptCache->getScript(url, this, isPending, reload); } } } @@ -901,7 +902,13 @@ void ScriptEngine::load(const QString& loadFile) { } QUrl url = resolvePath(loadFile); - emit loadScript(url.toString(), false); + if (_isReloading) { + auto scriptCache = DependencyManager::get(); + scriptCache->deleteScript(url.toString()); + emit reloadScript(url.toString(), false); + } else { + emit loadScript(url.toString(), false); + } } void ScriptEngine::nodeKilled(SharedNodePointer node) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 58ecf4adb2..e56b9c4f11 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -104,7 +104,7 @@ public: Q_INVOKABLE void removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler); public slots: - void loadURL(const QUrl& scriptURL); + void loadURL(const QUrl& scriptURL, bool reload); void stop(); QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1); @@ -132,6 +132,7 @@ signals: void runningStateChanged(); void evaluationFinished(QScriptValue result, bool isException); void loadScript(const QString& scriptName, bool isUserLoaded); + void reloadScript(const QString& scriptName, bool isUserLoaded); void doneRunning(); protected: @@ -165,6 +166,7 @@ private: Vec3 _vec3Library; ScriptUUID _uuidLibrary; bool _isUserLoaded; + bool _isReloading; ArrayBufferClass* _arrayBufferClass; From 6623d0c5527820843d6616f8d971ba2d0bc9a484 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 17 Jun 2015 16:09:51 -0700 Subject: [PATCH 6/9] Add "Reload" button associated with entity editor's script URL field The Reload button re-downloads the script for both the editor and for everyone in the vicinity. --- examples/edit.js | 9 +++++++ examples/html/entityProperties.html | 15 ++++++++++- examples/html/style.css | 5 ++++ .../src/EntityTreeRenderer.cpp | 25 ++++++++++--------- .../src/EntityTreeRenderer.h | 10 ++++---- libraries/entities/src/EntityItem.cpp | 6 +++++ libraries/entities/src/EntityItem.h | 5 ++++ .../entities/src/EntityItemProperties.cpp | 9 ++++++- libraries/entities/src/EntityItemProperties.h | 2 ++ .../src/EntityItemPropertiesDefaults.h | 1 + .../entities/src/EntityItemPropertiesMacros.h | 2 ++ libraries/entities/src/EntityPropertyFlags.h | 1 + libraries/entities/src/EntityTree.cpp | 12 ++++++--- libraries/entities/src/EntityTree.h | 4 +-- libraries/entities/src/EntityTreeElement.cpp | 7 ++++-- libraries/networking/src/PacketHeaders.cpp | 2 +- libraries/networking/src/PacketHeaders.h | 1 + libraries/script-engine/src/ScriptCache.cpp | 10 ++++---- 18 files changed, 93 insertions(+), 33 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 2974397cde..e61822558c 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1349,6 +1349,15 @@ PropertiesTool = function(opts) { pushCommandForSelections(); selectionManager._update(); } + } else if (data.action == "reloadScript") { + if (selectionManager.hasSelection()) { + var timestamp = Date.now(); + for (var i = 0; i < selectionManager.selections.length; i++) { + Entities.editEntity(selectionManager.selections[i], { + scriptTimestamp: timestamp, + }); + } + } } else if (data.action == "centerAtmosphereToZone") { if (selectionManager.hasSelection()) { selectionManager.saveProperties(); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 27a2929d1c..b4b336fbda 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -250,6 +250,8 @@ var elCollisionSoundURL = document.getElementById("property-collision-sound-url"); var elLifetime = document.getElementById("property-lifetime"); var elScriptURL = document.getElementById("property-script-url"); + var elScriptTimestamp = document.getElementById("property-script-timestamp"); + var elReloadScriptButton = document.getElementById("reload-script-button"); var elUserData = document.getElementById("property-user-data"); var elColorSection = document.getElementById("color-section"); @@ -470,6 +472,7 @@ elCollisionSoundURL.value = properties.collisionSoundURL; elLifetime.value = properties.lifetime; elScriptURL.value = properties.script; + elScriptTimestamp.value = properties.scriptTimestamp; elUserData.value = properties.userData; elHyperlinkHref.value = properties.href; @@ -688,6 +691,7 @@ elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime')); elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script')); + elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp')); elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); var colorChangeFunction = createEmitColorPropertyUpdateFunction( @@ -889,6 +893,12 @@ percentage: parseInt(elRescaleDimensionsPct.value), })); }); + elReloadScriptButton.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "reloadScript" + })); + }); elCenterAtmosphereToZone.addEventListener("click", function() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", @@ -1138,7 +1148,10 @@
-
Script URL
+
Script URL + + +
diff --git a/examples/html/style.css b/examples/html/style.css index dd2e96ab43..8564479ac7 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -250,6 +250,11 @@ table#properties-list { height: 1.2em; } +#properties-list .label input[type=button] { + float: right; + padding: 0 5px 1px; +} + #properties-list .value > div{ padding: 3pt 0; } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e1bd01547e..845c870d90 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -155,13 +155,14 @@ void EntityTreeRenderer::errorInLoadingScript(const QUrl& url) { } } -QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID, bool isPreload) { +QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID, bool isPreload, bool reload) { EntityItemPointer entity = static_cast(_tree)->findEntityByEntityItemID(entityItemID); - return loadEntityScript(entity, isPreload); + return loadEntityScript(entity, isPreload, reload); } -QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut) { +QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut, + bool& reload) { isPending = false; QUrl url(scriptMaybeURLorText); @@ -199,7 +200,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe auto scriptCache = DependencyManager::get(); if (!scriptCache->isInBadScriptList(url)) { - scriptContents = scriptCache->getScript(url, this, isPending); + scriptContents = scriptCache->getScript(url, this, isPending, reload); } } } @@ -208,7 +209,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe } -QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool isPreload) { +QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool isPreload, bool reload) { if (_shuttingDown) { return QScriptValue(); // since we're shutting down, we don't load any more scripts } @@ -228,8 +229,8 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool if (_entityScripts.contains(entityID)) { EntityScriptDetails details = _entityScripts[entityID]; - // check to make sure our script text hasn't changed on us since we last loaded it - if (details.scriptText == entityScript) { + // check to make sure our script text hasn't changed on us since we last loaded it and we're not redownloading it + if (details.scriptText == entityScript && !reload) { return details.scriptObject; // previously loaded } @@ -244,7 +245,7 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool bool isURL = false; // loadScriptContents() will tell us if this is a URL or just text. bool isPending = false; QUrl url; - QString scriptContents = loadScriptContents(entityScript, isURL, isPending, url); + QString scriptContents = loadScriptContents(entityScript, isURL, isPending, url, reload); if (isPending && isPreload && isURL) { _waitingOnPreload.insert(url, entityID); @@ -1029,17 +1030,17 @@ void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) { } -void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) { +void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID, const bool reload) { if (_tree && !_shuttingDown) { checkAndCallUnload(entityID); - checkAndCallPreload(entityID); + checkAndCallPreload(entityID, reload); } } -void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) { +void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const bool reload) { if (_tree && !_shuttingDown) { // load the entity script if needed... - QScriptValue entityScript = loadEntityScript(entityID, true); // is preload! + QScriptValue entityScript = loadEntityScript(entityID, true, reload); // is preload! if (entityScript.property("preload").isValid()) { QScriptValueList entityArgs = createEntityArgs(entityID); entityScript.property("preload").call(entityScript, entityArgs); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index e491524c78..451aed6ce5 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -110,7 +110,7 @@ signals: public slots: void addingEntity(const EntityItemID& entityID); void deletingEntity(const EntityItemID& entityID); - void entitySciptChanging(const EntityItemID& entityID); + void entitySciptChanging(const EntityItemID& entityID, const bool reload); void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); // optional slots that can be wired to menu items @@ -127,7 +127,7 @@ private: void applyZonePropertiesToScene(std::shared_ptr zone); void renderElementProxy(EntityTreeElement* entityTreeElement, RenderArgs* args); - void checkAndCallPreload(const EntityItemID& entityID); + void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false); void checkAndCallUnload(const EntityItemID& entityID); QList _releasedModels; @@ -148,10 +148,10 @@ private: ScriptEngine* _entitiesScriptEngine; ScriptEngine* _sandboxScriptEngine; - QScriptValue loadEntityScript(EntityItemPointer entity, bool isPreload = false); - QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false); + QScriptValue loadEntityScript(EntityItemPointer entity, bool isPreload = false, bool reload = false); + QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false, bool reload = false); QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID); - QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url); + QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url, bool& reload); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index d2881e5f3a..63a770ab7a 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -54,6 +54,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _friction(ENTITY_ITEM_DEFAULT_FRICTION), _lifetime(ENTITY_ITEM_DEFAULT_LIFETIME), _script(ENTITY_ITEM_DEFAULT_SCRIPT), + _scriptTimestamp(ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP), _collisionSoundURL(ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL), _registrationPoint(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT), _angularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY), @@ -107,6 +108,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_FRICTION; requestedProperties += PROP_LIFETIME; requestedProperties += PROP_SCRIPT; + requestedProperties += PROP_SCRIPT_TIMESTAMP; requestedProperties += PROP_COLLISION_SOUND_URL; requestedProperties += PROP_REGISTRATION_POINT; requestedProperties += PROP_ANGULAR_VELOCITY; @@ -238,6 +240,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_FRICTION, getFriction()); APPEND_ENTITY_PROPERTY(PROP_LIFETIME, getLifetime()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT, getScript()); + APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, getScriptTimestamp()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping()); @@ -555,6 +558,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction); READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime); READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript); + READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); //READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityInDegrees); @@ -901,6 +905,7 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(created, getCreated); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime); COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptTimestamp, getScriptTimestamp); COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionSoundURL, getCollisionSoundURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint); COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getAngularVelocity); @@ -967,6 +972,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { // non-simulation properties below SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptTimestamp, setScriptTimestamp); SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel); SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 73f9127361..8d9ad244ef 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -276,6 +276,10 @@ public: const QString& getScript() const { return _script; } void setScript(const QString& value) { _script = value; } + + const quint64 getScriptTimestamp() const { return _scriptTimestamp; } + void setScriptTimestamp(const quint64 value) { _scriptTimestamp = value; } + const QString& getCollisionSoundURL() const { return _collisionSoundURL; } void setCollisionSoundURL(const QString& value) { _collisionSoundURL = value; } @@ -412,6 +416,7 @@ protected: float _friction; float _lifetime; QString _script; + quint64 _scriptTimestamp; QString _collisionSoundURL; glm::vec3 _registrationPoint; glm::vec3 _angularVelocity; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index cbb3b1dc31..471d191d17 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -51,6 +51,7 @@ CONSTRUCT_PROPERTY(friction, ENTITY_ITEM_DEFAULT_FRICTION), CONSTRUCT_PROPERTY(lifetime, ENTITY_ITEM_DEFAULT_LIFETIME), CONSTRUCT_PROPERTY(created, UNKNOWN_CREATED_TIME), CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT), +CONSTRUCT_PROPERTY(scriptTimestamp, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP), CONSTRUCT_PROPERTY(collisionSoundURL, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL), CONSTRUCT_PROPERTY(color, ), CONSTRUCT_PROPERTY(modelURL, ""), @@ -299,6 +300,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_FRICTION, friction); CHECK_PROPERTY_CHANGE(PROP_LIFETIME, lifetime); CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script); + CHECK_PROPERTY_CHANGE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp); CHECK_PROPERTY_CHANGE(PROP_COLLISION_SOUND_URL, collisionSoundURL); CHECK_PROPERTY_CHANGE(PROP_COLOR, color); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); @@ -391,6 +393,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(created, created.toString(Qt::ISODate)); COPY_PROPERTY_TO_QSCRIPTVALUE(script); + COPY_PROPERTY_TO_QSCRIPTVALUE(scriptTimestamp); COPY_PROPERTY_TO_QSCRIPTVALUE(registrationPoint); COPY_PROPERTY_TO_QSCRIPTVALUE(angularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(angularDamping); @@ -503,6 +506,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(friction, float, setFriction); COPY_PROPERTY_FROM_QSCRIPTVALUE(lifetime, float, setLifetime); COPY_PROPERTY_FROM_QSCRIPTVALUE(script, QString, setScript); + COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptTimestamp, quint64, setScriptTimestamp); COPY_PROPERTY_FROM_QSCRIPTVALUE(registrationPoint, glmVec3, setRegistrationPoint); COPY_PROPERTY_FROM_QSCRIPTVALUE(angularVelocity, glmVec3, setAngularVelocity); COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping); @@ -710,6 +714,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_FRICTION, properties.getFriction()); APPEND_ENTITY_PROPERTY(PROP_LIFETIME, properties.getLifetime()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT, properties.getScript()); + APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, properties.getScriptTimestamp()); APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, properties.getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, properties.getAngularVelocity()); @@ -961,7 +966,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RESTITUTION, float, setRestitution); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FRICTION, float, setFriction); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT,QString, setScript); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT, QString, setScript); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, xColor, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity); @@ -1099,6 +1105,7 @@ void EntityItemProperties::markAllChanged() { _userDataChanged = true; _simulatorIDChanged = true; _scriptChanged = true; + _scriptTimestampChanged = true; _collisionSoundURLChanged = true; _registrationPointChanged = true; _angularVelocityChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 068bc98f7e..294aab7f75 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -98,6 +98,7 @@ public: DEFINE_PROPERTY(PROP_LIFETIME, Lifetime, lifetime, float); DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64); DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString); + DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64); DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor); DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString); @@ -259,6 +260,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Friction, friction, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifetime, lifetime, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ScriptTimestamp, scriptTimestamp, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionSoundURL, collisionSoundURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 11341b160c..7ca93a3f33 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -32,6 +32,7 @@ const float ENTITY_ITEM_DEFAULT_GLOW_LEVEL = 0.0f; const bool ENTITY_ITEM_DEFAULT_VISIBLE = true; const QString ENTITY_ITEM_DEFAULT_SCRIPT = QString(""); +const quint64 ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP = 0; const QString ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL = QString(""); const glm::vec3 ENTITY_ITEM_DEFAULT_REGISTRATION_POINT = ENTITY_ITEM_HALF_VEC3; // center diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 0f3459e5d2..d4fcfa8cab 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -92,6 +92,7 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec3& v) { r inline QScriptValue convertScriptValue(QScriptEngine* e, float v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, int v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, quint32 v) { return QScriptValue(v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, quint64 v) { return QScriptValue((qsreal)v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QString& v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const xColor& v) { return xColorToScriptValue(e, v); } @@ -134,6 +135,7 @@ typedef glm::vec3 glmVec3; typedef glm::quat glmQuat; typedef QVector qVectorVec3; inline float float_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } +inline quint64 quint64_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toULongLong(&isValid); } inline uint16_t uint16_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toBool(); } diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index f1ebdb8a1f..666f9e0346 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -55,6 +55,7 @@ enum EntityPropertyList { PROP_DAMPING, PROP_LIFETIME, PROP_SCRIPT, + PROP_SCRIPT_TIMESTAMP, // these properties are supported by some derived classes PROP_COLOR, diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 363f3c56d7..752082932b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -185,6 +185,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI // else client accepts what the server says QString entityScriptBefore = entity->getScript(); + quint64 entityScriptTimestampBefore = entity->getScriptTimestamp(); QString collisionSoundURLBefore = entity->getCollisionSoundURL(); uint32_t preFlags = entity->getDirtyFlags(); UpdateEntityOperator theOperator(this, containingElement, entity, properties); @@ -206,8 +207,10 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI } QString entityScriptAfter = entity->getScript(); - if (entityScriptBefore != entityScriptAfter) { - emitEntityScriptChanging(entity->getEntityItemID()); // the entity script has changed + quint64 entityScriptTimestampAfter = entity->getScriptTimestamp(); + bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; + if (entityScriptBefore != entityScriptAfter || reload) { + emitEntityScriptChanging(entity->getEntityItemID(), reload); // the entity script has changed } maybeNotifyNewCollisionSoundURL(collisionSoundURLBefore, entity->getCollisionSoundURL()); } @@ -266,9 +269,10 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti return result; } -void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) { - emit entityScriptChanging(entityItemID); +void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID, const bool reload) { + emit entityScriptChanging(entityItemID, reload); } + void EntityTree::maybeNotifyNewCollisionSoundURL(const QString& previousCollisionSoundURL, const QString& nextCollisionSoundURL) { if (!nextCollisionSoundURL.isEmpty() && (nextCollisionSoundURL != previousCollisionSoundURL)) { emit newCollisionSoundURL(QUrl(nextCollisionSoundURL)); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 1ad3ef6b1c..9c4be9a86f 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -155,7 +155,7 @@ public: void entityChanged(EntityItemPointer entity); - void emitEntityScriptChanging(const EntityItemID& entityItemID); + void emitEntityScriptChanging(const EntityItemID& entityItemID, const bool reload); void setSimulation(EntitySimulation* simulation); EntitySimulation* getSimulation() const { return _simulation; } @@ -171,7 +171,7 @@ public: signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); - void entityScriptChanging(const EntityItemID& entityItemID); + void entityScriptChanging(const EntityItemID& entityItemID, const bool reload); void newCollisionSoundURL(const QUrl& url); void clearingEntities(); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 80333a44e5..4bc81e1da6 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -733,6 +733,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int // 3) remember the old cube for the entity so we can mark it as dirty if (entityItem) { QString entityScriptBefore = entityItem->getScript(); + quint64 entityScriptTimestampBefore = entityItem->getScriptTimestamp(); bool bestFitBefore = bestFitEntityBounds(entityItem); EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID); @@ -755,8 +756,10 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int } QString entityScriptAfter = entityItem->getScript(); - if (entityScriptBefore != entityScriptAfter) { - _myTree->emitEntityScriptChanging(entityItemID); // the entity script has changed + quint64 entityScriptTimestampAfter = entityItem->getScriptTimestamp(); + bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; + if (entityScriptBefore != entityScriptAfter || reload) { + _myTree->emitEntityScriptChanging(entityItemID, reload); // the entity script has changed } } else { diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 78bb3f11e1..f989c417f0 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -73,7 +73,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketTypeEntityAdd: case PacketTypeEntityEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_LINE_POINTS; + return VERSION_ENTITIES_SCRIPT_TIMESTAMP; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 228df1cdde..5780e7bec2 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -183,5 +183,6 @@ const PacketVersion VERSION_ENTITIES_HAVE_FRICTION = 26; const PacketVersion VERSION_NO_ENTITY_ID_SWAP = 27; const PacketVersion VERSION_ENTITIES_PARTICLE_FIX = 28; const PacketVersion VERSION_ENTITIES_LINE_POINTS = 29; +const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP = 30; #endif // hifi_PacketHeaders_h diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index ae16494af3..2047442ce6 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -27,11 +27,11 @@ ScriptCache::ScriptCache(QObject* parent) { // nothing to do here... } -QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending, bool redownload) { - assert(!_scriptCache.contains(url) || !redownload); +QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending, bool reload) { + assert(!_scriptCache.contains(url) || !reload); QString scriptContents; - if (_scriptCache.contains(url)) { + if (_scriptCache.contains(url) && !reload) { qCDebug(scriptengine) << "Found script in cache:" << url.toString(); scriptContents = _scriptCache[url]; scriptUser->scriptContentsAvailable(url, scriptContents); @@ -47,8 +47,8 @@ QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& is QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - if (redownload) { - networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); + if (reload) { + networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); qCDebug(scriptengine) << "Redownloading script at:" << url.toString(); } else { qCDebug(scriptengine) << "Downloading script at:" << url.toString(); From 93509f4c6e91f475039e90e4ebe2f2d73ae78c6b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 23 Jun 2015 11:25:02 -0700 Subject: [PATCH 7/9] Code review --- libraries/entities/src/EntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 8d9ad244ef..f6f1aba2fc 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -277,7 +277,7 @@ public: const QString& getScript() const { return _script; } void setScript(const QString& value) { _script = value; } - const quint64 getScriptTimestamp() const { return _scriptTimestamp; } + quint64 getScriptTimestamp() const { return _scriptTimestamp; } void setScriptTimestamp(const quint64 value) { _scriptTimestamp = value; } const QString& getCollisionSoundURL() const { return _collisionSoundURL; } From ccbc048f64c55030f056d7c4db1ced1538947f49 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 26 Jun 2015 14:59:40 -0700 Subject: [PATCH 8/9] repair bad ordering of PROP_SCRIPT_TIMESTAMP --- libraries/entities/src/EntityPropertyFlags.h | 2 +- libraries/networking/src/PacketHeaders.cpp | 2 +- libraries/networking/src/PacketHeaders.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 9f4cc27cf3..e83a69227f 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -55,7 +55,6 @@ enum EntityPropertyList { PROP_DAMPING, PROP_LIFETIME, PROP_SCRIPT, - PROP_SCRIPT_TIMESTAMP, // these properties are supported by some derived classes PROP_COLOR, @@ -124,6 +123,7 @@ enum EntityPropertyList { PROP_DESCRIPTION, PROP_FACE_CAMERA, + PROP_SCRIPT_TIMESTAMP, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties ABOVE this line diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index f989c417f0..5fe9bbbb99 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -73,7 +73,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketTypeEntityAdd: case PacketTypeEntityEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_SCRIPT_TIMESTAMP; + return VERSION_ENTITIES_SCRIPT_TIMESTAMP_FIX; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index ade4fc8bd9..789675ec00 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -185,5 +185,6 @@ const PacketVersion VERSION_ENTITIES_PARTICLE_FIX = 28; const PacketVersion VERSION_ENTITIES_LINE_POINTS = 29; const PacketVersion VERSION_ENTITIES_FACE_CAMERA = 30; const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP = 31; +const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP_FIX = 32; #endif // hifi_PacketHeaders_h From f61581d29d79870a861193a134fab2354f736236 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 26 Jun 2015 15:42:04 -0700 Subject: [PATCH 9/9] possible fix for deadlock --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index dfbc765228..03d88200c5 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -122,9 +122,9 @@ void EntityTreeRenderer::init() { // first chance, we'll check for enter/leave entity events. _lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE); - connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity); - connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::addingEntity); - connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::entitySciptChanging); + connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity, Qt::QueuedConnection); + connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::addingEntity, Qt::QueuedConnection); + connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::entitySciptChanging, Qt::QueuedConnection); } void EntityTreeRenderer::shutdown() {