Skip to content

Commit

Permalink
Script API for subscribing to entity server messages
Browse files Browse the repository at this point in the history
  • Loading branch information
ksuprynowicz committed Jan 28, 2024
1 parent d75efdb commit c311d3c
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 10 deletions.
5 changes: 5 additions & 0 deletions interface/src/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>();
Expand Down
9 changes: 7 additions & 2 deletions libraries/entities/src/EntityScriptServerLogClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -140,3 +140,8 @@ void EntityScriptServerLogClient::canRezChanged(bool canRez) {
enableToEntityServerScriptLog(canRez);
}
}

void EntityScriptServerLogClient::requestMessagesForScriptEngines(bool areMessagesRequested) {
_areMessagesRequestedByScripts = areMessagesRequested;
connectionsChanged();
}
9 changes: 9 additions & 0 deletions libraries/entities/src/EntityScriptServerLogClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -66,7 +72,10 @@ private slots:
void connectionsChanged();

private:
std::atomic<bool> _areMessagesRequestedByScripts {false};
bool _subscribed { false };

friend class ScriptEngines;
};

#endif // hifi_EntityScriptServerLogClient_h
61 changes: 61 additions & 0 deletions libraries/script-engine/src/ScriptEngines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
21 changes: 18 additions & 3 deletions libraries/script-engine/src/ScriptEngines.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ class ScriptEngines : public QObject, public Dependency, public ScriptInitialize

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:
Expand Down Expand Up @@ -323,6 +330,12 @@ class ScriptEngines : public QObject, public Dependency, public ScriptInitialize
*/
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
Expand Down Expand Up @@ -406,9 +419,11 @@ protected slots:
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.
Expand Down
29 changes: 25 additions & 4 deletions libraries/script-engine/src/ScriptManagerScriptingInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ class ScriptManagerScriptingInterface : public QObject {

/*@jsdoc
* Start collecting object statistics that can later be reported with Script.dumpHeapObjectStatistics().
* @function Script.dumpHeapObjectStatistics
* @function Script.startCollectingObjectStatistics
*/
Q_INVOKABLE void startCollectingObjectStatistics();

Expand Down Expand Up @@ -562,18 +562,24 @@ class ScriptManagerScriptingInterface : public QObject {
* 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:

Expand Down

0 comments on commit c311d3c

Please sign in to comment.