diff --git a/examples/edit.js b/examples/edit.js index 92e4737f29..23cc942192 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1321,6 +1321,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 c974845585..8a15147f16 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", @@ -1181,7 +1191,10 @@
-
Script URL
+
Script URL + + +
diff --git a/examples/html/style.css b/examples/html/style.css index f12cfb6dd0..e63b6338fc 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -260,6 +260,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/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 d9c5589631..eb6a53f765 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4048,6 +4048,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); @@ -4263,7 +4264,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; @@ -4292,7 +4293,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 @@ -4303,6 +4304,10 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser return scriptEngine; } +void Application::reloadScript(const QString& scriptName, bool isUserLoaded) { + loadScript(scriptName, isUserLoaded, false, false, true); +} + void Application::handleScriptEngineLoaded(const QString& scriptFilename) { ScriptEngine* scriptEngine = qobject_cast(sender()); @@ -4330,14 +4335,25 @@ void Application::scriptFinished(const QString& scriptName) { } void Application::stopAllScripts(bool restart) { - // stops all current running scripts + 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()); + } + } + } + + // 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()) { - 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(); @@ -4345,13 +4361,20 @@ 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(); } -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. @@ -4367,6 +4390,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 2c78327f02..0787cffbdc 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -409,15 +409,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); + 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 31c9271c22..1165de7592 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(reloadOneScript(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..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); @@ -59,7 +59,8 @@ private slots: private: Ui::RunningScriptsWidget* ui; - QSignalMapper _signalMapper; + QSignalMapper _reloadSignalMapper; + QSignalMapper _stopSignalMapper; ScriptsModelFilter _scriptsModelFilter; ScriptsModel _scriptsModel; ScriptsTableWidget* _recentlyLoadedScriptsTable; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 0ea6080165..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() { @@ -148,13 +148,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); @@ -192,7 +193,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); } } } @@ -201,7 +202,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 } @@ -221,8 +222,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 } @@ -237,7 +238,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); @@ -1030,17 +1031,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 56a8208eb4..bd6044516f 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 da5f96f503..74b7a36504 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 b27129c124..3ce3a8e269 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; } + + 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 cab96eb2e6..f755cd49fb 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, ""), @@ -300,6 +301,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); @@ -393,6 +395,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); @@ -506,6 +509,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); @@ -713,6 +717,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()); @@ -966,7 +971,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); @@ -1106,6 +1112,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 4772d08c44..fdf01c4761 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); @@ -262,6 +263,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 1e3a016049..0cdfe73040 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 5e35419ba2..e83a69227f 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -123,6 +123,7 @@ enum EntityPropertyList { PROP_DESCRIPTION, PROP_FACE_CAMERA, + PROP_SCRIPT_TIMESTAMP, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties ABOVE this line 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 29a670761a..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_FACE_CAMERA; + 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 1d064c3399..789675ec00 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -184,5 +184,7 @@ 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_FACE_CAMERA = 30; +const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP = 31; +const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP_FIX = 32; #endif // hifi_PacketHeaders_h diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 8f854cfdc3..2047442ce6 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -16,18 +16,22 @@ #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 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); @@ -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 (reload) { + networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + 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); } @@ -52,6 +61,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..25a36c04d8 100644 --- a/libraries/script-engine/src/ScriptCache.h +++ b/libraries/script-engine/src/ScriptCache.h @@ -26,7 +26,8 @@ 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 0945a556f2..55cf9e9890 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); } } } @@ -904,7 +905,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 ec28804049..a96042184d 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;