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/QEventLoop>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>
#include <QtCore/QThread>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QScriptEngine>
#include <QScriptValue>
#include <QtScript/QScriptEngine>
#include <QtScript/QScriptValue>
#include <AudioConstants.h>
#include <AudioEffectOptions.h>
@ -942,6 +943,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
#endif
auto scriptCache = DependencyManager::get<ScriptCache>();
bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
// first check the syntax of the script contents
QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents);
@ -950,7 +952,9 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
qCDebug(scriptengine) << " " << syntaxCheck.errorMessage() << ":"
<< syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();
qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
scriptCache->addScriptToBadScriptList(scriptOrURL);
if (!isFileUrl) {
scriptCache->addScriptToBadScriptList(scriptOrURL);
}
return; // done processing script
}
@ -965,14 +969,21 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID;
qCDebug(scriptengine) << " NOT CONSTRUCTOR";
qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL;
scriptCache->addScriptToBadScriptList(scriptOrURL);
if (!isFileUrl) {
scriptCache->addScriptToBadScriptList(scriptOrURL);
}
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();
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject };
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };
_entityScripts[entityID] = newDetails;
if (isURL) {
setParentURL("");
@ -1022,6 +1033,41 @@ void ScriptEngine::unloadAllEntityScripts() {
_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) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
@ -1039,6 +1085,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
"entityID:" << entityID << "methodName:" << methodName;
#endif
refreshFileScript(entityID);
if (_entityScripts.contains(entityID)) {
EntityScriptDetails details = _entityScripts[entityID];
QScriptValue entityScript = details.scriptObject; // previously loaded
@ -1069,6 +1116,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
"entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";
#endif
refreshFileScript(entityID);
if (_entityScripts.contains(entityID)) {
EntityScriptDetails details = _entityScripts[entityID];
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";
#endif
refreshFileScript(entityID);
if (_entityScripts.contains(entityID)) {
EntityScriptDetails details = _entityScripts[entityID];
QScriptValue entityScript = details.scriptObject; // previously loaded

View file

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