From 1128a98e887f67d79d2e8f744b7f0c1348480032 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Oct 2017 11:15:57 -0700 Subject: [PATCH 1/5] implement callEntityClientMethod --- .../src/scripts/EntityScriptServer.cpp | 5 +++ .../entities/src/EntityScriptingInterface.cpp | 39 +++++++++++++++++++ .../entities/src/EntityScriptingInterface.h | 32 ++++++++++++--- .../networking/src/EntityScriptClient.cpp | 25 ++++++++++++ libraries/networking/src/EntityScriptClient.h | 7 ++++ libraries/script-engine/src/ScriptEngine.cpp | 4 ++ 6 files changed, 107 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 0ced0a632e..d627b019ef 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -30,6 +30,8 @@ #include #include +#include // for EntityScriptServerStub + #include "EntityScriptServerLogging.h" #include "../entities/AssignmentParentFinder.h" @@ -68,6 +70,9 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig DependencyManager::set(); DependencyManager::set(ScriptEngine::ENTITY_SERVER_SCRIPT); + DependencyManager::set(); + + // Needed to ensure the creation of the DebugDraw instance on the main thread DebugDraw::getInstance(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 39f19323e3..4e556c83a3 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -50,6 +50,10 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership connect(nodeList.data(), &NodeList::canRezCertifiedChanged, this, &EntityScriptingInterface::canRezCertifiedChanged); connect(nodeList.data(), &NodeList::canRezTmpCertifiedChanged, this, &EntityScriptingInterface::canRezTmpCertifiedChanged); connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged); + + + auto& packetReceiver = nodeList->getPacketReceiver(); + packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket"); } void EntityScriptingInterface::queueEntityMessage(PacketType packetType, @@ -571,6 +575,41 @@ void EntityScriptingInterface::callEntityServerMethod(QUuid id, const QString& m DependencyManager::get()->callEntityServerMethod(id, method, params); } +void EntityScriptingInterface::callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params) { + PROFILE_RANGE(script_entities, __FUNCTION__); + DependencyManager::get()->callEntityClientMethod(clientSessionID, entityID, method, params); +} + + +void EntityScriptingInterface::handleEntityScriptCallMethodPacket(QSharedPointer receivedMessage, SharedNodePointer senderNode) { + PROFILE_RANGE(script_entities, __FUNCTION__); + + auto nodeList = DependencyManager::get(); + SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::EntityScriptServer); + + if (entityScriptServer == senderNode) { + auto entityID = QUuid::fromRfc4122(receivedMessage->read(NUM_BYTES_RFC4122_UUID)); + + auto method = receivedMessage->readString(); + + quint16 paramCount; + receivedMessage->readPrimitive(¶mCount); + + QStringList params; + for (int param = 0; param < paramCount; param++) { + auto paramString = receivedMessage->readString(); + params << paramString; + } + + std::lock_guard lock(_entitiesScriptEngineLock); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, method, params, senderNode->getUUID()); + } + } +} + + + QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const { PROFILE_RANGE(script_entities, __FUNCTION__); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index ee5011e99d..a6d754cf9e 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -189,12 +189,15 @@ public slots: Q_INVOKABLE void deleteEntity(QUuid entityID); /**jsdoc - * Call a method on an entity. Allows a script to call a method on an entity's script. - * The method will execute in the entity script engine. If the entity does not have an - * entity script or the method does not exist, this call will have no effect. - * If it is running an entity script (specified by the `script` property) + * Call a method on an entity in the same context as this function is called. Allows a script + * to call a method on an entity's script. The method will execute in the entity script engine. + * If the entity does not have an entity script or the method does not exist, this call will + * have no effect. If it is running an entity script (specified by the `script` property) * and it exposes a property with the specified name `method`, it will be called - * using `params` as the list of arguments. + * using `params` as the list of arguments. If this is called within an entity script, the + * method will be executed on the client in the entity script engine in which it was called. If + * this is called in an entity server script, the method will be executed on the entity server + * script engine. * * @function Entities.callEntityMethod * @param {EntityID} entityID The ID of the entity to call the method on. @@ -218,6 +221,21 @@ public slots: */ Q_INVOKABLE void callEntityServerMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); + /**jsdoc + * Call a client method on an entity on a specific client node. Allows a server entity script to call a + * method on an entity's client script for a particular client. The method will execute in the entity script + * engine on that single client. If the entity does not have an entity script or the method does not exist, or + * the client is not connected to the domain, or you attempt to make this call outside of the entity server + * script, this call will have no effect. + * + * @function Entities.callEntityClientMethod + * @param {SessionID} clientSessionID The session ID of the client to call the method on. + * @param {EntityID} entityID The ID of the entity to call the method on. + * @param {string} method The name of the method to call. + * @param {string[]} params The list of parameters to call the specified method with. + */ + Q_INVOKABLE void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params = QStringList()); + /**jsdoc * finds the closest model to the center point, within the radius * will return a EntityItemID.isKnownID = false if no models are in the radius @@ -447,6 +465,10 @@ protected: std::lock_guard lock(_entitiesScriptEngineLock); function(_entitiesScriptEngine); }; + +private slots: + void handleEntityScriptCallMethodPacket(QSharedPointer receivedMessage, SharedNodePointer senderNode); + private: bool actionWorker(const QUuid& entityID, std::function actor); bool polyVoxWorker(QUuid entityID, std::function actor); diff --git a/libraries/networking/src/EntityScriptClient.cpp b/libraries/networking/src/EntityScriptClient.cpp index 399cb80bfa..b3a6a1b59d 100644 --- a/libraries/networking/src/EntityScriptClient.cpp +++ b/libraries/networking/src/EntityScriptClient.cpp @@ -92,6 +92,31 @@ void EntityScriptClient::callEntityServerMethod(QUuid entityID, const QString& m } } +void EntityScriptServerStub::callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params) { + qDebug() << __FUNCTION__; + + + // only valid to call this function if you are the entity script server + auto nodeList = DependencyManager::get(); + SharedNodePointer targetClient = nodeList->nodeWithUUID(clientSessionID); + + if (nodeList->getOwnerType() == NodeType::EntityScriptServer && targetClient) { + auto packetList = NLPacketList::create(PacketType::EntityScriptCallMethod, QByteArray(), true, true); + + packetList->write(entityID.toRfc4122()); + + packetList->writeString(method); + + quint16 paramCount = params.length(); + packetList->writePrimitive(paramCount); + + foreach(const QString& param, params) { + packetList->writeString(param); + } + + nodeList->sendPacketList(std::move(packetList), *targetClient); + } +} MessageID EntityScriptClient::getEntityServerScriptStatus(QUuid entityID, GetScriptStatusCallback callback) { auto nodeList = DependencyManager::get(); diff --git a/libraries/networking/src/EntityScriptClient.h b/libraries/networking/src/EntityScriptClient.h index 6f1a0376ea..43e89611da 100644 --- a/libraries/networking/src/EntityScriptClient.h +++ b/libraries/networking/src/EntityScriptClient.h @@ -74,4 +74,11 @@ private: void forceFailureOfPendingRequests(SharedNodePointer node); }; + +class EntityScriptServerStub : public QObject, public Dependency { + Q_OBJECT +public: + void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params); +}; + #endif diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 98846c5213..92aab79219 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -2510,6 +2510,8 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS if (remoteCallerID == QUuid()) { callAllowed = true; } else { + qDebug() << __FUNCTION__ << "checking remotelyCallable method: " << methodName << " on entityID:" << entityID; + if (entityScript.property("remotelyCallable").isArray()) { auto callables = entityScript.property("remotelyCallable"); auto callableCount = callables.property("length").toInteger(); @@ -2536,6 +2538,8 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); this->globalObject().property("Script").setProperty("remoteCallerID", oldData); } + } else { + qDebug() << "Entity script not running for EntityID: " << entityID << " - Method [" << methodName << "] not callable."; } } From a1885926b57f8071c255bd8b605072e54c781435 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Oct 2017 11:46:42 -0700 Subject: [PATCH 2/5] some cleanup --- assignment-client/src/scripts/EntityScriptServer.cpp | 4 ++-- libraries/entities/src/EntityScriptingInterface.cpp | 9 ++++++++- libraries/networking/src/EntityScriptClient.cpp | 5 +---- libraries/networking/src/EntityScriptClient.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 2 -- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index d627b019ef..8f4ce65579 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -30,7 +30,7 @@ #include #include -#include // for EntityScriptServerStub +#include // for EntityScriptServerServices #include "EntityScriptServerLogging.h" #include "../entities/AssignmentParentFinder.h" @@ -70,7 +70,7 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig DependencyManager::set(); DependencyManager::set(ScriptEngine::ENTITY_SERVER_SCRIPT); - DependencyManager::set(); + DependencyManager::set(); // Needed to ensure the creation of the DebugDraw instance on the main thread diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4e556c83a3..d34c1e923f 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -577,7 +577,14 @@ void EntityScriptingInterface::callEntityServerMethod(QUuid id, const QString& m void EntityScriptingInterface::callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params) { PROFILE_RANGE(script_entities, __FUNCTION__); - DependencyManager::get()->callEntityClientMethod(clientSessionID, entityID, method, params); + auto scriptServerServices = DependencyManager::get(); + + // this won't be available on clients + if (scriptServerServices) { + scriptServerServices->callEntityClientMethod(clientSessionID, entityID, method, params); + } else { + qWarning() << "Entities.callEntityClientMethod() not allowed in client"; + } } diff --git a/libraries/networking/src/EntityScriptClient.cpp b/libraries/networking/src/EntityScriptClient.cpp index b3a6a1b59d..75ae7369fb 100644 --- a/libraries/networking/src/EntityScriptClient.cpp +++ b/libraries/networking/src/EntityScriptClient.cpp @@ -92,10 +92,7 @@ void EntityScriptClient::callEntityServerMethod(QUuid entityID, const QString& m } } -void EntityScriptServerStub::callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params) { - qDebug() << __FUNCTION__; - - +void EntityScriptServerServices::callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params) { // only valid to call this function if you are the entity script server auto nodeList = DependencyManager::get(); SharedNodePointer targetClient = nodeList->nodeWithUUID(clientSessionID); diff --git a/libraries/networking/src/EntityScriptClient.h b/libraries/networking/src/EntityScriptClient.h index 43e89611da..4cfb03dca7 100644 --- a/libraries/networking/src/EntityScriptClient.h +++ b/libraries/networking/src/EntityScriptClient.h @@ -75,7 +75,7 @@ private: }; -class EntityScriptServerStub : public QObject, public Dependency { +class EntityScriptServerServices : public QObject, public Dependency { Q_OBJECT public: void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 92aab79219..4ef4180447 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -2510,8 +2510,6 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS if (remoteCallerID == QUuid()) { callAllowed = true; } else { - qDebug() << __FUNCTION__ << "checking remotelyCallable method: " << methodName << " on entityID:" << entityID; - if (entityScript.property("remotelyCallable").isArray()) { auto callables = entityScript.property("remotelyCallable"); auto callableCount = callables.property("length").toInteger(); From ed408e09d0904d1e1b6b60cd9ef1b9a100da43fa Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Oct 2017 11:50:25 -0700 Subject: [PATCH 3/5] remove some log spam --- libraries/script-engine/src/ScriptEngine.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 4ef4180447..98846c5213 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -2536,8 +2536,6 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args); this->globalObject().property("Script").setProperty("remoteCallerID", oldData); } - } else { - qDebug() << "Entity script not running for EntityID: " << entityID << " - Method [" << methodName << "] not callable."; } } From 12c32d58349f6151e14268b4de143174be5b3160 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Oct 2017 13:08:48 -0700 Subject: [PATCH 4/5] bump protocol version --- libraries/networking/src/udt/PacketHeaders.cpp | 3 +++ libraries/networking/src/udt/PacketHeaders.h | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index efb38eede1..4ce84c64f1 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -62,6 +62,9 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::DomainServerAddedNode: return static_cast(DomainServerAddedNodeVersion::PermissionsGrid); + case PacketType::EntityScriptCallMethod: + return static_cast(EntityScriptCallMethodVersion::ClientCallable); + case PacketType::MixedAudio: case PacketType::SilentAudioFrame: case PacketType::InjectAudio: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 9e2eb51fdd..fa5f910810 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -274,6 +274,12 @@ const PacketVersion VERSION_ENTITIES_UV_MODE_PROPERTY = 76; const PacketVersion VERSION_ENTITIES_STROKE_COLOR_PROPERTY = 77; + +enum class EntityScriptCallMethodVersion : PacketVersion { + ServerCallable = 18, + ClientCallable = 19 +}; + enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, JSONFilterWithFamilyTree = 19 From 275fff4609dafc6f35b40e688062f88f4a4b582f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Oct 2017 14:02:47 -0700 Subject: [PATCH 5/5] more CR feedback --- assignment-client/src/scripts/EntityScriptServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 8f4ce65579..c8067ce81f 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -588,6 +588,7 @@ void EntityScriptServer::aboutToFinish() { // cleanup the AudioInjectorManager (and any still running injectors) DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); // cleanup codec & encoder if (_codec && _encoder) {