Merge pull request #10 from huffman/feat/entity-server-script-property

Entity script status + reload
This commit is contained in:
Clément Brisset 2017-01-19 11:19:40 -08:00 committed by GitHub
commit 89d1242925
15 changed files with 290 additions and 33 deletions

View file

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

View file

@ -39,6 +39,9 @@ private slots:
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
void handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
private:
void negotiateAudioFormat();
void selectAudioFormat(const QString& selectedCodecName);

View file

@ -61,7 +61,7 @@
#include <CursorManager.h>
#include <DebugDraw.h>
#include <DeferredLightingEffect.h>
#include <display-plugins/DisplayPlugin.h>
#include <EntityScriptClient.h>
#include <EntityScriptingInterface.h>
#include <ErrorDialog.h>
#include <FileScriptingInterface.h>
@ -173,6 +173,7 @@
#include "FrameTimingsScriptingInterface.h"
#include <GPUIdent.h>
#include <gl/GLHelpers.h>
#include <EntityScriptClient.h>
// 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<EntityTreeRenderer>(true, qApp, qApp);
DependencyManager::set<CompositorHelper>();
DependencyManager::set<OffscreenQmlSurfaceCache>();
DependencyManager::set<EntityScriptClient>();
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);

View file

@ -25,6 +25,7 @@
#include "QVariantGLM.h"
#include "SimulationOwner.h"
#include "ZoneEntityItem.h"
#include <EntityScriptClient.h>
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<NodeList>();
SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::AssetServer);
auto client = DependencyManager::get<EntityScriptClient>();
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<EntityScriptClient>();
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) {

View file

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

View file

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

View file

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

View file

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

View file

@ -27,6 +27,8 @@ using AssetHash = QString;
using AssetMapping = std::map<AssetPath, AssetHash>;
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

View file

@ -0,0 +1,153 @@
#include "EntityScriptClient.h"
#include "NodeList.h"
#include "NetworkLogging.h"
#include <QThread>
MessageID EntityScriptClient::_currentID = 0;
GetScriptStatusRequest::GetScriptStatusRequest(QUuid entityID) : _entityID(entityID) {
}
GetScriptStatusRequest::~GetScriptStatusRequest() {
}
void GetScriptStatusRequest::start() {
auto client = DependencyManager::get<EntityScriptClient>();
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<EntityScriptClient*>(dependency)->deleteLater();
});
auto nodeList = DependencyManager::get<NodeList>();
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<NodeList>();
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<NodeList>();
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<ReceivedMessage> 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();
}
}
}

View file

@ -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 <DependencyManager.h>
#include <cstdint>
#include <unordered_map>
using MessageID = uint32_t;
using GetScriptStatusCallback = std::function<void(bool responseReceived)>;
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<ReceivedMessage> message, SharedNodePointer senderNode);
private:
static MessageID _currentID;
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, GetScriptStatusCallback>> _pendingEntityScriptStatusRequests;
void forceFailureOfPendingRequests(SharedNodePointer node);
};
#endif

View file

@ -68,7 +68,7 @@ void GetMappingRequest::doStart() {
_mappingRequestID = assetClient->getAssetMapping(_path,
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> 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<ReceivedMessage> 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<ReceivedMessage> 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<ReceivedMessage> 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<ReceivedMessage> message) {
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
_mappingRequestID = INVALID_MESSAGE_ID;
if (!responseReceived) {
_error = NetworkError;
} else {

View file

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

View file

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

View file

@ -107,6 +107,8 @@ public:
RequestsDomainListData,
ExitingSpaceBubble,
PerAvatarGainSet,
EntityScriptGetStatus,
EntityScriptGetStatusReply,
ReloadEntityServerScript,
LAST_PACKET_TYPE = ReloadEntityServerScript
};