From 2de982a5a2751d27989cef07630d94cb62daa2c1 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 30 Apr 2018 12:04:35 -0700 Subject: [PATCH] Added script types and better script reload --- interface/src/Application.cpp | 23 +++++++++++-------- interface/src/Application.h | 2 +- interface/src/ModelPackager.cpp | 1 + interface/src/avatar/MyAvatar.cpp | 6 +---- interface/src/avatar/MyAvatar.h | 5 ---- libraries/fbx/src/FSTReader.cpp | 22 ++++++++++++++++++ libraries/fbx/src/FSTReader.h | 2 ++ .../src/model-networking/ModelCache.cpp | 19 +++++++-------- libraries/script-engine/src/ScriptEngine.cpp | 14 +++++++++++ libraries/script-engine/src/ScriptEngine.h | 12 ++++++++++ libraries/script-engine/src/ScriptEngines.cpp | 6 +++-- libraries/script-engine/src/ScriptEngines.h | 2 +- 12 files changed, 81 insertions(+), 33 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c38bb9295a..833224510b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1231,6 +1231,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] { getEntities()->reloadEntityScripts(); + loadAvatarScripts(getMyAvatar()->getSkeletonModel()->getFBXGeometry().scripts); }, Qt::QueuedConnection); connect(scriptEngines, &ScriptEngines::scriptLoadError, @@ -2285,10 +2286,6 @@ void Application::onAboutToQuit() { // Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown. DependencyManager::get()->hide("RunningScripts"); - if (auto avatar = getMyAvatar()) { - auto urls = avatar->getScriptsToUnload(); - unloadAvatarScripts(urls); - } _aboutToQuit = true; @@ -4737,18 +4734,24 @@ void Application::loadAvatarScripts(const QVector& urls) { for (auto url : urls) { int index = runningScripts.indexOf(url); if (index < 0) { - scriptEngines->loadScript(url); - getMyAvatar()->addScriptToUnload(url); + auto scriptEnginePointer = scriptEngines->loadScript(url, false); + if (scriptEnginePointer) { + scriptEnginePointer->setType(ScriptEngine::Type::AVATAR); + } } } } } -void Application::unloadAvatarScripts(const QVector& urls) { - if (urls.size() > 0) { - auto scriptEngines = DependencyManager::get(); +void Application::unloadAvatarScripts() { + auto scriptEngines = DependencyManager::get(); + auto urls = scriptEngines->getRunningScripts(); + if (urls.size() > 0) { for (auto url : urls) { - scriptEngines->stopScript(url, false); + auto scriptEngine = scriptEngines->getScriptEngine(url); + if (scriptEngine->getType() == ScriptEngine::Type::AVATAR) { + scriptEngines->stopScript(url, false); + } } } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 2e91f842a4..28c85ec855 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -291,7 +291,7 @@ public: void replaceDomainContent(const QString& url); void loadAvatarScripts(const QVector& urls); - void unloadAvatarScripts(const QVector& urls); + void unloadAvatarScripts(); signals: void svoImportRequested(const QString& url); diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index a87669bf47..72b99ce5a3 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -177,6 +177,7 @@ bool ModelPackager::zipModel() { _scriptDir = _modelFile.path() + "/" + scriptField; QDir wdir = QDir(_scriptDir); _mapping.remove(SCRIPT_FIELD); + wdir.setSorting(QDir::Name | QDir::Reversed); auto list = wdir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries); for (auto script : list) { auto sc = tempDir.relativeFilePath(scriptDir.path()) + "/" + QUrl(script).fileName(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 462dbebbbb..99fdea0449 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -123,6 +123,7 @@ MyAvatar::MyAvatar(QThread* thread) : connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) { if (success) { + qApp->unloadAvatarScripts(); auto geometry = getSkeletonModel()->getFBXGeometry(); qApp->loadAvatarScripts(geometry.scripts); } @@ -1469,7 +1470,6 @@ void MyAvatar::clearJointsData() { } void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { - qApp->unloadAvatarScripts(_scriptsToUnload); _skeletonModelChangeCount++; int skeletonModelChangeCount = _skeletonModelChangeCount; Avatar::setSkeletonModelURL(skeletonModelURL); @@ -2838,10 +2838,6 @@ float MyAvatar::getWalkSpeed() const { return _walkSpeed.get() * _walkSpeedScalar; } -void MyAvatar::addScriptToUnload(const QString& url) { - _scriptsToUnload.push_back(url); -} - void MyAvatar::setSprintMode(bool sprint) { _walkSpeedScalar = sprint ? AVATAR_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 00cb50e079..a927a1d0ba 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -594,9 +594,6 @@ public: void setWalkSpeed(float value); float getWalkSpeed() const; - void addScriptToUnload(const QString& url); - const QVector& getScriptsToUnload() const { return _scriptsToUnload; }; - public slots: void increaseSize(); void decreaseSize(); @@ -907,8 +904,6 @@ private: // max unscaled forward movement speed ThreadSafeValueCache _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED }; float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR }; - - QVector _scriptsToUnload; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 603f214c3e..fcbfca1e7d 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -187,6 +187,28 @@ FSTReader::ModelType FSTReader::predictModelType(const QVariantHash& mapping) { return ENTITY_MODEL; } +QVector FSTReader::getScripts(const QUrl& url, const QVariantHash& mapping) { + + auto fstMapping = mapping.isEmpty() ? downloadMapping(url.toString()) : mapping; + QVector scriptPaths; + if (!fstMapping.value(SCRIPT_FIELD).isNull()) { + auto scripts = fstMapping.values(SCRIPT_FIELD).toVector(); + if (scripts.size() > 0) { + for (auto &script : scripts) { + QString scriptPath = script.toString(); + if (QUrl(scriptPath).isRelative()) { + if (scriptPath.at(0) == '/') { + scriptPath = scriptPath.right(scriptPath.length() - 1); + } + scriptPath = url.resolved(QUrl(scriptPath)).toString(); + } + scriptPaths.push_back(scriptPath); + } + } + } + return scriptPaths; +} + QVariantHash FSTReader::downloadMapping(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index d1204c0876..4a8574f0cf 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -50,6 +50,8 @@ public: /// Predicts the type of model by examining the mapping static ModelType predictModelType(const QVariantHash& mapping); + static QVector getScripts(const QUrl& fstUrl, const QVariantHash& mapping = QVariantHash()); + static QString getNameFromType(ModelType modelType); static FSTReader::ModelType getTypeFromName(const QString& name); static QVariantHash downloadMapping(const QString& url); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index e3f543c403..e1086bc0c9 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -83,6 +83,14 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) { _textureBaseUrl = url.resolved(QUrl(".")); } + auto scripts = FSTReader::getScripts(_url, mapping); + if (scripts.size() > 0) { + mapping.remove(SCRIPT_FIELD); + for (auto &scriptPath : scripts) { + mapping.insertMulti(SCRIPT_FIELD, scriptPath); + } + } + auto animGraphVariant = mapping.value("animGraphUrl"); if (animGraphVariant.isValid()) { QUrl fstUrl(animGraphVariant.toString()); @@ -210,19 +218,12 @@ void GeometryReader::run() { throw QString("unsupported format"); } - // Store fst scripts on geometry + // Add scripts to fbxgeometry if (!_mapping.value(SCRIPT_FIELD).isNull()) { QVariantList scripts = _mapping.values(SCRIPT_FIELD); if (scripts.size() > 0) { for (auto &script : scripts) { - QString scriptUrl = script.toString(); - if (QUrl(scriptUrl).isRelative()) { - if (scriptUrl.at(0) == '/') { - scriptUrl = scriptUrl.right(scriptUrl.length() - 1); - } - scriptUrl = _url.resolved(QUrl(scriptUrl)).toString(); - } - fbxGeometry->scripts.push_back(scriptUrl); + fbxGeometry->scripts.push_back(script.toString()); } } } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c79ffffec7..d4912fbd3d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -180,6 +180,20 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const // don't delete `ScriptEngines` until all `ScriptEngine`s are gone _scriptEngines(DependencyManager::get()) { + switch (_context) { + case Context::CLIENT_SCRIPT: + _type = Type::CLIENT; + break; + case Context::ENTITY_CLIENT_SCRIPT: + _type = Type::ENTITY_CLIENT; + break; + case Context::ENTITY_SERVER_SCRIPT: + _type = Type::ENTITY_SERVER; + break; + case Context::AGENT_SCRIPT: + _type = Type::AGENT; + } + connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) { if (hasUncaughtException()) { // the engine's uncaughtException() seems to produce much better stack traces here diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 7f69eee990..cf2606330f 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -99,6 +99,14 @@ public: AGENT_SCRIPT }; + enum Type { + CLIENT, + ENTITY_CLIENT, + ENTITY_SERVER, + AGENT, + AVATAR + }; + static int processLevelMaxRetries; ScriptEngine(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine")); ~ScriptEngine(); @@ -209,6 +217,9 @@ public: Q_INVOKABLE QUuid generateUUID() { return QUuid::createUuid(); } + void setType(Type type) { _type = type; }; + Type getType() { return _type; }; + bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget bool isRunning() const { return _isRunning; } // used by ScriptWidget @@ -293,6 +304,7 @@ protected: void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args); Context _context; + Type _type; QString _scriptContents; QString _parentURL; std::atomic _isFinished { false }; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 871705d74b..59d431fabb 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -427,11 +427,13 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) { if (_scriptEnginesHash.contains(scriptURL)) { ScriptEnginePointer scriptEngine = _scriptEnginesHash[scriptURL]; if (restart) { + bool isUserLoaded = scriptEngine->isUserLoaded(); + ScriptEngine::Type type = scriptEngine->getType(); auto scriptCache = DependencyManager::get(); scriptCache->deleteScript(scriptURL); connect(scriptEngine.data(), &ScriptEngine::finished, - this, [this](QString scriptName, ScriptEnginePointer engine) { - reloadScript(scriptName); + this, [this, isUserLoaded, type](QString scriptName, ScriptEnginePointer engine) { + reloadScript(scriptName, isUserLoaded)->setType(type); }); } scriptEngine->stop(); diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index ea07ebe840..63b18c73ea 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -110,7 +110,7 @@ protected slots: protected: friend class ScriptEngine; - void reloadScript(const QString& scriptName) { loadScript(scriptName, true, false, false, true); } + ScriptEnginePointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); } void removeScriptEngine(ScriptEnginePointer); void onScriptEngineLoaded(const QString& scriptFilename); void onScriptEngineError(const QString& scriptFilename);