diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index ff3c0b09c5..5b638f6671 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -25,6 +25,8 @@ #include "../entities/AssignmentParentFinder.h" +const size_t UUID_LENGTH_BYTES = 16; + int EntityScriptServer::_entitiesScriptEngineCount = 0; EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssignment(message) { @@ -52,10 +54,39 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); + + packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket"); + packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket"); } static const QString ENTITY_SCRIPT_SERVER_LOGGING_NAME = "entity-script-server"; +void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer message, SharedNodePointer senderNode) { + auto entityID = QUuid::fromRfc4122(message->read(UUID_LENGTH_BYTES)); + + if (_entityViewer.getTree() && !_shuttingDown) { + qDebug() << "Reloading: " << entityID; + _entitiesScriptEngine->unloadEntityScript(entityID); + checkAndCallPreload(entityID, true); + } +} + +void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer message, SharedNodePointer senderNode) { + MessageID messageID; + message->readPrimitive(&messageID); + auto entityID = QUuid::fromRfc4122(message->read(UUID_LENGTH_BYTES)); + + // TODO(Huffman) Get Status + + qDebug() << "Getting script status of: " << entityID; + auto replyPacket = NLPacket::create(PacketType::EntityScriptGetStatusReply, -1, true); + replyPacket->writePrimitive(messageID); + replyPacket->writeString("running"); + + auto nodeList = DependencyManager::get(); + nodeList->sendPacket(std::move(replyPacket), *senderNode); +} + void EntityScriptServer::run() { // make sure we request our script once the agent connects to the domain auto nodeList = DependencyManager::get(); @@ -75,7 +106,8 @@ void EntityScriptServer::run() { connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &EntityScriptServer::nodeKilled); nodeList->addSetOfNodeTypesToNodeInterestSet({ - NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer + NodeType::Agent, NodeType::AudioMixer, NodeType::AvatarMixer, + NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer }); // Setup Script Engine diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h index 5c2c700097..55c639476b 100644 --- a/assignment-client/src/scripts/EntityScriptServer.h +++ b/assignment-client/src/scripts/EntityScriptServer.h @@ -39,6 +39,9 @@ private slots: void handleJurisdictionPacket(QSharedPointer message, SharedNodePointer senderNode); void handleSelectedAudioFormat(QSharedPointer message); + void handleReloadEntityServerScriptPacket(QSharedPointer message, SharedNodePointer senderNode); + void handleEntityScriptGetStatusPacket(QSharedPointer message, SharedNodePointer senderNode); + private: void negotiateAudioFormat(); void selectAudioFormat(const QString& selectedCodecName); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4f35b10a1b..5d32a830d0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -61,7 +61,7 @@ #include #include #include -#include +#include #include #include #include @@ -173,6 +173,7 @@ #include "FrameTimingsScriptingInterface.h" #include #include +#include // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -514,6 +515,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(true, qApp, qApp); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -852,7 +854,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // tell the NodeList instance who to tell the domain server we care about nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer - << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer); + << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer << NodeType::EntityScriptServer); // connect to the packet sent signal of the _entityEditSender connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f90fddfc5a..bba9347cff 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -25,6 +25,7 @@ #include "QVariantGLM.h" #include "SimulationOwner.h" #include "ZoneEntityItem.h" +#include EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) : @@ -671,23 +672,20 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke } bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) { - // Send packet to entity script server - auto nodeList = DependencyManager::get(); - SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::AssetServer); + auto client = DependencyManager::get(); + return client->reloadServerScript(entityID); +} - if (entityScriptServer) { - auto id = entityID.toByteArray(); - auto payloadSize = id.size(); - auto packet = NLPacket::create(PacketType::ReloadEntityServerScript, payloadSize, true); - - packet->write(id); - - if (nodeList->sendPacket(std::move(packet), *entityScriptServer) != -1) { - return true; - } - } - - return false; +bool EntityScriptingInterface::getServerScriptStatus(QUuid entityID, QScriptValue callback) { + auto client = DependencyManager::get(); + auto request = client->createScriptStatusRequest(entityID); + connect(request, &GetScriptStatusRequest::finished, callback.engine(), [callback](GetScriptStatusRequest* request) mutable { + QScriptValueList args { true }; + callback.call(QScriptValue(), args); + request->deleteLater(); + }); + request->start(); + return true; } void EntityScriptingInterface::setLightsArePickable(bool value) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 30739782ea..12fef968f9 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -211,6 +211,7 @@ public slots: Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); Q_INVOKABLE bool reloadServerScripts(QUuid entityID); + Q_INVOKABLE bool getServerScriptStatus(QUuid entityID, QScriptValue callback); Q_INVOKABLE void setLightsArePickable(bool value); Q_INVOKABLE bool getLightsArePickable() const; diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 10a88c0676..be1cafa232 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -22,10 +22,8 @@ #include "AssetUtils.h" #include "LimitedNodeList.h" -#include "NLPacket.h" #include "Node.h" #include "ReceivedMessage.h" -#include "ResourceCache.h" class GetMappingRequest; class SetMappingRequest; @@ -60,8 +58,6 @@ public: Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data); - static const MessageID INVALID_MESSAGE_ID = 0; - public slots: void init(); diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 13caf87b8d..8d663933ca 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -77,7 +77,7 @@ void AssetRequest::start() { _assetInfoRequestID = assetClient->getAssetInfo(_hash, [this](bool responseReceived, AssetServerError serverError, AssetInfo info) { - _assetInfoRequestID = AssetClient::INVALID_MESSAGE_ID; + _assetInfoRequestID = INVALID_MESSAGE_ID; _info = info; @@ -119,7 +119,7 @@ void AssetRequest::start() { // If the request is dead, return return; } - _assetRequestID = AssetClient::INVALID_MESSAGE_ID; + _assetRequestID = INVALID_MESSAGE_ID; if (!responseReceived) { _error = NetworkError; diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index e5d9d119d7..1632a55336 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -64,8 +64,8 @@ private: QString _hash; QByteArray _data; int _numPendingRequests { 0 }; - MessageID _assetRequestID { AssetClient::INVALID_MESSAGE_ID }; - MessageID _assetInfoRequestID { AssetClient::INVALID_MESSAGE_ID }; + MessageID _assetRequestID { INVALID_MESSAGE_ID }; + MessageID _assetInfoRequestID { INVALID_MESSAGE_ID }; }; #endif diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index 9e508418ab..b0008271d2 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -27,6 +27,8 @@ using AssetHash = QString; using AssetMapping = std::map; using AssetPathList = QStringList; +const MessageID INVALID_MESSAGE_ID = 0; + const size_t SHA256_HASH_LENGTH = 32; const size_t SHA256_HASH_HEX_LENGTH = 64; const uint64_t MAX_UPLOAD_SIZE = 1000 * 1000 * 1000; // 1GB diff --git a/libraries/networking/src/EntityScriptClient.cpp b/libraries/networking/src/EntityScriptClient.cpp new file mode 100644 index 0000000000..2d4b493f11 --- /dev/null +++ b/libraries/networking/src/EntityScriptClient.cpp @@ -0,0 +1,153 @@ +#include "EntityScriptClient.h" +#include "NodeList.h" +#include "NetworkLogging.h" + +#include + +MessageID EntityScriptClient::_currentID = 0; + +GetScriptStatusRequest::GetScriptStatusRequest(QUuid entityID) : _entityID(entityID) { +} + +GetScriptStatusRequest::~GetScriptStatusRequest() { + +} + +void GetScriptStatusRequest::start() { + auto client = DependencyManager::get(); + qDebug() << "Sending script status request"; + client->getEntityServerScriptStatus(_entityID, [this](bool responseReceived) { + qDebug() << "Received script status request"; + emit finished(this); + }); +} + +EntityScriptClient::EntityScriptClient() { + setCustomDeleter([](Dependency* dependency){ + static_cast(dependency)->deleteLater(); + }); + + auto nodeList = DependencyManager::get(); + auto& packetReceiver = nodeList->getPacketReceiver(); + + packetReceiver.registerListener(PacketType::EntityScriptGetStatusReply, this, "handleGetScriptStatusReply"); + + connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &EntityScriptClient::handleNodeKilled); + connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset, + this, &EntityScriptClient::handleNodeClientConnectionReset); +} + +GetScriptStatusRequest* EntityScriptClient::createScriptStatusRequest(QUuid entityID) { + auto request = new GetScriptStatusRequest(entityID); + + request->moveToThread(thread()); + + return request; +} + +bool EntityScriptClient::reloadServerScript(QUuid entityID) { + // Send packet to entity script server + auto nodeList = DependencyManager::get(); + SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::EntityScriptServer); + + if (entityScriptServer) { + auto id = entityID.toRfc4122(); + auto payloadSize = id.size(); + auto packet = NLPacket::create(PacketType::ReloadEntityServerScript, payloadSize, true); + + packet->write(id); + + if (nodeList->sendPacket(std::move(packet), *entityScriptServer) != -1) { + return true; + } + } + + return false; +} + +MessageID EntityScriptClient::getEntityServerScriptStatus(QUuid entityID, GetScriptStatusCallback callback) { + auto nodeList = DependencyManager::get(); + SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::EntityScriptServer); + + if (entityScriptServer) { + auto packetList = NLPacketList::create(PacketType::EntityScriptGetStatus, QByteArray(), true, true); + + auto messageID = ++_currentID; + packetList->writePrimitive(messageID); + + packetList->write(entityID.toRfc4122()); + + if (nodeList->sendPacketList(std::move(packetList), *entityScriptServer) != -1) { + _pendingEntityScriptStatusRequests[entityScriptServer][messageID] = callback; + + return messageID; + } + } + + callback(false); + return INVALID_MESSAGE_ID; +} + +void EntityScriptClient::handleGetScriptStatusReply(QSharedPointer message, SharedNodePointer senderNode) { + Q_ASSERT(QThread::currentThread() == thread()); + + MessageID messageID; + message->readPrimitive(&messageID); + + auto status = message->readString(); + + // Check if we have any pending requests for this node + auto messageMapIt = _pendingEntityScriptStatusRequests.find(senderNode); + if (messageMapIt != _pendingEntityScriptStatusRequests.end()) { + + // Found the node, get the MessageID -> Callback map + auto& messageCallbackMap = messageMapIt->second; + + // Check if we have this pending request + auto requestIt = messageCallbackMap.find(messageID); + if (requestIt != messageCallbackMap.end()) { + auto callback = requestIt->second; + callback(true); + messageCallbackMap.erase(requestIt); + } + + // Although the messageCallbackMap may now be empty, we won't delete the node until we have disconnected from + // it to avoid constantly creating/deleting the map on subsequent requests. + } +} + +void EntityScriptClient::handleNodeKilled(SharedNodePointer node) { + Q_ASSERT(QThread::currentThread() == thread()); + + if (node->getType() != NodeType::EntityScriptServer) { + return; + } + + forceFailureOfPendingRequests(node); +} + +void EntityScriptClient::handleNodeClientConnectionReset(SharedNodePointer node) { + // a client connection to a Node was reset + // if it was an EntityScriptServer we need to cause anything pending to fail so it is re-attempted + + if (node->getType() != NodeType::EntityScriptServer) { + return; + } + + //qCDebug(entity_script_client) << "EntityScriptClient detected client connection reset handshake with Asset Server - failing any pending requests"; + + forceFailureOfPendingRequests(node); +} + +void EntityScriptClient::forceFailureOfPendingRequests(SharedNodePointer node) { + + { + auto messageMapIt = _pendingEntityScriptStatusRequests.find(node); + if (messageMapIt != _pendingEntityScriptStatusRequests.end()) { + for (const auto& value : messageMapIt->second) { + value.second(false); + } + messageMapIt->second.clear(); + } + } +} \ No newline at end of file diff --git a/libraries/networking/src/EntityScriptClient.h b/libraries/networking/src/EntityScriptClient.h new file mode 100644 index 0000000000..795ed3e530 --- /dev/null +++ b/libraries/networking/src/EntityScriptClient.h @@ -0,0 +1,67 @@ +// +// EntityScriptClient.h +// libraries/networking/src +// +// Created by Ryan Huffman on 2017/01/13 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_EntityScriptClient_h +#define hifi_EntityScriptClient_h + +#include "LimitedNodeList.h" +#include "ReceivedMessage.h" +#include "AssetUtils.h" + +#include +#include +#include + +using MessageID = uint32_t; + +using GetScriptStatusCallback = std::function; + +class GetScriptStatusRequest : public QObject { + Q_OBJECT +public: + GetScriptStatusRequest(QUuid); + ~GetScriptStatusRequest(); + + Q_INVOKABLE void start(); + +signals: + void finished(GetScriptStatusRequest* request); + +private: + QUuid _entityID; + MessageID _messageID; + QString status; +}; + +class EntityScriptClient : public QObject, public Dependency { + Q_OBJECT +public: + EntityScriptClient(); + + Q_INVOKABLE GetScriptStatusRequest* createScriptStatusRequest(QUuid entityID); + + bool reloadServerScript(QUuid entityID); + MessageID getEntityServerScriptStatus(QUuid entityID, GetScriptStatusCallback callback); + +private slots: + void handleNodeKilled(SharedNodePointer node); + void handleNodeClientConnectionReset(SharedNodePointer node); + + void handleGetScriptStatusReply(QSharedPointer message, SharedNodePointer senderNode); + +private: + static MessageID _currentID; + std::unordered_map> _pendingEntityScriptStatusRequests; + + void forceFailureOfPendingRequests(SharedNodePointer node); +}; + +#endif \ No newline at end of file diff --git a/libraries/networking/src/MappingRequest.cpp b/libraries/networking/src/MappingRequest.cpp index 62a60c521b..810b5b376d 100644 --- a/libraries/networking/src/MappingRequest.cpp +++ b/libraries/networking/src/MappingRequest.cpp @@ -68,7 +68,7 @@ void GetMappingRequest::doStart() { _mappingRequestID = assetClient->getAssetMapping(_path, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer message) { - _mappingRequestID = AssetClient::INVALID_MESSAGE_ID; + _mappingRequestID = INVALID_MESSAGE_ID; if (!responseReceived) { _error = NetworkError; } else { @@ -100,7 +100,7 @@ void GetAllMappingsRequest::doStart() { _mappingRequestID = assetClient->getAllAssetMappings( [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer message) { - _mappingRequestID = AssetClient::INVALID_MESSAGE_ID; + _mappingRequestID = INVALID_MESSAGE_ID; if (!responseReceived) { _error = NetworkError; @@ -152,7 +152,7 @@ void SetMappingRequest::doStart() { _mappingRequestID = assetClient->setAssetMapping(_path, _hash, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer message) { - _mappingRequestID = AssetClient::INVALID_MESSAGE_ID; + _mappingRequestID = INVALID_MESSAGE_ID; if (!responseReceived) { _error = NetworkError; } else { @@ -195,7 +195,7 @@ void DeleteMappingsRequest::doStart() { _mappingRequestID = assetClient->deleteAssetMappings(_paths, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer message) { - _mappingRequestID = AssetClient::INVALID_MESSAGE_ID; + _mappingRequestID = INVALID_MESSAGE_ID; if (!responseReceived) { _error = NetworkError; } else { @@ -237,7 +237,7 @@ void RenameMappingRequest::doStart() { _mappingRequestID = assetClient->renameAssetMapping(_oldPath, _newPath, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer message) { - _mappingRequestID = AssetClient::INVALID_MESSAGE_ID; + _mappingRequestID = INVALID_MESSAGE_ID; if (!responseReceived) { _error = NetworkError; } else { diff --git a/libraries/networking/src/MappingRequest.h b/libraries/networking/src/MappingRequest.h index 274d9dbd19..85b68e2427 100644 --- a/libraries/networking/src/MappingRequest.h +++ b/libraries/networking/src/MappingRequest.h @@ -40,7 +40,7 @@ public: protected: Error _error { NoError }; - MessageID _mappingRequestID { AssetClient::INVALID_MESSAGE_ID }; + MessageID _mappingRequestID { INVALID_MESSAGE_ID }; private: virtual void doStart() = 0; diff --git a/libraries/networking/src/NetworkLogging.h b/libraries/networking/src/NetworkLogging.h index 30116ff405..518c600efe 100644 --- a/libraries/networking/src/NetworkLogging.h +++ b/libraries/networking/src/NetworkLogging.h @@ -17,6 +17,7 @@ Q_DECLARE_LOGGING_CATEGORY(resourceLog) Q_DECLARE_LOGGING_CATEGORY(networking) Q_DECLARE_LOGGING_CATEGORY(asset_client) +Q_DECLARE_LOGGING_CATEGORY(entity_script_client) Q_DECLARE_LOGGING_CATEGORY(messages_client) #endif // hifi_NetworkLogging_h diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index bbeb1c3919..375043a370 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -107,6 +107,8 @@ public: RequestsDomainListData, ExitingSpaceBubble, PerAvatarGainSet, + EntityScriptGetStatus, + EntityScriptGetStatusReply, ReloadEntityServerScript, LAST_PACKET_TYPE = ReloadEntityServerScript };