diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aa9d0a2f2f..137457c722 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -902,7 +902,12 @@ bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + auto scriptEngines = DependencyManager::get(); + auto entityScriptServerLog = DependencyManager::get(); + QObject::connect(scriptEngines.data(), &ScriptEngines::requestingEntityScriptServerLog, entityScriptServerLog.data(), &EntityScriptServerLogClient::requestMessagesForScriptEngines); + DependencyManager::set(); DependencyManager::set(nullptr, qApp->getOcteeSceneStats()); DependencyManager::set(); diff --git a/libraries/entities/src/EntityScriptServerLogClient.cpp b/libraries/entities/src/EntityScriptServerLogClient.cpp index 5c21f01776..7329cf1fdd 100644 --- a/libraries/entities/src/EntityScriptServerLogClient.cpp +++ b/libraries/entities/src/EntityScriptServerLogClient.cpp @@ -39,9 +39,9 @@ void EntityScriptServerLogClient::disconnectNotify(const QMetaMethod& signal) { void EntityScriptServerLogClient::connectionsChanged() { auto numReceivers = receivers(SIGNAL(receivedNewLogLines(QString))); - if (!_subscribed && numReceivers > 0) { + if (!_subscribed && (numReceivers > 0 || _areMessagesRequestedByScripts)) { enableToEntityServerScriptLog(DependencyManager::get()->getThisNodeCanRez()); - } else if (_subscribed && numReceivers == 0) { + } else if (_subscribed && (numReceivers == 0 && !_areMessagesRequestedByScripts)) { enableToEntityServerScriptLog(false); } } @@ -140,3 +140,8 @@ void EntityScriptServerLogClient::canRezChanged(bool canRez) { enableToEntityServerScriptLog(canRez); } } + +void EntityScriptServerLogClient::requestMessagesForScriptEngines(bool areMessagesRequested) { + _areMessagesRequestedByScripts = areMessagesRequested; + connectionsChanged(); +} diff --git a/libraries/entities/src/EntityScriptServerLogClient.h b/libraries/entities/src/EntityScriptServerLogClient.h index 9eee5daed8..e388de917a 100644 --- a/libraries/entities/src/EntityScriptServerLogClient.h +++ b/libraries/entities/src/EntityScriptServerLogClient.h @@ -33,6 +33,12 @@ class EntityScriptServerLogClient : public QObject, public Dependency { public: EntityScriptServerLogClient(); + /** + * @brief This is called by ScriptEngines when scripts need access to entity server script messages and when access + * is not needed anymore. + */ + void requestMessagesForScriptEngines(bool areMessagesRequested); + signals: /*@jsdoc @@ -66,7 +72,10 @@ private slots: void connectionsChanged(); private: + std::atomic _areMessagesRequestedByScripts {false}; bool _subscribed { false }; + + friend class ScriptEngines; }; #endif // hifi_EntityScriptServerLogClient_h diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index b47255ed24..52d3b2753b 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -158,8 +158,69 @@ void ScriptEngines::removeScriptEngine(ScriptManagerPointer manager) { QMutexLocker locker(&_allScriptsMutex); _allKnownScriptManagers.remove(manager); } + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + _managersSubscribedToEntityScriptMessages.remove(manager.get()); + _entitiesSubscribedToEntityScriptMessages.remove(manager.get()); } +void ScriptEngines::requestServerEntityScriptMessages(ScriptManager *manager) { + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + if (!_managersSubscribedToEntityScriptMessages.contains(manager)) { + _managersSubscribedToEntityScriptMessages.insert(manager); + // Emit a signal to inform EntityScriptServerLogClient about subscription request + emit requestingEntityScriptServerLog(true); + qDebug() << "ScriptEngines::requestServerEntityScriptMessages"; + } +} + +void ScriptEngines::requestServerEntityScriptMessages(ScriptManager *manager, const QUuid& entityID) { + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + if (!_entitiesSubscribedToEntityScriptMessages.contains(manager)) { + _entitiesSubscribedToEntityScriptMessages.insert(manager,QSet()); + } + if (!_entitiesSubscribedToEntityScriptMessages[manager].contains(entityID)) { + _entitiesSubscribedToEntityScriptMessages[manager].insert(entityID); + // Emit a signal to inform EntityScriptServerLogClient about subscription request + emit requestingEntityScriptServerLog(true); + qDebug() << "ScriptEngines::requestServerEntityScriptMessages uuid"; + } +} + +void ScriptEngines::removeServerEntityScriptMessagesRequest(ScriptManager *manager) { + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + if (_managersSubscribedToEntityScriptMessages.contains(manager)) { + _managersSubscribedToEntityScriptMessages.remove(manager); + } + if (_entitiesSubscribedToEntityScriptMessages.isEmpty() + && _managersSubscribedToEntityScriptMessages.isEmpty()) { + // No managers requiring entity script server messages remain, so we inform EntityScriptServerLogClient about this + // Emit a signal to inform EntityScriptServerLogClient about subscription request + emit requestingEntityScriptServerLog(false); + qDebug() << "ScriptEngines::removeServerEntityScriptMessagesRequest"; + } +} + +void ScriptEngines::removeServerEntityScriptMessagesRequest(ScriptManager *manager, const QUuid& entityID) { + std::lock_guard lock(_subscriptionsToEntityScriptMessagesMutex); + if (!_entitiesSubscribedToEntityScriptMessages.contains(manager)) { + return; + } + if (_entitiesSubscribedToEntityScriptMessages[manager].contains(entityID)) { + _entitiesSubscribedToEntityScriptMessages[manager].remove(entityID); + } + if (_entitiesSubscribedToEntityScriptMessages[manager].isEmpty()) { + _entitiesSubscribedToEntityScriptMessages.remove(manager); + } + if (_entitiesSubscribedToEntityScriptMessages.isEmpty() + && _managersSubscribedToEntityScriptMessages.isEmpty()) { + // No managers requiring entity script server messages remain, so we inform EntityScriptServerLogClient about this + // Emit a signal to inform EntityScriptServerLogClient about subscription request + emit requestingEntityScriptServerLog(false); + qDebug() << "ScriptEngines::removeServerEntityScriptMessagesRequest uuid"; + } +} + + void ScriptEngines::shutdownScripting() { _isStopped = true; QMutexLocker locker(&_allScriptsMutex); diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 1a4e3c4389..bafaa1322c 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -186,6 +186,13 @@ public: void removeScriptEngine(ScriptManagerPointer); + // Called by ScriptManagerScriptingInterface + void requestServerEntityScriptMessages(ScriptManager *manager); + void requestServerEntityScriptMessages(ScriptManager *manager, const QUuid& entityID); + + void removeServerEntityScriptMessagesRequest(ScriptManager *manager); + void removeServerEntityScriptMessagesRequest(ScriptManager *manager, const QUuid& entityID); + ScriptGatekeeper scriptGatekeeper; signals: @@ -323,6 +330,12 @@ signals: */ void clearDebugWindow(); + /** + * @brief Fires when script engines need entity server script messages (areMessagesRequested == true) + * and when messages are not needed anymore (areMessagesRequested == false). + */ + void requestingEntityScriptServerLog(bool areMessagesRequested); + public slots: /*@jsdoc @@ -406,9 +419,11 @@ protected: bool _defaultScriptsLocationOverridden { false }; QString _debugScriptUrl; - // TODO: remove script managers when shutting them down - QSet _scriptManagersThatRequestedServerEntityMessages; - QMutex _serverEntityMessagesMutex; + // For subscriptions to server entity script messages + std::mutex _subscriptionsToEntityScriptMessagesMutex; + QSet _managersSubscribedToEntityScriptMessages; + // Since multiple entity scripts run in the same script engine, there's a need to track subscriptions per entity + QHash> _entitiesSubscribedToEntityScriptMessages; // If this is set, defaultScripts.js will not be run if it is in the settings, // and this will be run instead. This script will not be persisted to settings. diff --git a/libraries/script-engine/src/ScriptManagerScriptingInterface.cpp b/libraries/script-engine/src/ScriptManagerScriptingInterface.cpp index d41b53660d..8c2d2799f3 100644 --- a/libraries/script-engine/src/ScriptManagerScriptingInterface.cpp +++ b/libraries/script-engine/src/ScriptManagerScriptingInterface.cpp @@ -91,16 +91,37 @@ void ScriptManagerScriptingInterface::stopProfilingAndSave() { } void ScriptManagerScriptingInterface::requestServerEntityScriptMessages() { - if (_manager->isEntityServerScript() || _manager->isEntityServerScript() || _manager->isClientScript()) { + if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) { + _manager->engine()->raiseException("Uuid needs to be specified when requestServerEntityScriptMessages is invoked from entity script"); + } else { auto scriptEngines = DependencyManager::get().data(); - //TODO; + scriptEngines->requestServerEntityScriptMessages(_manager); + } +} + +void ScriptManagerScriptingInterface::requestServerEntityScriptMessages(const QUuid& entityID) { + if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) { + auto scriptEngines = DependencyManager::get().data(); + scriptEngines->requestServerEntityScriptMessages(_manager, entityID); + } else { + _manager->engine()->raiseException("Uuid must not be specified when requestServerEntityScriptMessages is invoked from entity script"); } } void ScriptManagerScriptingInterface::removeServerEntityScriptMessagesRequest() { - if (_manager->isEntityServerScript() || _manager->isEntityServerScript() || _manager->isClientScript()) { + if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) { + _manager->engine()->raiseException("Uuid needs to be specified when removeServerEntityScriptMessagesRequest is invoked from entity script"); + } else { auto scriptEngines = DependencyManager::get().data(); - //TODO; + scriptEngines->removeServerEntityScriptMessagesRequest(_manager); } } +void ScriptManagerScriptingInterface::removeServerEntityScriptMessagesRequest(const QUuid& entityID) { + if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) { + auto scriptEngines = DependencyManager::get().data(); + scriptEngines->removeServerEntityScriptMessagesRequest(_manager, entityID); + } else { + _manager->engine()->raiseException("Uuid must not be specified when removeServerEntityScriptMessagesRequest is invoked from entity script"); + } +} diff --git a/libraries/script-engine/src/ScriptManagerScriptingInterface.h b/libraries/script-engine/src/ScriptManagerScriptingInterface.h index 13cacb07fb..c1d6bad360 100644 --- a/libraries/script-engine/src/ScriptManagerScriptingInterface.h +++ b/libraries/script-engine/src/ScriptManagerScriptingInterface.h @@ -512,7 +512,7 @@ public: /*@jsdoc * Start collecting object statistics that can later be reported with Script.dumpHeapObjectStatistics(). - * @function Script.dumpHeapObjectStatistics + * @function Script.startCollectingObjectStatistics */ Q_INVOKABLE void startCollectingObjectStatistics(); @@ -562,18 +562,24 @@ public: * through signals such as errorEntityMessage. This function can be invoked both from client-side entity scripts * and from interface scripts. * @function Script.subscribeToServerEntityScriptMessages + * @param {Uuid=} entityID - The ID of the entity that requests entity server script messages. Only needs to be specified + * for entity scripts, and must not be specified for other types of scripts. */ Q_INVOKABLE void requestServerEntityScriptMessages(); + Q_INVOKABLE void requestServerEntityScriptMessages(const QUuid& entityID); /*@jsdoc * Calling this function signalizes that current script doesn't require stop receiving server-side entity script messages * through signals such as errorEntityMessage. This function can be invoked both from client-side entity scripts * and from interface scripts. * @function Script.unsubscribeFromServerEntityScriptMessages + * @param {Uuid=} entityID - The ID of the entity that requests entity server script messages. Only needs to be specified + * for entity scripts, and must not be specified for other types of scripts. */ Q_INVOKABLE void removeServerEntityScriptMessagesRequest(); + Q_INVOKABLE void removeServerEntityScriptMessagesRequest(const QUuid& entityID); signals: