mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-19 09:19:03 +02:00
Merge pull request #16 from huffman/feat/entity-server-script-property
Add server script status to edit.js
This commit is contained in:
commit
c2ca76f630
10 changed files with 279 additions and 75 deletions
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "EntityScriptServer.h"
|
||||
|
||||
#include "EntityScriptUtils.h"
|
||||
|
||||
#include <AudioConstants.h>
|
||||
#include <AudioInjectorManager.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
|
@ -21,12 +23,11 @@
|
|||
#include <ScriptCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <SoundCache.h>
|
||||
#include <UUID.h>
|
||||
#include <WebSocketServerClass.h>
|
||||
|
||||
#include "../entities/AssignmentParentFinder.h"
|
||||
|
||||
const size_t UUID_LENGTH_BYTES = 16;
|
||||
|
||||
int EntityScriptServer::_entitiesScriptEngineCount = 0;
|
||||
|
||||
EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssignment(message) {
|
||||
|
@ -62,29 +63,42 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
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));
|
||||
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
||||
// about each other.
|
||||
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
|
||||
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
if (_entityViewer.getTree() && !_shuttingDown) {
|
||||
qDebug() << "Reloading: " << entityID;
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
checkAndCallPreload(entityID, true);
|
||||
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));
|
||||
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
|
||||
// about each other.
|
||||
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
|
||||
MessageID messageID;
|
||||
message->readPrimitive(&messageID);
|
||||
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// 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 replyPacketList = NLPacketList::create(PacketType::EntityScriptGetStatusReply, QByteArray(), true, true);
|
||||
replyPacketList->writePrimitive(messageID);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->sendPacket(std::move(replyPacket), *senderNode);
|
||||
EntityScriptDetails details;
|
||||
if (_entitiesScriptEngine->getEntityScriptDetails(entityID, details)) {
|
||||
replyPacketList->writePrimitive(true);
|
||||
replyPacketList->writePrimitive(details.status);
|
||||
replyPacketList->writeString(details.errorInfo);
|
||||
} else {
|
||||
replyPacketList->writePrimitive(false);
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->sendPacketList(std::move(replyPacketList), *senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::run() {
|
||||
|
|
|
@ -953,21 +953,32 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
||||
if (nodeData->isAuthenticated()) {
|
||||
// if this authenticated node has any interest types, send back those nodes as well
|
||||
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){
|
||||
limitedNodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
if (otherNode->getUUID() != node->getUUID()
|
||||
&& nodeInterestSet.contains(otherNode->getType())) {
|
||||
|
||||
// since we're about to add a node to the packet we start a segment
|
||||
domainListPackets->startSegment();
|
||||
|
||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||
domainListStream << *otherNode.data();
|
||||
// (1/19/17) Agents only need to connect to Entity Script Servers to perform administrative tasks
|
||||
// related to entity server scripts. Only agents with rez permissions should be doing that, so
|
||||
// if the agent does not have those permissions, we do not want them and the server to incur the
|
||||
// overhead of connecting to one another.
|
||||
bool shouldNotConnect = (node->getType() == NodeType::Agent && otherNode->getType() == NodeType::EntityScriptServer
|
||||
&& !node->getCanRez() && !node->getCanRezTmp())
|
||||
|| (node->getType() == NodeType::EntityScriptServer && otherNode->getType() == NodeType::Agent
|
||||
&& !otherNode->getCanRez() && !otherNode->getCanRezTmp());
|
||||
|
||||
// pack the secret that these two nodes will use to communicate with each other
|
||||
domainListStream << connectionSecretForNodes(node, otherNode);
|
||||
if (!shouldNotConnect) {
|
||||
// since we're about to add a node to the packet we start a segment
|
||||
domainListPackets->startSegment();
|
||||
|
||||
// we've added the node we wanted so end the segment now
|
||||
domainListPackets->endSegment();
|
||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||
domainListStream << *otherNode.data();
|
||||
|
||||
// pack the secret that these two nodes will use to communicate with each other
|
||||
domainListStream << connectionSecretForNodes(node, otherNode);
|
||||
|
||||
// we've added the node we wanted so end the segment now
|
||||
domainListPackets->endSegment();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -680,7 +680,22 @@ bool EntityScriptingInterface::getServerScriptStatus(QUuid entityID, QScriptValu
|
|||
auto client = DependencyManager::get<EntityScriptClient>();
|
||||
auto request = client->createScriptStatusRequest(entityID);
|
||||
connect(request, &GetScriptStatusRequest::finished, callback.engine(), [callback](GetScriptStatusRequest* request) mutable {
|
||||
QScriptValueList args { true };
|
||||
QString statusString;
|
||||
switch (request->getStatus()) {
|
||||
case RUNNING:
|
||||
statusString = "running";
|
||||
break;
|
||||
case ERROR_LOADING_SCRIPT:
|
||||
statusString = "error_loading_script";
|
||||
break;
|
||||
case ERROR_RUNNING_SCRIPT:
|
||||
statusString = "error_running_script";
|
||||
break;
|
||||
default:
|
||||
statusString = "";
|
||||
break;
|
||||
}
|
||||
QScriptValueList args { request->getResponseReceived(), request->getIsRunning(), statusString, request->getErrorInfo() };
|
||||
callback.call(QScriptValue(), args);
|
||||
request->deleteLater();
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "EntityScriptClient.h"
|
||||
#include "NodeList.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "EntityScriptUtils.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
|
@ -15,9 +16,12 @@ 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";
|
||||
client->getEntityServerScriptStatus(_entityID, [this](bool responseReceived, bool isRunning, EntityScriptStatus status, QString errorInfo) {
|
||||
_responseReceived = responseReceived;
|
||||
_isRunning = isRunning;
|
||||
_status = status;
|
||||
_errorInfo = errorInfo;
|
||||
|
||||
emit finished(this);
|
||||
});
|
||||
}
|
||||
|
@ -84,7 +88,7 @@ MessageID EntityScriptClient::getEntityServerScriptStatus(QUuid entityID, GetScr
|
|||
}
|
||||
}
|
||||
|
||||
callback(false);
|
||||
callback(false, false, ERROR_LOADING_SCRIPT, "");
|
||||
return INVALID_MESSAGE_ID;
|
||||
}
|
||||
|
||||
|
@ -92,9 +96,17 @@ void EntityScriptClient::handleGetScriptStatusReply(QSharedPointer<ReceivedMessa
|
|||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
MessageID messageID;
|
||||
message->readPrimitive(&messageID);
|
||||
bool isKnown { false };
|
||||
EntityScriptStatus status = ERROR_LOADING_SCRIPT;
|
||||
QString errorInfo { "" };
|
||||
|
||||
auto status = message->readString();
|
||||
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);
|
||||
|
@ -107,7 +119,7 @@ void EntityScriptClient::handleGetScriptStatusReply(QSharedPointer<ReceivedMessa
|
|||
auto requestIt = messageCallbackMap.find(messageID);
|
||||
if (requestIt != messageCallbackMap.end()) {
|
||||
auto callback = requestIt->second;
|
||||
callback(true);
|
||||
callback(true, isKnown, status, errorInfo);
|
||||
messageCallbackMap.erase(requestIt);
|
||||
}
|
||||
|
||||
|
@ -145,7 +157,7 @@ void EntityScriptClient::forceFailureOfPendingRequests(SharedNodePointer node) {
|
|||
auto messageMapIt = _pendingEntityScriptStatusRequests.find(node);
|
||||
if (messageMapIt != _pendingEntityScriptStatusRequests.end()) {
|
||||
for (const auto& value : messageMapIt->second) {
|
||||
value.second(false);
|
||||
value.second(false, false, ERROR_LOADING_SCRIPT, "");
|
||||
}
|
||||
messageMapIt->second.clear();
|
||||
}
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
#include "LimitedNodeList.h"
|
||||
#include "ReceivedMessage.h"
|
||||
#include "AssetUtils.h"
|
||||
#include "EntityScriptUtils.h"
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
using MessageID = uint32_t;
|
||||
|
||||
using GetScriptStatusCallback = std::function<void(bool responseReceived)>;
|
||||
using GetScriptStatusCallback = std::function<void(bool responseReceived, bool isRunning, EntityScriptStatus status, QString errorInfo)>;
|
||||
|
||||
class GetScriptStatusRequest : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -32,13 +32,22 @@ public:
|
|||
|
||||
Q_INVOKABLE void start();
|
||||
|
||||
bool getResponseReceived() const { return _responseReceived; }
|
||||
bool getIsRunning() const { return _isRunning; }
|
||||
EntityScriptStatus getStatus() const { return _status; }
|
||||
QString getErrorInfo() const { return _errorInfo; }
|
||||
|
||||
signals:
|
||||
void finished(GetScriptStatusRequest* request);
|
||||
|
||||
private:
|
||||
QUuid _entityID;
|
||||
MessageID _messageID;
|
||||
QString status;
|
||||
|
||||
bool _responseReceived;
|
||||
bool _isRunning;
|
||||
EntityScriptStatus _status;
|
||||
QString _errorInfo;
|
||||
};
|
||||
|
||||
class EntityScriptClient : public QObject, public Dependency {
|
||||
|
|
|
@ -1374,6 +1374,15 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin
|
|||
}
|
||||
}
|
||||
|
||||
bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const {
|
||||
auto it = _entityScripts.constFind(entityID);
|
||||
if (it == _entityScripts.constEnd()) {
|
||||
return false;
|
||||
}
|
||||
details = it.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
// since all of these operations can be asynch we will always do the actual work in the response handler
|
||||
// for the download
|
||||
void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
||||
|
@ -1421,11 +1430,24 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
|
||||
auto fileName = isURL ? scriptOrURL : "EmbeddedEntityScript";
|
||||
|
||||
EntityScriptDetails newDetails;
|
||||
newDetails.scriptText = scriptOrURL;
|
||||
|
||||
if (!success) {
|
||||
newDetails.status = ERROR_LOADING_SCRIPT;
|
||||
newDetails.errorInfo = "Failed to load script";
|
||||
_entityScripts[entityID] = newDetails;
|
||||
return;
|
||||
}
|
||||
|
||||
QScriptProgram program(contents, fileName);
|
||||
if (!hasCorrectSyntax(program, this)) {
|
||||
if (!isFileUrl) {
|
||||
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||
}
|
||||
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||
newDetails.errorInfo = "Bad syntax";
|
||||
_entityScripts[entityID] = newDetails;
|
||||
return; // done processing script
|
||||
}
|
||||
|
||||
|
@ -1451,6 +1473,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
testConstructor = sandbox.evaluate(program);
|
||||
}
|
||||
if (hadUncaughtExceptions(sandbox, program.fileName(), this)) {
|
||||
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||
newDetails.errorInfo = "Exception";
|
||||
_entityScripts[entityID] = newDetails;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1473,6 +1499,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||
}
|
||||
|
||||
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||
newDetails.errorInfo = "Could not find constructor";
|
||||
_entityScripts[entityID] = newDetails;
|
||||
|
||||
return; // done processing script
|
||||
}
|
||||
|
||||
|
@ -1489,8 +1519,11 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
};
|
||||
doWithEnvironment(entityID, sandboxURL, initialization);
|
||||
|
||||
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified, sandboxURL };
|
||||
newDetails.scriptObject = entityScriptObject;
|
||||
newDetails.lastModified = lastModified;
|
||||
newDetails.definingSandboxURL = sandboxURL;
|
||||
_entityScripts[entityID] = newDetails;
|
||||
|
||||
if (isURL) {
|
||||
setParentURL("");
|
||||
}
|
||||
|
@ -1516,7 +1549,9 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
|
|||
#endif
|
||||
|
||||
if (_entityScripts.contains(entityID)) {
|
||||
callEntityScriptMethod(entityID, "unload");
|
||||
if (_entityScripts[entityID].status == RUNNING) {
|
||||
callEntityScriptMethod(entityID, "unload");
|
||||
}
|
||||
_entityScripts.remove(entityID);
|
||||
stopAllTimersForEntityScript(entityID);
|
||||
}
|
||||
|
@ -1535,7 +1570,9 @@ void ScriptEngine::unloadAllEntityScripts() {
|
|||
qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
|
||||
#endif
|
||||
foreach(const EntityItemID& entityID, _entityScripts.keys()) {
|
||||
callEntityScriptMethod(entityID, "unload");
|
||||
if (_entityScripts[entityID].status == RUNNING) {
|
||||
callEntityScriptMethod(entityID, "unload");
|
||||
}
|
||||
}
|
||||
_entityScripts.clear();
|
||||
|
||||
|
@ -1574,7 +1611,7 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
|
|||
QString scriptContents = QTextStream(&file).readAll();
|
||||
this->unloadEntityScript(entityID);
|
||||
this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true);
|
||||
if (!_entityScripts.contains(entityID)) {
|
||||
if (!_entityScripts.contains(entityID) || _entityScripts[entityID].status != RUNNING) {
|
||||
scriptWarningMessage("Reload script " + details.scriptText + " failed");
|
||||
} else {
|
||||
details = _entityScripts[entityID];
|
||||
|
@ -1633,7 +1670,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
|||
#endif
|
||||
|
||||
refreshFileScript(entityID);
|
||||
if (_entityScripts.contains(entityID)) {
|
||||
if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
|
||||
EntityScriptDetails details = _entityScripts[entityID];
|
||||
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||
if (entityScript.property(methodName).isFunction()) {
|
||||
|
@ -1665,7 +1702,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
|||
#endif
|
||||
|
||||
refreshFileScript(entityID);
|
||||
if (_entityScripts.contains(entityID)) {
|
||||
if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
|
||||
EntityScriptDetails details = _entityScripts[entityID];
|
||||
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||
if (entityScript.property(methodName).isFunction()) {
|
||||
|
@ -1698,7 +1735,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
|||
#endif
|
||||
|
||||
refreshFileScript(entityID);
|
||||
if (_entityScripts.contains(entityID)) {
|
||||
if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
|
||||
EntityScriptDetails details = _entityScripts[entityID];
|
||||
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||
if (entityScript.property(methodName).isFunction()) {
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <LimitedNodeList.h>
|
||||
#include <EntityItemID.h>
|
||||
#include <EntitiesScriptEngineProvider.h>
|
||||
#include <EntityScriptUtils.h>
|
||||
|
||||
#include "PointerEvent.h"
|
||||
#include "ArrayBufferClass.h"
|
||||
|
@ -58,10 +59,15 @@ typedef QHash<QString, CallbackList> RegisteredEventHandlers;
|
|||
|
||||
class EntityScriptDetails {
|
||||
public:
|
||||
QString scriptText;
|
||||
QScriptValue scriptObject;
|
||||
int64_t lastModified;
|
||||
QUrl definingSandboxURL;
|
||||
EntityScriptStatus status { RUNNING };
|
||||
|
||||
// If status indicates an error, this contains a human-readable string giving more information about the error.
|
||||
QString errorInfo { "" };
|
||||
|
||||
QString scriptText { "" };
|
||||
QScriptValue scriptObject { QScriptValue() };
|
||||
int64_t lastModified { 0 };
|
||||
QUrl definingSandboxURL { QUrl() };
|
||||
};
|
||||
|
||||
class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider {
|
||||
|
@ -178,6 +184,8 @@ public:
|
|||
void scriptWarningMessage(const QString& message);
|
||||
void scriptInfoMessage(const QString& message);
|
||||
|
||||
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;
|
||||
|
||||
public slots:
|
||||
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
|
||||
void updateMemoryCost(const qint64&);
|
||||
|
@ -200,6 +208,28 @@ signals:
|
|||
void doneRunning();
|
||||
|
||||
protected:
|
||||
void init();
|
||||
|
||||
bool evaluatePending() const { return _evaluatesPending > 0; }
|
||||
void timerFired();
|
||||
void stopAllTimers();
|
||||
void stopAllTimersForEntityScript(const EntityItemID& entityID);
|
||||
void refreshFileScript(const EntityItemID& entityID);
|
||||
|
||||
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
|
||||
|
||||
QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
|
||||
void stopTimer(QTimer* timer);
|
||||
|
||||
QHash<EntityItemID, RegisteredEventHandlers> _registeredHandlers;
|
||||
void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs);
|
||||
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success);
|
||||
|
||||
EntityItemID currentEntityIdentifier {}; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution.
|
||||
QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty.
|
||||
void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation);
|
||||
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
|
||||
|
||||
QString _scriptContents;
|
||||
QString _parentURL;
|
||||
std::atomic<bool> _isFinished { false };
|
||||
|
@ -215,19 +245,6 @@ protected:
|
|||
bool _debuggable { false };
|
||||
qint64 _lastUpdate;
|
||||
|
||||
void init();
|
||||
|
||||
bool evaluatePending() const { return _evaluatesPending > 0; }
|
||||
void timerFired();
|
||||
void stopAllTimers();
|
||||
void stopAllTimersForEntityScript(const EntityItemID& entityID);
|
||||
void refreshFileScript(const EntityItemID& entityID);
|
||||
|
||||
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
|
||||
|
||||
QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
|
||||
void stopTimer(QTimer* timer);
|
||||
|
||||
QString _fileNameString;
|
||||
Quat _quatLibrary;
|
||||
Vec3 _vec3Library;
|
||||
|
@ -240,15 +257,6 @@ protected:
|
|||
|
||||
AssetScriptingInterface _assetScriptingInterface{ this };
|
||||
|
||||
QHash<EntityItemID, RegisteredEventHandlers> _registeredHandlers;
|
||||
void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs);
|
||||
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success);
|
||||
|
||||
EntityItemID currentEntityIdentifier {}; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution.
|
||||
QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty.
|
||||
void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation);
|
||||
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
|
||||
|
||||
std::function<bool()> _emitScriptUpdates{ [](){ return true; } };
|
||||
|
||||
std::recursive_mutex _lock;
|
||||
|
|
|
@ -1388,6 +1388,37 @@ function pushCommandForSelections(createdEntityData, deletedEntityData) {
|
|||
|
||||
var ENTITY_PROPERTIES_URL = Script.resolvePath('html/entityProperties.html');
|
||||
|
||||
var ServerScriptStatusMonitor = function(entityID, statusCallback) {
|
||||
var self = this;
|
||||
|
||||
self.entityID = entityID;
|
||||
self.active = true;
|
||||
self.sendRequestTimerID = null;
|
||||
|
||||
var onStatusReceived = function(success, isRunning, status, errorInfo) {
|
||||
print("Got script status:", success, isRunning, status, errorInfo);
|
||||
if (self.active) {
|
||||
print("Requesting status of script");
|
||||
statusCallback({
|
||||
statusRetrieved: success,
|
||||
isRunning: isRunning,
|
||||
status: status,
|
||||
errorInfo: errorInfo
|
||||
});
|
||||
self.sendRequestTimerID = Script.setTimeout(function() {
|
||||
if (self.active) {
|
||||
Entities.getServerScriptStatus(entityID, onStatusReceived);
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
};
|
||||
self.stop = function() {
|
||||
self.active = false;
|
||||
}
|
||||
|
||||
Entities.getServerScriptStatus(entityID, onStatusReceived);
|
||||
};
|
||||
|
||||
var PropertiesTool = function (opts) {
|
||||
var that = {};
|
||||
|
||||
|
@ -1399,6 +1430,11 @@ var PropertiesTool = function (opts) {
|
|||
|
||||
var visible = false;
|
||||
|
||||
// This keeps track of the last entity ID that was selected. If multiple entities
|
||||
// are selected or if no entity is selected this will be `null`.
|
||||
var currentSelectedEntityID = null;
|
||||
var statusMonitor = null;
|
||||
|
||||
webView.setVisible(visible);
|
||||
|
||||
that.setVisible = function (newVisible) {
|
||||
|
@ -1406,10 +1442,43 @@ var PropertiesTool = function (opts) {
|
|||
webView.setVisible(visible);
|
||||
};
|
||||
|
||||
function updateScriptStatus(info) {
|
||||
print("Got status: ", info);
|
||||
info.type = "server_script_status";
|
||||
webView.emitScriptEvent(JSON.stringify(info));
|
||||
};
|
||||
|
||||
function resetScriptStatus() {
|
||||
updateScriptStatus({
|
||||
statusRetrieved: false,
|
||||
isRunning: false,
|
||||
status: "",
|
||||
errorInfo: ""
|
||||
});
|
||||
}
|
||||
|
||||
selectionManager.addEventListener(function () {
|
||||
var data = {
|
||||
type: 'update'
|
||||
};
|
||||
|
||||
resetScriptStatus();
|
||||
|
||||
if (selectionManager.selections.length !== 1) {
|
||||
if (statusMonitor !== null) {
|
||||
statusMonitor.stop();
|
||||
statusMonitor = null;
|
||||
}
|
||||
currentSelectedEntityID = null;
|
||||
} else if (currentSelectedEntityID != selectionManager.selections[0]) {
|
||||
if (statusMonitor !== null) {
|
||||
statusMonitor.stop();
|
||||
}
|
||||
var entityID = selectionManager.selections[0];
|
||||
currentSelectedEntityID = entityID;
|
||||
statusMonitor = new ServerScriptStatusMonitor(entityID, updateScriptStatus);
|
||||
}
|
||||
|
||||
var selections = [];
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
var entity = {};
|
||||
|
|
|
@ -319,10 +319,17 @@
|
|||
<input type="button" id="reload-script-button" class="glyph" value="F">
|
||||
</div>
|
||||
<div class="behavior-group property url refresh">
|
||||
<label for="property-server-scripts">Server Script URLs</label>
|
||||
<label for="property-server-scripts">Server Script URL</label>
|
||||
<input type="text" id="property-server-scripts">
|
||||
<input type="button" id="reload-server-scripts-button" class="glyph" value="F">
|
||||
</div>
|
||||
<div class="behavior-group property">
|
||||
<label for="server-script-status">Server Script Status</label>
|
||||
<span id="server-script-status"></span>
|
||||
</div>
|
||||
<div class="behavior-group property">
|
||||
<textarea id="server-script-error"></textarea>
|
||||
</div>
|
||||
<div class="section-header model-group model-section zone-section">
|
||||
<label>Model</label><span>M</span>
|
||||
</div>
|
||||
|
|
|
@ -593,6 +593,8 @@ function loaded() {
|
|||
var elReloadScriptsButton = document.getElementById("reload-script-button");
|
||||
var elServerScripts = document.getElementById("property-server-scripts");
|
||||
var elReloadServerScriptsButton = document.getElementById("reload-server-scripts-button");
|
||||
var elServerScriptStatus = document.getElementById("server-script-status");
|
||||
var elServerScriptError = document.getElementById("server-script-error");
|
||||
var elUserData = document.getElementById("property-user-data");
|
||||
var elClearUserData = document.getElementById("userdata-clear");
|
||||
var elSaveUserData = document.getElementById("userdata-save");
|
||||
|
@ -710,7 +712,27 @@ function loaded() {
|
|||
var properties;
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
if (data.type == "update") {
|
||||
if (data.type == "server_script_status") {
|
||||
if (!data.statusRetrieved) {
|
||||
elServerScriptStatus.innerHTML = "Failed to retrieve status";
|
||||
elServerScriptError.style.display = "none";
|
||||
} else if (data.isRunning) {
|
||||
if (data.status == "running") {
|
||||
elServerScriptStatus.innerHTML = "Running";
|
||||
elServerScriptError.style.display = "none";
|
||||
} else if (data.status == "error_loading_script") {
|
||||
elServerScriptStatus.innerHTML = "Error loading script";
|
||||
elServerScriptError.style.display = "block";
|
||||
} else if (data.status == "error_running_script") {
|
||||
elServerScriptStatus.innerHTML = "Error running script";
|
||||
elServerScriptError.style.display = "block";
|
||||
}
|
||||
elServerScriptError.innerHTML = data.errorInfo;;
|
||||
} else {
|
||||
elServerScriptStatus.innerHTML = "Not running";
|
||||
elServerScriptError.style.display = "none";
|
||||
}
|
||||
} else if (data.type == "update") {
|
||||
|
||||
if (data.selections.length == 0) {
|
||||
if (editor !== null && lastEntityID !== null) {
|
||||
|
|
Loading…
Reference in a new issue