From 0b607fa390462ff08e3142df2ac5d5d10523c385 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 20 Feb 2015 15:28:25 -0800 Subject: [PATCH] first cut at shutting down scripts ahead of other cleanup --- interface/src/Application.cpp | 18 +- .../src/EntityTreeRenderer.cpp | 329 ++++++++++-------- .../src/EntityTreeRenderer.h | 3 + libraries/script-engine/src/ScriptEngine.cpp | 58 ++- libraries/script-engine/src/ScriptEngine.h | 10 + 5 files changed, 270 insertions(+), 148 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1211df3727..ecb74392a6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -527,12 +527,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : } void Application::aboutToQuit() { +qDebug() << "Application::aboutToQuit()"; _aboutToQuit = true; cleanupBeforeQuit(); } void Application::cleanupBeforeQuit() { + qDebug() << "Application::cleanupBeforeQuit() ------------ START -----------------"; + + // stop entities running scripts + _entities.shutdown(); + + // stop all running scripts + ScriptEngine::gracefullyStopAllScripts(); + + // first stop all timers directly or by invokeMethod // depending on what thread they run in locationUpdateTimer->stop(); @@ -572,6 +582,8 @@ void Application::cleanupBeforeQuit() { // destroy the AudioClient so it and its thread have a chance to go down safely DependencyManager::destroy(); + + qDebug() << "Application::cleanupBeforeQuit() ------------ DONE -----------------"; } Application::~Application() { @@ -3432,6 +3444,7 @@ void Application::clearScriptsBeforeRunning() { } void Application::saveScripts() { +qDebug() << "Application::saveScripts()"; // Saves all currently running user-loaded scripts Settings settings; settings.beginWriteArray(SETTINGS_KEY); @@ -3525,7 +3538,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri #endif QThread* workerThread = new QThread(this); - workerThread->setObjectName("Script Engine Thread"); + QString scriptEngineName = QString("Script Thread:") + scriptEngine->getFilename(); + workerThread->setObjectName(scriptEngineName); // when the worker thread is started, call our engine's run.. connect(workerThread, &QThread::started, scriptEngine, &ScriptEngine::run); @@ -3599,6 +3613,7 @@ void Application::handleScriptLoadError(const QString& scriptFilename) { } void Application::scriptFinished(const QString& scriptName) { + qDebug() << "Application::scriptFinished(), scriptName:" << scriptName; const QString& scriptURLString = QUrl(scriptName).toString(); QHash::iterator it = _scriptEnginesHash.find(scriptURLString); if (it != _scriptEnginesHash.end()) { @@ -3609,6 +3624,7 @@ void Application::scriptFinished(const QString& scriptName) { } void Application::stopAllScripts(bool restart) { + qDebug() << "Application::stopAllScripts()... restart:" << restart; // stops all current running scripts for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); it != _scriptEnginesHash.constEnd(); it++) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 975d0f515d..b0396f32d6 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -97,6 +97,27 @@ void EntityTreeRenderer::init() { connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID); } +void EntityTreeRenderer::shutdown() { + _shuttingDown = true; + + /* + if (_entitiesScriptEngine) { + _entitiesScriptEngine->stop(); + + QEventLoop loop; + QObject::connect(_entitiesScriptEngine, &ScriptEngine::doneRunning, &loop, &QEventLoop::quit); + + _entitiesScriptEngine->stop(); + + qDebug() << "waiting on Entities sandbox script to stop... "; + loop.exec(); + qDebug() << "done waiting... "; + + } + */ +} + + QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) { EntityItem* entity = static_cast(_tree)->findEntityByEntityItemID(entityItemID); return loadEntityScript(entity); @@ -156,6 +177,10 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { + if (_shuttingDown) { + return QScriptValue(); // no entity... + } + if (!entity) { return QScriptValue(); // no entity... } @@ -235,7 +260,7 @@ void EntityTreeRenderer::setTree(Octree* newTree) { } void EntityTreeRenderer::update() { - if (_tree) { + if (_tree && !_shuttingDown) { EntityTree* tree = static_cast(_tree); tree->update(); @@ -258,7 +283,7 @@ void EntityTreeRenderer::update() { } void EntityTreeRenderer::checkEnterLeaveEntities() { - if (_tree) { + if (_tree && !_shuttingDown) { _tree->lockForWrite(); // so that our scripts can do edits if they want glm::vec3 avatarPosition = _viewState->getAvatarPosition() / (float) TREE_SCALE; @@ -309,7 +334,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { } void EntityTreeRenderer::leaveAllEntities() { - if (_tree) { + if (_tree && !_shuttingDown) { _tree->lockForWrite(); // so that our scripts can do edits if they want // for all of our previous containing entities, if they are no longer containing then send them a leave event @@ -330,7 +355,7 @@ void EntityTreeRenderer::leaveAllEntities() { } } void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) { - if (_tree) { + if (_tree && !_shuttingDown) { Model::startScene(renderSide); RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, renderSide, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -700,179 +725,193 @@ QScriptValueList EntityTreeRenderer::createEntityArgs(const EntityItemID& entity } void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { - PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); - PickRay ray = _viewState->computePickRay(event->x(), event->y()); + if (_tree && !_shuttingDown) { + PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); + PickRay ray = _viewState->computePickRay(event->x(), event->y()); - bool precisionPicking = !_dontDoPrecisionPicking; - RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking); - if (rayPickResult.intersects) { - //qDebug() << "mousePressEvent over entity:" << rayPickResult.entityID; - emit mousePressOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + bool precisionPicking = !_dontDoPrecisionPicking; + RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking); + if (rayPickResult.intersects) { + //qDebug() << "mousePressEvent over entity:" << rayPickResult.entityID; + emit mousePressOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); - QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); - QScriptValue entityScript = loadEntityScript(rayPickResult.entity); - if (entityScript.property("mousePressOnEntity").isValid()) { - entityScript.property("mousePressOnEntity").call(entityScript, entityScriptArgs); - } + QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); + QScriptValue entityScript = loadEntityScript(rayPickResult.entity); + if (entityScript.property("mousePressOnEntity").isValid()) { + entityScript.property("mousePressOnEntity").call(entityScript, entityScriptArgs); + } - _currentClickingOnEntityID = rayPickResult.entityID; - emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); - if (entityScript.property("clickDownOnEntity").isValid()) { - entityScript.property("clickDownOnEntity").call(entityScript, entityScriptArgs); + _currentClickingOnEntityID = rayPickResult.entityID; + emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + if (entityScript.property("clickDownOnEntity").isValid()) { + entityScript.property("clickDownOnEntity").call(entityScript, entityScriptArgs); + } } + _lastMouseEvent = MouseEvent(*event, deviceID); + _lastMouseEventValid = true; } - _lastMouseEvent = MouseEvent(*event, deviceID); - _lastMouseEventValid = true; } void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { - PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent"); - PickRay ray = _viewState->computePickRay(event->x(), event->y()); - bool precisionPicking = !_dontDoPrecisionPicking; - RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking); - if (rayPickResult.intersects) { - //qDebug() << "mouseReleaseEvent over entity:" << rayPickResult.entityID; - emit mouseReleaseOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + if (_tree && !_shuttingDown) { + PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent"); + PickRay ray = _viewState->computePickRay(event->x(), event->y()); + bool precisionPicking = !_dontDoPrecisionPicking; + RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking); + if (rayPickResult.intersects) { + //qDebug() << "mouseReleaseEvent over entity:" << rayPickResult.entityID; + emit mouseReleaseOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); - QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); - QScriptValue entityScript = loadEntityScript(rayPickResult.entity); - if (entityScript.property("mouseReleaseOnEntity").isValid()) { - entityScript.property("mouseReleaseOnEntity").call(entityScript, entityScriptArgs); + QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); + QScriptValue entityScript = loadEntityScript(rayPickResult.entity); + if (entityScript.property("mouseReleaseOnEntity").isValid()) { + entityScript.property("mouseReleaseOnEntity").call(entityScript, entityScriptArgs); + } } - } - // Even if we're no longer intersecting with an entity, if we started clicking on it, and now - // we're releasing the button, then this is considered a clickOn event - if (!_currentClickingOnEntityID.isInvalidID()) { - emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + // Even if we're no longer intersecting with an entity, if we started clicking on it, and now + // we're releasing the button, then this is considered a clickOn event + if (!_currentClickingOnEntityID.isInvalidID()) { + emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); - QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID); - QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); - if (currentClickingEntity.property("clickReleaseOnEntity").isValid()) { - currentClickingEntity.property("clickReleaseOnEntity").call(currentClickingEntity, currentClickingEntityArgs); + QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID); + QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); + if (currentClickingEntity.property("clickReleaseOnEntity").isValid()) { + currentClickingEntity.property("clickReleaseOnEntity").call(currentClickingEntity, currentClickingEntityArgs); + } } - } - // makes it the unknown ID, we just released so we can't be clicking on anything - _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); - _lastMouseEvent = MouseEvent(*event, deviceID); - _lastMouseEventValid = true; + // makes it the unknown ID, we just released so we can't be clicking on anything + _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); + _lastMouseEvent = MouseEvent(*event, deviceID); + _lastMouseEventValid = true; + } } void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { - PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent"); + if (_tree && !_shuttingDown) { + PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent"); - PickRay ray = _viewState->computePickRay(event->x(), event->y()); + PickRay ray = _viewState->computePickRay(event->x(), event->y()); - bool precisionPicking = false; // for mouse moves we do not do precision picking - RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking); - if (rayPickResult.intersects) { - QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); + bool precisionPicking = false; // for mouse moves we do not do precision picking + RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking); + if (rayPickResult.intersects) { + QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); - // load the entity script if needed... - QScriptValue entityScript = loadEntityScript(rayPickResult.entity); - if (entityScript.property("mouseMoveEvent").isValid()) { - entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs); - } + // load the entity script if needed... + QScriptValue entityScript = loadEntityScript(rayPickResult.entity); + if (entityScript.property("mouseMoveEvent").isValid()) { + entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs); + } - //qDebug() << "mouseMoveEvent over entity:" << rayPickResult.entityID; - emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); - if (entityScript.property("mouseMoveOnEntity").isValid()) { - entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs); - } + //qDebug() << "mouseMoveEvent over entity:" << rayPickResult.entityID; + emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + if (entityScript.property("mouseMoveOnEntity").isValid()) { + entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs); + } - // handle the hover logic... + // handle the hover logic... - // if we were previously hovering over an entity, and this new entity is not the same as our previous entity - // then we need to send the hover leave. - if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) { - emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); + // if we were previously hovering over an entity, and this new entity is not the same as our previous entity + // then we need to send the hover leave. + if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) { + emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); - QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID); + QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID); - QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); - if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { - currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs); - } - } - - // If the new hover entity does not match the previous hover entity then we are entering the new one - // this is true if the _currentHoverOverEntityID is known or unknown - if (rayPickResult.entityID != _currentHoverOverEntityID) { - emit hoverEnterEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); - if (entityScript.property("hoverEnterEntity").isValid()) { - entityScript.property("hoverEnterEntity").call(entityScript, entityScriptArgs); - } - } - - // and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and - // we should send our hover over event - emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); - if (entityScript.property("hoverOverEntity").isValid()) { - entityScript.property("hoverOverEntity").call(entityScript, entityScriptArgs); - } - - // remember what we're hovering over - _currentHoverOverEntityID = rayPickResult.entityID; - - } else { - // handle the hover logic... - // if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to - // send the hover leave for our previous entity - if (!_currentHoverOverEntityID.isInvalidID()) { - emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); - - QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID); - - QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); - if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { - currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs); + QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); + if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { + currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs); + } } - _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID + // If the new hover entity does not match the previous hover entity then we are entering the new one + // this is true if the _currentHoverOverEntityID is known or unknown + if (rayPickResult.entityID != _currentHoverOverEntityID) { + emit hoverEnterEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + if (entityScript.property("hoverEnterEntity").isValid()) { + entityScript.property("hoverEnterEntity").call(entityScript, entityScriptArgs); + } + } + + // and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and + // we should send our hover over event + emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + if (entityScript.property("hoverOverEntity").isValid()) { + entityScript.property("hoverOverEntity").call(entityScript, entityScriptArgs); + } + + // remember what we're hovering over + _currentHoverOverEntityID = rayPickResult.entityID; + + } else { + // handle the hover logic... + // if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to + // send the hover leave for our previous entity + if (!_currentHoverOverEntityID.isInvalidID()) { + emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); + + QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID); + + QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); + if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { + currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs); + } + + _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID + } } - } - // Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have - // not yet released the hold then this is still considered a holdingClickOnEntity event - if (!_currentClickingOnEntityID.isInvalidID()) { - emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + // Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have + // not yet released the hold then this is still considered a holdingClickOnEntity event + if (!_currentClickingOnEntityID.isInvalidID()) { + emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); - QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID); + QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID); - QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); - if (currentClickingEntity.property("holdingClickOnEntity").isValid()) { - currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs); + QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); + if (currentClickingEntity.property("holdingClickOnEntity").isValid()) { + currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs); + } } + _lastMouseEvent = MouseEvent(*event, deviceID); + _lastMouseEventValid = true; } - _lastMouseEvent = MouseEvent(*event, deviceID); - _lastMouseEventValid = true; } void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { - checkAndCallUnload(entityID); + if (_tree && !_shuttingDown) { + checkAndCallUnload(entityID); + } _entityScripts.remove(entityID); } void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) { - checkAndCallUnload(entityID); + if (_tree && !_shuttingDown) { + checkAndCallUnload(entityID); + } checkAndCallPreload(entityID); } void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) { - // load the entity script if needed... - QScriptValue entityScript = loadEntityScript(entityID); - if (entityScript.property("preload").isValid()) { - QScriptValueList entityArgs = createEntityArgs(entityID); - entityScript.property("preload").call(entityScript, entityArgs); + if (_tree && !_shuttingDown) { + // load the entity script if needed... + QScriptValue entityScript = loadEntityScript(entityID); + if (entityScript.property("preload").isValid()) { + QScriptValueList entityArgs = createEntityArgs(entityID); + entityScript.property("preload").call(entityScript, entityArgs); + } } } void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) { - QScriptValue entityScript = getPreviouslyLoadedEntityScript(entityID); - if (entityScript.property("unload").isValid()) { - QScriptValueList entityArgs = createEntityArgs(entityID); - entityScript.property("unload").call(entityScript, entityArgs); + if (_tree && !_shuttingDown) { + QScriptValue entityScript = getPreviouslyLoadedEntityScript(entityID); + if (entityScript.property("unload").isValid()) { + QScriptValueList entityArgs = createEntityArgs(entityID); + entityScript.property("unload").call(entityScript, entityArgs); + } } } @@ -887,22 +926,24 @@ void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { - QScriptValue entityScriptA = loadEntityScript(idA); - if (entityScriptA.property("collisionWithEntity").isValid()) { - QScriptValueList args; - args << idA.toScriptValue(_entitiesScriptEngine); - args << idB.toScriptValue(_entitiesScriptEngine); - args << collisionToScriptValue(_entitiesScriptEngine, collision); - entityScriptA.property("collisionWithEntity").call(entityScriptA, args); - } + if (_tree && !_shuttingDown) { + QScriptValue entityScriptA = loadEntityScript(idA); + if (entityScriptA.property("collisionWithEntity").isValid()) { + QScriptValueList args; + args << idA.toScriptValue(_entitiesScriptEngine); + args << idB.toScriptValue(_entitiesScriptEngine); + args << collisionToScriptValue(_entitiesScriptEngine, collision); + entityScriptA.property("collisionWithEntity").call(entityScriptA, args); + } - QScriptValue entityScriptB = loadEntityScript(idB); - if (entityScriptB.property("collisionWithEntity").isValid()) { - QScriptValueList args; - args << idB.toScriptValue(_entitiesScriptEngine); - args << idA.toScriptValue(_entitiesScriptEngine); - args << collisionToScriptValue(_entitiesScriptEngine, collision); - entityScriptB.property("collisionWithEntity").call(entityScriptA, args); + QScriptValue entityScriptB = loadEntityScript(idB); + if (entityScriptB.property("collisionWithEntity").isValid()) { + QScriptValueList args; + args << idB.toScriptValue(_entitiesScriptEngine); + args << idA.toScriptValue(_entitiesScriptEngine); + args << collisionToScriptValue(_entitiesScriptEngine, collision); + entityScriptB.property("collisionWithEntity").call(entityScriptA, args); + } } } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 3826a80238..0da85f360b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -46,6 +46,7 @@ public: virtual int getBoundaryLevelAdjust() const; virtual void setTree(Octree* newTree); + void shutdown(); void update(); EntityTree* getTree() { return static_cast(_tree); } @@ -154,6 +155,8 @@ private: bool _displayModelElementProxy; bool _dontDoPrecisionPicking; + bool _shuttingDown = false; + }; #endif // hifi_EntityTreeRenderer_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0956374238..63ecea4254 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -94,8 +94,45 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _isUserLoaded(false), _arrayBufferClass(new ArrayBufferClass(this)) { + _allKnownScriptEngines.insert(this); } +ScriptEngine::~ScriptEngine() { + qDebug() << "[" << QThread::currentThread() << "]" << "ScriptEngine::~ScriptEngine() " << getFilename(); + _allKnownScriptEngines.remove(this); +} + +QSet ScriptEngine::_allKnownScriptEngines; + +void ScriptEngine::gracefullyStopAllScripts() { + qDebug() << "[" << QThread::currentThread() << "]" << "ScriptEngine::gracefullyStopAllScripts() ----------- START ------------------"; + foreach(ScriptEngine* scriptEngine, _allKnownScriptEngines) { + if (scriptEngine->isRunning()) { + qDebug() << "scriptEngine still alive:" << scriptEngine->getFilename() << "[" << scriptEngine << "]"; + + QEventLoop loop; + QObject::connect(scriptEngine, &ScriptEngine::doneRunning, &loop, &QEventLoop::quit); + + scriptEngine->stop(); + + qDebug() << "waiting on script to stop... "; + loop.exec(); + qDebug() << "done waiting... "; + } + } + qDebug() << "[" << QThread::currentThread() << "]" << "ScriptEngine::gracefullyStopAllScripts() ----------- DONE ------------------"; +} + +QString ScriptEngine::getFilename() const { + QStringList fileNameParts = _fileNameString.split("/"); + QString lastPart; + if (!fileNameParts.isEmpty()) { + lastPart = fileNameParts.last(); + } + return lastPart; +} + + void ScriptEngine::setIsAvatar(bool isAvatar) { _isAvatar = isAvatar; @@ -364,16 +401,18 @@ void ScriptEngine::run() { } if (_isFinished) { + qDebug() << "ScriptEngine::run()... while() about to break " << getFilename(); break; } QCoreApplication::processEvents(); if (_isFinished) { + qDebug() << "ScriptEngine::run()... while() about to break " << getFilename(); break; } - if (_entityScriptingInterface.getEntityPacketSender()->serversExist()) { + if (!_isFinished && _entityScriptingInterface.getEntityPacketSender()->serversExist()) { // release the queue of edit entity messages. _entityScriptingInterface.getEntityPacketSender()->releaseQueuedMessages(); @@ -383,7 +422,7 @@ void ScriptEngine::run() { } } - if (_isAvatar && _avatarData) { + if (!_isFinished && _isAvatar && _avatarData) { const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * AudioConstants::SAMPLE_RATE) / (1000 * 1000)) + 0.5); @@ -493,9 +532,13 @@ void ScriptEngine::run() { clearExceptions(); } - emit update(deltaTime); + if (!_isFinished) { + //qDebug() << "ScriptEngine::run()... about to emit update() _scriptName:" << _scriptName << "_fileNameString:" << _fileNameString << "_isFinished:" << _isFinished; + emit update(deltaTime); + } lastUpdate = now; } + qDebug() << "ScriptEngine::run()... about to emit scriptEnding() " << getFilename(); emit scriptEnding(); // kill the avatar identity timer @@ -513,16 +556,25 @@ void ScriptEngine::run() { // If we were on a thread, then wait till it's done if (thread()) { + qDebug() << "ScriptEngine::run()... about to call thread()->quit() " << getFilename(); thread()->quit(); } + qDebug() << "ScriptEngine::run()... about to emit finished() " << getFilename(); emit finished(_fileNameString); _isRunning = false; + + qDebug() << "ScriptEngine::run()... about to emit runningStateChanged() " << getFilename(); emit runningStateChanged(); + + qDebug() << "ScriptEngine::run()... about to emit doneRunning() " << getFilename(); + emit doneRunning(); + qDebug() << "ScriptEngine::run()... DONE WITH run()... " << getFilename(); } void ScriptEngine::stop() { + qDebug() << "ScriptEngine::stop()... " << getFilename(); _isFinished = true; emit runningStateChanged(); } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index f2911842e6..f3289a8c70 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -43,6 +43,8 @@ public: const QString& fileNameString = QString(""), AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); + ~ScriptEngine(); + /// Access the EntityScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static EntityScriptingInterface* getEntityScriptingInterface() { return &_entityScriptingInterface; } @@ -88,6 +90,10 @@ public: bool isUserLoaded() const { return _isUserLoaded; } void setParentURL(const QString& parentURL) { _parentURL = parentURL; } + + QString getFilename() const; + + static void gracefullyStopAllScripts(); public slots: void loadURL(const QUrl& scriptURL); @@ -118,6 +124,7 @@ signals: void runningStateChanged(); void evaluationFinished(QScriptValue result, bool isException); void loadScript(const QString& scriptName, bool isUserLoaded); + void doneRunning(); protected: QString _scriptContents; @@ -156,6 +163,9 @@ private: QHash _outgoingScriptAudioSequenceNumbers; private slots: void handleScriptDownload(); + +private: + static QSet _allKnownScriptEngines; }; #endif // hifi_ScriptEngine_h