Merge pull request #6021 from jherico/homer

Support reload-on-change for local scripts
This commit is contained in:
Brad Hefta-Gaub 2015-10-07 20:34:15 -07:00
commit 65473cadcb
2 changed files with 57 additions and 6 deletions

View file

@ -11,12 +11,13 @@
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QEventLoop> #include <QtCore/QEventLoop>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QScriptEngine> #include <QtScript/QScriptEngine>
#include <QScriptValue> #include <QtScript/QScriptValue>
#include <AudioConstants.h> #include <AudioConstants.h>
#include <AudioEffectOptions.h> #include <AudioEffectOptions.h>
@ -942,6 +943,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
#endif #endif
auto scriptCache = DependencyManager::get<ScriptCache>(); auto scriptCache = DependencyManager::get<ScriptCache>();
bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
// first check the syntax of the script contents // first check the syntax of the script contents
QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents); QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents);
@ -950,7 +952,9 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
qCDebug(scriptengine) << " " << syntaxCheck.errorMessage() << ":" qCDebug(scriptengine) << " " << syntaxCheck.errorMessage() << ":"
<< syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber(); << syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();
qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL; qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
if (!isFileUrl) {
scriptCache->addScriptToBadScriptList(scriptOrURL); scriptCache->addScriptToBadScriptList(scriptOrURL);
}
return; // done processing script return; // done processing script
} }
@ -965,14 +969,21 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID; qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
qCDebug(scriptengine) << " NOT CONSTRUCTOR"; qCDebug(scriptengine) << " NOT CONSTRUCTOR";
qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL; qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
if (!isFileUrl) {
scriptCache->addScriptToBadScriptList(scriptOrURL); scriptCache->addScriptToBadScriptList(scriptOrURL);
}
return; // done processing script return; // done processing script
} }
QScriptValue entityScriptConstructor = evaluate(contents); int64_t lastModified = 0;
if (isFileUrl) {
QString file = QUrl(scriptOrURL).toLocalFile();
lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();
}
QScriptValue entityScriptConstructor = evaluate(contents);
QScriptValue entityScriptObject = entityScriptConstructor.construct(); QScriptValue entityScriptObject = entityScriptConstructor.construct();
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject }; EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };
_entityScripts[entityID] = newDetails; _entityScripts[entityID] = newDetails;
if (isURL) { if (isURL) {
setParentURL(""); setParentURL("");
@ -1022,6 +1033,41 @@ void ScriptEngine::unloadAllEntityScripts() {
_entityScripts.clear(); _entityScripts.clear();
} }
void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
if (!_entityScripts.contains(entityID)) {
return;
}
static bool recurseGuard = false;
if (recurseGuard) {
return;
}
recurseGuard = true;
EntityScriptDetails details = _entityScripts[entityID];
// Check to see if a file based script needs to be reloaded (easier debugging)
if (details.lastModified > 0) {
QString filePath = QUrl(details.scriptText).toLocalFile();
auto lastModified = QFileInfo(filePath).lastModified().toMSecsSinceEpoch();
if (lastModified > details.lastModified) {
qDebug() << "Reloading modified script " << details.scriptText;
QFile file(filePath);
file.open(QIODevice::ReadOnly);
QString scriptContents = QTextStream(&file).readAll();
this->unloadEntityScript(entityID);
this->entityScriptContentAvailable(entityID, details.scriptText, scriptContents, true, true);
if (!_entityScripts.contains(entityID)) {
qWarning() << "Reload script " << details.scriptText << " failed";
} else {
details = _entityScripts[entityID];
}
}
}
recurseGuard = false;
}
void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName) { void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
@ -1039,6 +1085,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
"entityID:" << entityID << "methodName:" << methodName; "entityID:" << entityID << "methodName:" << methodName;
#endif #endif
refreshFileScript(entityID);
if (_entityScripts.contains(entityID)) { if (_entityScripts.contains(entityID)) {
EntityScriptDetails details = _entityScripts[entityID]; EntityScriptDetails details = _entityScripts[entityID];
QScriptValue entityScript = details.scriptObject; // previously loaded QScriptValue entityScript = details.scriptObject; // previously loaded
@ -1069,6 +1116,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
"entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent"; "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";
#endif #endif
refreshFileScript(entityID);
if (_entityScripts.contains(entityID)) { if (_entityScripts.contains(entityID)) {
EntityScriptDetails details = _entityScripts[entityID]; EntityScriptDetails details = _entityScripts[entityID];
QScriptValue entityScript = details.scriptObject; // previously loaded QScriptValue entityScript = details.scriptObject; // previously loaded
@ -1101,6 +1149,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
"entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
#endif #endif
refreshFileScript(entityID);
if (_entityScripts.contains(entityID)) { if (_entityScripts.contains(entityID)) {
EntityScriptDetails details = _entityScripts[entityID]; EntityScriptDetails details = _entityScripts[entityID];
QScriptValue entityScript = details.scriptObject; // previously loaded QScriptValue entityScript = details.scriptObject; // previously loaded

View file

@ -45,6 +45,7 @@ class EntityScriptDetails {
public: public:
QString scriptText; QString scriptText;
QScriptValue scriptObject; QScriptValue scriptObject;
int64_t lastModified;
}; };
class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider { class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider {
@ -171,6 +172,7 @@ private:
bool evaluatePending() const { return _evaluatesPending > 0; } bool evaluatePending() const { return _evaluatesPending > 0; }
void timerFired(); void timerFired();
void stopAllTimers(); void stopAllTimers();
void refreshFileScript(const EntityItemID& entityID);
void setParentURL(const QString& parentURL) { _parentURL = parentURL; } void setParentURL(const QString& parentURL) { _parentURL = parentURL; }