mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 17:24:24 +02:00
Update ScriptEngine to store full running status off entity scripts
This commit is contained in:
parent
89d1242925
commit
cbb55a06a0
3 changed files with 93 additions and 42 deletions
|
@ -11,8 +11,11 @@
|
||||||
|
|
||||||
#include "EntityScriptServer.h"
|
#include "EntityScriptServer.h"
|
||||||
|
|
||||||
|
#include "EntityScriptUtils.h"
|
||||||
|
|
||||||
#include <AudioConstants.h>
|
#include <AudioConstants.h>
|
||||||
#include <AudioInjectorManager.h>
|
#include <AudioInjectorManager.h>
|
||||||
|
#include <src/entities/AssignmentParentFinder.h>
|
||||||
#include <EntityScriptingInterface.h>
|
#include <EntityScriptingInterface.h>
|
||||||
#include <MessagesClient.h>
|
#include <MessagesClient.h>
|
||||||
#include <plugins/CodecPlugin.h>
|
#include <plugins/CodecPlugin.h>
|
||||||
|
@ -23,8 +26,6 @@
|
||||||
#include <SoundCache.h>
|
#include <SoundCache.h>
|
||||||
#include <WebSocketServerClass.h>
|
#include <WebSocketServerClass.h>
|
||||||
|
|
||||||
#include "../entities/AssignmentParentFinder.h"
|
|
||||||
|
|
||||||
const size_t UUID_LENGTH_BYTES = 16;
|
const size_t UUID_LENGTH_BYTES = 16;
|
||||||
|
|
||||||
int EntityScriptServer::_entitiesScriptEngineCount = 0;
|
int EntityScriptServer::_entitiesScriptEngineCount = 0;
|
||||||
|
@ -75,16 +76,21 @@ void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<Receiv
|
||||||
MessageID messageID;
|
MessageID messageID;
|
||||||
message->readPrimitive(&messageID);
|
message->readPrimitive(&messageID);
|
||||||
auto entityID = QUuid::fromRfc4122(message->read(UUID_LENGTH_BYTES));
|
auto entityID = QUuid::fromRfc4122(message->read(UUID_LENGTH_BYTES));
|
||||||
|
|
||||||
// TODO(Huffman) Get Status
|
|
||||||
|
|
||||||
qDebug() << "Getting script status of: " << entityID;
|
auto replyPacketList = NLPacketList::create(PacketType::EntityScriptGetStatusReply, QByteArray(), true, true);
|
||||||
auto replyPacket = NLPacket::create(PacketType::EntityScriptGetStatusReply, -1, true);
|
replyPacketList->writePrimitive(messageID);
|
||||||
replyPacket->writePrimitive(messageID);
|
|
||||||
replyPacket->writeString("running");
|
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>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
nodeList->sendPacket(std::move(replyPacket), *senderNode);
|
nodeList->sendPacketList(std::move(replyPacketList), *senderNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityScriptServer::run() {
|
void EntityScriptServer::run() {
|
||||||
|
|
|
@ -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
|
// since all of these operations can be asynch we will always do the actual work in the response handler
|
||||||
// for the download
|
// for the download
|
||||||
void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
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://");
|
bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
|
||||||
auto fileName = isURL ? scriptOrURL : "EmbeddedEntityScript";
|
auto fileName = isURL ? scriptOrURL : "EmbeddedEntityScript";
|
||||||
|
|
||||||
|
EntityScriptDetails newDetails;
|
||||||
|
newDetails.scriptText = isURL ? contents : scriptOrURL;
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
newDetails.status = ERROR_LOADING_SCRIPT;
|
||||||
|
newDetails.errorInfo = "Failed to load script";
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QScriptProgram program(contents, fileName);
|
QScriptProgram program(contents, fileName);
|
||||||
if (!hasCorrectSyntax(program, this)) {
|
if (!hasCorrectSyntax(program, this)) {
|
||||||
if (!isFileUrl) {
|
if (!isFileUrl) {
|
||||||
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||||
}
|
}
|
||||||
|
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||||
|
newDetails.errorInfo = "Bad syntax";
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
return; // done processing script
|
return; // done processing script
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1451,6 +1473,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
||||||
testConstructor = sandbox.evaluate(program);
|
testConstructor = sandbox.evaluate(program);
|
||||||
}
|
}
|
||||||
if (hadUncaughtExceptions(sandbox, program.fileName(), this)) {
|
if (hadUncaughtExceptions(sandbox, program.fileName(), this)) {
|
||||||
|
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||||
|
newDetails.errorInfo = "Exception";
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1473,6 +1499,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
||||||
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newDetails.status = ERROR_RUNNING_SCRIPT;
|
||||||
|
newDetails.errorInfo = "Could not find constructor";
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
|
|
||||||
return; // done processing script
|
return; // done processing script
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1489,8 +1519,11 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
||||||
};
|
};
|
||||||
doWithEnvironment(entityID, sandboxURL, initialization);
|
doWithEnvironment(entityID, sandboxURL, initialization);
|
||||||
|
|
||||||
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified, sandboxURL };
|
newDetails.scriptObject = entityScriptObject;
|
||||||
|
newDetails.lastModified = lastModified;
|
||||||
|
newDetails.definingSandboxURL = sandboxURL;
|
||||||
_entityScripts[entityID] = newDetails;
|
_entityScripts[entityID] = newDetails;
|
||||||
|
|
||||||
if (isURL) {
|
if (isURL) {
|
||||||
setParentURL("");
|
setParentURL("");
|
||||||
}
|
}
|
||||||
|
@ -1516,7 +1549,9 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_entityScripts.contains(entityID)) {
|
if (_entityScripts.contains(entityID)) {
|
||||||
callEntityScriptMethod(entityID, "unload");
|
if (_entityScripts[entityID].status == RUNNING) {
|
||||||
|
callEntityScriptMethod(entityID, "unload");
|
||||||
|
}
|
||||||
_entityScripts.remove(entityID);
|
_entityScripts.remove(entityID);
|
||||||
stopAllTimersForEntityScript(entityID);
|
stopAllTimersForEntityScript(entityID);
|
||||||
}
|
}
|
||||||
|
@ -1535,7 +1570,9 @@ void ScriptEngine::unloadAllEntityScripts() {
|
||||||
qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
|
qCDebug(scriptengine) << "ScriptEngine::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
|
||||||
#endif
|
#endif
|
||||||
foreach(const EntityItemID& entityID, _entityScripts.keys()) {
|
foreach(const EntityItemID& entityID, _entityScripts.keys()) {
|
||||||
callEntityScriptMethod(entityID, "unload");
|
if (_entityScripts[entityID].status == RUNNING) {
|
||||||
|
callEntityScriptMethod(entityID, "unload");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_entityScripts.clear();
|
_entityScripts.clear();
|
||||||
|
|
||||||
|
@ -1574,7 +1611,7 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
|
||||||
QString scriptContents = QTextStream(&file).readAll();
|
QString scriptContents = QTextStream(&file).readAll();
|
||||||
this->unloadEntityScript(entityID);
|
this->unloadEntityScript(entityID);
|
||||||
this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true);
|
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");
|
scriptWarningMessage("Reload script " + details.scriptText + " failed");
|
||||||
} else {
|
} else {
|
||||||
details = _entityScripts[entityID];
|
details = _entityScripts[entityID];
|
||||||
|
@ -1633,7 +1670,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
refreshFileScript(entityID);
|
refreshFileScript(entityID);
|
||||||
if (_entityScripts.contains(entityID)) {
|
if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
|
||||||
EntityScriptDetails details = _entityScripts[entityID];
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
QScriptValue entityScript = details.scriptObject; // previously loaded
|
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||||
if (entityScript.property(methodName).isFunction()) {
|
if (entityScript.property(methodName).isFunction()) {
|
||||||
|
@ -1665,7 +1702,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
refreshFileScript(entityID);
|
refreshFileScript(entityID);
|
||||||
if (_entityScripts.contains(entityID)) {
|
if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
|
||||||
EntityScriptDetails details = _entityScripts[entityID];
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
QScriptValue entityScript = details.scriptObject; // previously loaded
|
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||||
if (entityScript.property(methodName).isFunction()) {
|
if (entityScript.property(methodName).isFunction()) {
|
||||||
|
@ -1698,7 +1735,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
refreshFileScript(entityID);
|
refreshFileScript(entityID);
|
||||||
if (_entityScripts.contains(entityID)) {
|
if (_entityScripts.contains(entityID) && _entityScripts[entityID].status == RUNNING) {
|
||||||
EntityScriptDetails details = _entityScripts[entityID];
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
QScriptValue entityScript = details.scriptObject; // previously loaded
|
QScriptValue entityScript = details.scriptObject; // previously loaded
|
||||||
if (entityScript.property(methodName).isFunction()) {
|
if (entityScript.property(methodName).isFunction()) {
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <LimitedNodeList.h>
|
#include <LimitedNodeList.h>
|
||||||
#include <EntityItemID.h>
|
#include <EntityItemID.h>
|
||||||
#include <EntitiesScriptEngineProvider.h>
|
#include <EntitiesScriptEngineProvider.h>
|
||||||
|
#include <EntityScriptUtils.h>
|
||||||
|
|
||||||
#include "PointerEvent.h"
|
#include "PointerEvent.h"
|
||||||
#include "ArrayBufferClass.h"
|
#include "ArrayBufferClass.h"
|
||||||
|
@ -58,10 +59,15 @@ typedef QHash<QString, CallbackList> RegisteredEventHandlers;
|
||||||
|
|
||||||
class EntityScriptDetails {
|
class EntityScriptDetails {
|
||||||
public:
|
public:
|
||||||
QString scriptText;
|
EntityScriptStatus status { RUNNING };
|
||||||
QScriptValue scriptObject;
|
|
||||||
int64_t lastModified;
|
// If status indicates an error, this contains a human-readable string giving more information about the error.
|
||||||
QUrl definingSandboxURL;
|
QString errorInfo { "" };
|
||||||
|
|
||||||
|
QString scriptText { "" };
|
||||||
|
QScriptValue scriptObject { QScriptValue() };
|
||||||
|
int64_t lastModified { 0 };
|
||||||
|
QUrl definingSandboxURL { QUrl() };
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider {
|
class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider {
|
||||||
|
@ -178,6 +184,8 @@ public:
|
||||||
void scriptWarningMessage(const QString& message);
|
void scriptWarningMessage(const QString& message);
|
||||||
void scriptInfoMessage(const QString& message);
|
void scriptInfoMessage(const QString& message);
|
||||||
|
|
||||||
|
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
|
void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
|
||||||
void updateMemoryCost(const qint64&);
|
void updateMemoryCost(const qint64&);
|
||||||
|
@ -200,6 +208,28 @@ signals:
|
||||||
void doneRunning();
|
void doneRunning();
|
||||||
|
|
||||||
protected:
|
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 _scriptContents;
|
||||||
QString _parentURL;
|
QString _parentURL;
|
||||||
std::atomic<bool> _isFinished { false };
|
std::atomic<bool> _isFinished { false };
|
||||||
|
@ -215,19 +245,6 @@ protected:
|
||||||
bool _debuggable { false };
|
bool _debuggable { false };
|
||||||
qint64 _lastUpdate;
|
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;
|
QString _fileNameString;
|
||||||
Quat _quatLibrary;
|
Quat _quatLibrary;
|
||||||
Vec3 _vec3Library;
|
Vec3 _vec3Library;
|
||||||
|
@ -240,15 +257,6 @@ protected:
|
||||||
|
|
||||||
AssetScriptingInterface _assetScriptingInterface{ this };
|
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::function<bool()> _emitScriptUpdates{ [](){ return true; } };
|
||||||
|
|
||||||
std::recursive_mutex _lock;
|
std::recursive_mutex _lock;
|
||||||
|
|
Loading…
Reference in a new issue