overte-lubosz/libraries/script-engine/src/EntityScriptClient.cpp

219 lines
7.5 KiB
C++

//
// Copyright 2017-2018 High Fidelity, Inc.
// Copyright 2020-2023 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// SPDX-License-Identifier: Apache-2.0
//
#include "EntityScriptClient.h"
#include <NodeList.h>
#include <NetworkLogging.h>
#include "EntityScriptUtils.h"
#include <QThread>
MessageID EntityScriptClient::_currentID = 0;
GetScriptStatusRequest::GetScriptStatusRequest(QUuid entityID) : _entityID(entityID) {
}
GetScriptStatusRequest::~GetScriptStatusRequest() {
}
void GetScriptStatusRequest::start() {
auto client = DependencyManager::get<EntityScriptClient>();
client->getEntityServerScriptStatus(_entityID, [this](bool responseReceived, bool isRunning, EntityScriptStatus status, QString errorInfo) {
_responseReceived = responseReceived;
_isRunning = isRunning;
_status = status;
_errorInfo = errorInfo;
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,
PacketReceiver::makeSourcedListenerReference<EntityScriptClient>(this, &EntityScriptClient::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;
}
void EntityScriptClient::callEntityServerMethod(QUuid entityID, const QString& method, const QStringList& params) {
// Send packet to entity script server
auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer entityScriptServer = nodeList->soloNodeOfType(NodeType::EntityScriptServer);
if (entityScriptServer) {
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), *entityScriptServer);
}
}
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<NodeList>();
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<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, false, EntityScriptStatus::ERROR_LOADING_SCRIPT, "");
return INVALID_MESSAGE_ID;
}
void EntityScriptClient::handleGetScriptStatusReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
Q_ASSERT(QThread::currentThread() == thread());
MessageID messageID;
bool isKnown { false };
EntityScriptStatus status = EntityScriptStatus::ERROR_LOADING_SCRIPT;
QString errorInfo { "" };
message->readPrimitive(&messageID);
message->readPrimitive(&isKnown);
if (isKnown) {
message->readPrimitive(&status);
errorInfo = 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, isKnown, status, errorInfo);
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;
}
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, false, EntityScriptStatus::ERROR_LOADING_SCRIPT, "");
}
messageMapIt->second.clear();
}
}
}