Script API for subscribing to entity server messages

This commit is contained in:
ksuprynowicz 2024-01-29 00:19:14 +01:00
parent d75efdb4ca
commit c311d3cbfc
7 changed files with 132 additions and 10 deletions

View file

@ -902,7 +902,12 @@ bool setupEssentials(const QCommandLineParser& parser, bool runningMarkerExisted
DependencyManager::set<CompositorHelper>();
DependencyManager::set<OffscreenQmlSurfaceCache>();
DependencyManager::set<EntityScriptClient>();
DependencyManager::set<EntityScriptServerLogClient>();
auto scriptEngines = DependencyManager::get<ScriptEngines>();
auto entityScriptServerLog = DependencyManager::get<EntityScriptServerLogClient>();
QObject::connect(scriptEngines.data(), &ScriptEngines::requestingEntityScriptServerLog, entityScriptServerLog.data(), &EntityScriptServerLogClient::requestMessagesForScriptEngines);
DependencyManager::set<GooglePolyScriptingInterface>();
DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats());
DependencyManager::set<AvatarBookmarks>();

View file

@ -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<NodeList>()->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();
}

View file

@ -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<bool> _areMessagesRequestedByScripts {false};
bool _subscribed { false };
friend class ScriptEngines;
};
#endif // hifi_EntityScriptServerLogClient_h

View file

@ -158,8 +158,69 @@ void ScriptEngines::removeScriptEngine(ScriptManagerPointer manager) {
QMutexLocker locker(&_allScriptsMutex);
_allKnownScriptManagers.remove(manager);
}
std::lock_guard<std::mutex> lock(_subscriptionsToEntityScriptMessagesMutex);
_managersSubscribedToEntityScriptMessages.remove(manager.get());
_entitiesSubscribedToEntityScriptMessages.remove(manager.get());
}
void ScriptEngines::requestServerEntityScriptMessages(ScriptManager *manager) {
std::lock_guard<std::mutex> 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<std::mutex> lock(_subscriptionsToEntityScriptMessagesMutex);
if (!_entitiesSubscribedToEntityScriptMessages.contains(manager)) {
_entitiesSubscribedToEntityScriptMessages.insert(manager,QSet<QUuid>());
}
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<std::mutex> 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<std::mutex> 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);

View file

@ -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<ScriptManagerPointer> _scriptManagersThatRequestedServerEntityMessages;
QMutex _serverEntityMessagesMutex;
// For subscriptions to server entity script messages
std::mutex _subscriptionsToEntityScriptMessagesMutex;
QSet<ScriptManager*> _managersSubscribedToEntityScriptMessages;
// Since multiple entity scripts run in the same script engine, there's a need to track subscriptions per entity
QHash<ScriptManager*, QSet<QUuid>> _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.

View file

@ -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<ScriptEngines>().data();
//TODO;
scriptEngines->requestServerEntityScriptMessages(_manager);
}
}
void ScriptManagerScriptingInterface::requestServerEntityScriptMessages(const QUuid& entityID) {
if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) {
auto scriptEngines = DependencyManager::get<ScriptEngines>().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<ScriptEngines>().data();
//TODO;
scriptEngines->removeServerEntityScriptMessagesRequest(_manager);
}
}
void ScriptManagerScriptingInterface::removeServerEntityScriptMessagesRequest(const QUuid& entityID) {
if (_manager->isEntityServerScript() || _manager->isEntityServerScript()) {
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
scriptEngines->removeServerEntityScriptMessagesRequest(_manager, entityID);
} else {
_manager->engine()->raiseException("Uuid must not be specified when removeServerEntityScriptMessagesRequest is invoked from entity script");
}
}

View file

@ -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: