History

resolve conflicts on merge with upstream/master

Stephen Birarda committed 10 years ago

Commit 4573e5c2 parents a7dfc99 85b8266325 files · +10929 −5218Browse files

Showing the changes to libraries/script-engine/src/ScriptEngine.cpp (1 of 325 files). Show all

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+159−144
@@ -24,6 +24,7 @@ · 23 unmodified lines
24#include <AudioEffectOptions.h>24#include <AudioEffectOptions.h>
25#include <AvatarData.h>25#include <AvatarData.h>
26#include <EntityScriptingInterface.h>26#include <EntityScriptingInterface.h>
27#include <MessagesClient.h>
27#include <NetworkAccessManager.h>28#include <NetworkAccessManager.h>
28#include <NodeList.h>29#include <NodeList.h>
29#include <udt/PacketHeaders.h>30#include <udt/PacketHeaders.h>
@@ -46,6 +47,7 @@ · 16 unmodified lines
46#include "WebSocketClass.h"47#include "WebSocketClass.h"
47 48
48#include "SceneScriptingInterface.h"49#include "SceneScriptingInterface.h"
50#include "RecordingScriptingInterface.h"
49 51
50#include "MIDIEvent.h"52#include "MIDIEvent.h"
51 53
@@ -60,14 +62,14 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ · 8 unmodified lines
60 }62 }
61 message += context->argument(i).toString();63 message += context->argument(i).toString();
62 }64 }
63 qCDebug(scriptengine) << "script:print()<<" << message;65 qCDebug(scriptengine).noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline
64 66
65 message = message.replace("\\", "\\\\")67 message = message.replace("\\", "\\\\")
66 .replace("\n", "\\n")68 .replace("\n", "\\n")
67 .replace("\r", "\\r")69 .replace("\r", "\\r")
68 .replace("'", "\\'");70 .replace("'", "\\'");
69 engine->evaluate("Script.print('" + message + "')");71 engine->evaluate("Script.print('" + message + "')");
70 72
71 return QScriptValue();73 return QScriptValue();
72}74}
73 75
@@ -108,7 +110,7 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName · 34 unmodified lines
108 const auto exception = engine.uncaughtException().toString();110 const auto exception = engine.uncaughtException().toString();
109 const auto line = QString::number(engine.uncaughtExceptionLineNumber());111 const auto line = QString::number(engine.uncaughtExceptionLineNumber());
110 engine.clearExceptions();112 engine.clearExceptions();
111 113
112 auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);114 auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);
113 if (!backtrace.empty()) {115 if (!backtrace.empty()) {
114 static const auto lineSeparator = "\n ";116 static const auto lineSeparator = "\n ";
@@ -121,16 +123,11 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName · 6 unmodified lines
121}123}
122 124
123ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) :125ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) :
124 _scriptContents(scriptContents),126_scriptContents(scriptContents),
125 _isFinished(false),127_timerFunctionMap(),
126 _isRunning(false),128_wantSignals(wantSignals),
127 _isInitialized(false),129_fileNameString(fileNameString),
128 _timerFunctionMap(),130_arrayBufferClass(new ArrayBufferClass(this))
129 _wantSignals(wantSignals),
130 _fileNameString(fileNameString),
131 _isUserLoaded(false),
132 _isReloading(false),
133 _arrayBufferClass(new ArrayBufferClass(this))
134{131{
135 _allScriptsMutex.lock();132 _allScriptsMutex.lock();
136 _allKnownScriptEngines.insert(this);133 _allKnownScriptEngines.insert(this);
@@ -138,6 +135,8 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam · 1 unmodified line
138}135}
139 136
140ScriptEngine::~ScriptEngine() {137ScriptEngine::~ScriptEngine() {
138 qCDebug(scriptengine) << "Script Engine shutting down (destructor) for script:" << getFilename();
139
141 // If we're not already in the middle of stopping all scripts, then we should remove ourselves140 // If we're not already in the middle of stopping all scripts, then we should remove ourselves
142 // from the list of running scripts. We don't do this if we're in the process of stopping all scripts141 // from the list of running scripts. We don't do this if we're in the process of stopping all scripts
143 // because that method removes scripts from its list as it iterates them142 // because that method removes scripts from its list as it iterates them
@@ -148,25 +147,35 @@ ScriptEngine::~ScriptEngine() { · 4 unmodified lines
148 }147 }
149}148}
150 149
150void ScriptEngine::disconnectNonEssentialSignals() {
151 disconnect();
152 connect(this, &ScriptEngine::doneRunning, thread(), &QThread::quit);
153}
154
151void ScriptEngine::runInThread() {155void ScriptEngine::runInThread() {
156 _isThreaded = true;
152 QThread* workerThread = new QThread(); // thread is not owned, so we need to manage the delete157 QThread* workerThread = new QThread(); // thread is not owned, so we need to manage the delete
153 QString scriptEngineName = QString("Script Thread:") + getFilename();158 QString scriptEngineName = QString("Script Thread:") + getFilename();
154 workerThread->setObjectName(scriptEngineName);159 workerThread->setObjectName(scriptEngineName);
155 160
161 // NOTE: If you connect any essential signals for proper shutdown or cleanup of
162 // the script engine, make sure to add code to "reconnect" them to the
163 // disconnectNonEssentialSignals() method
164
156 // when the worker thread is started, call our engine's run..165 // when the worker thread is started, call our engine's run..
157 connect(workerThread, &QThread::started, this, &ScriptEngine::run);166 connect(workerThread, &QThread::started, this, &ScriptEngine::run);
158 167
159 // tell the thread to stop when the script engine is done168 // tell the thread to stop when the script engine is done
160 connect(this, &ScriptEngine::doneRunning, workerThread, &QThread::quit);169 connect(this, &ScriptEngine::doneRunning, workerThread, &QThread::quit);
161 170
162 // when the thread is finished, add thread to the deleteLater queue171 // when the thread is finished, add thread to the deleteLater queue
163 connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);172 connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
164 173
165 // when the thread is finished, add scriptEngine to the deleteLater queue174 // when the thread is finished, add scriptEngine to the deleteLater queue
166 connect(workerThread, &QThread::finished, this, &ScriptEngine::deleteLater);175 connect(workerThread, &QThread::finished, this, &ScriptEngine::deleteLater);
167 176
168 moveToThread(workerThread);177 moveToThread(workerThread);
169 178
170 // Starts an event loop, and emits workerThread->started()179 // Starts an event loop, and emits workerThread->started()
171 workerThread->start();180 workerThread->start();
172}181}
@@ -174,28 +183,29 @@ void ScriptEngine::runInThread() { · 1 unmodified line
174QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;183QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;
175QMutex ScriptEngine::_allScriptsMutex;184QMutex ScriptEngine::_allScriptsMutex;
176bool ScriptEngine::_stoppingAllScripts = false;185bool ScriptEngine::_stoppingAllScripts = false;
177bool ScriptEngine::_doneRunningThisScript = false;
178 186
179void ScriptEngine::stopAllScripts(QObject* application) {187void ScriptEngine::stopAllScripts(QObject* application) {
180 _allScriptsMutex.lock();188 _allScriptsMutex.lock();
181 _stoppingAllScripts = true;189 _stoppingAllScripts = true;
182 190
191 qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size();
192
183 QMutableSetIterator<ScriptEngine*> i(_allKnownScriptEngines);193 QMutableSetIterator<ScriptEngine*> i(_allKnownScriptEngines);
184 while (i.hasNext()) {194 while (i.hasNext()) {
185 ScriptEngine* scriptEngine = i.next();195 ScriptEngine* scriptEngine = i.next();
186 196
187 QString scriptName = scriptEngine->getFilename();197 QString scriptName = scriptEngine->getFilename();
188 198
189 // NOTE: typically all script engines are running. But there's at least one known exception to this, the199 // NOTE: typically all script engines are running. But there's at least one known exception to this, the
190 // "entities sandbox" which is only used to evaluate entities scripts to test their validity before using200 // "entities sandbox" which is only used to evaluate entities scripts to test their validity before using
191 // them. We don't need to stop scripts that aren't running.201 // them. We don't need to stop scripts that aren't running.
192 if (scriptEngine->isRunning()) {202 if (scriptEngine->isRunning()) {
193 203
194 // If the script is running, but still evaluating then we need to wait for its evaluation step to204 // If the script is running, but still evaluating then we need to wait for its evaluation step to
195 // complete. After that we can handle the stop process appropriately205 // complete. After that we can handle the stop process appropriately
196 if (scriptEngine->evaluatePending()) {206 if (scriptEngine->evaluatePending()) {
197 while (scriptEngine->evaluatePending()) {207 while (scriptEngine->evaluatePending()) {
198 208
199 // This event loop allows any started, but not yet finished evaluate() calls to complete209 // This event loop allows any started, but not yet finished evaluate() calls to complete
200 // we need to let these complete so that we can be guaranteed that the script engine isn't210 // we need to let these complete so that we can be guaranteed that the script engine isn't
201 // in a partially setup state, which can confuse our shutdown unwinding.211 // in a partially setup state, which can confuse our shutdown unwinding.
@@ -204,42 +214,42 @@ void ScriptEngine::stopAllScripts(QObject* application) { · 2 unmodified lines
204 loop.exec();214 loop.exec();
205 }215 }
206 }216 }
207 217
208 // We disconnect any script engine signals from the application because we don't want to do any218 // We disconnect any script engine signals from the application because we don't want to do any
209 // extra stopScript/loadScript processing that the Application normally does when scripts start219 // extra stopScript/loadScript processing that the Application normally does when scripts start
210 // and stop. We can safely short circuit this because we know we're in the "quitting" process220 // and stop. We can safely short circuit this because we know we're in the "quitting" process
211 scriptEngine->disconnect(application);221 scriptEngine->disconnect(application);
212 222
213 // Calling stop on the script engine will set it's internal _isFinished state to true, and result223 // Calling stop on the script engine will set it's internal _isFinished state to true, and result
214 // in the ScriptEngine gracefully ending it's run() method.224 // in the ScriptEngine gracefully ending it's run() method.
215 scriptEngine->stop();225 scriptEngine->stop();
216 226
217 // We need to wait for the engine to be done running before we proceed, because we don't227 // We need to wait for the engine to be done running before we proceed, because we don't
218 // want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing228 // want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing
219 // any application state after we leave this stopAllScripts() method229 // any application state after we leave this stopAllScripts() method
230 qCDebug(scriptengine) << "waiting on script:" << scriptName;
220 scriptEngine->waitTillDoneRunning();231 scriptEngine->waitTillDoneRunning();
221 232 qCDebug(scriptengine) << "done waiting on script:" << scriptName;
233
222 // If the script is stopped, we can remove it from our set234 // If the script is stopped, we can remove it from our set
223 i.remove();235 i.remove();
224 }236 }
225 }237 }
226 _stoppingAllScripts = false;238 _stoppingAllScripts = false;
227 _allScriptsMutex.unlock();239 _allScriptsMutex.unlock();
240 qCDebug(scriptengine) << "DONE Stopping all scripts....";
228}241}
229 242
230 243
231void ScriptEngine::waitTillDoneRunning() {244void ScriptEngine::waitTillDoneRunning() {
232 // If the script never started running or finished running before we got here, we don't need to wait for it245 // If the script never started running or finished running before we got here, we don't need to wait for it
233 if (_isRunning) {246 if (_isRunning && _isThreaded) {
234 247
235 _doneRunningThisScript = false; // NOTE: this is static, we serialize our waiting for scripts to finish
236
237 // NOTE: waitTillDoneRunning() will be called on the main Application thread, inside of stopAllScripts()248 // NOTE: waitTillDoneRunning() will be called on the main Application thread, inside of stopAllScripts()
238 // we want the application thread to continue to process events, because the scripts will likely need to249 // we want the application thread to continue to process events, because the scripts will likely need to
239 // marshall messages across to the main thread. For example if they access Settings or Meny in any of their250 // marshall messages across to the main thread. For example if they access Settings or Meny in any of their
240 // shutdown code.251 // shutdown code.
241 while (!_doneRunningThisScript) {252 while (thread()->isRunning()) {
242
243 // process events for the main application thread, allowing invokeMethod calls to pass between threads253 // process events for the main application thread, allowing invokeMethod calls to pass between threads
244 QCoreApplication::processEvents();254 QCoreApplication::processEvents();
245 }255 }
@@ -261,12 +271,12 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { · 15 unmodified lines
261 if (_isRunning) {271 if (_isRunning) {
262 return;272 return;
263 }273 }
264 274
265 _fileNameString = scriptURL.toString();275 _fileNameString = scriptURL.toString();
266 _isReloading = reload;276 _isReloading = reload;
267 277
268 QUrl url(scriptURL);278 QUrl url(scriptURL);
269 279
270 bool isPending;280 bool isPending;
271 auto scriptCache = DependencyManager::get<ScriptCache>();281 auto scriptCache = DependencyManager::get<ScriptCache>();
272 scriptCache->getScript(url, this, isPending, reload);282 scriptCache->getScript(url, this, isPending, reload);
@@ -313,12 +323,12 @@ void ScriptEngine::init() { · 40 unmodified lines
313 if (_isInitialized) {323 if (_isInitialized) {
314 return; // only initialize once324 return; // only initialize once
315 }325 }
316 326
317 _isInitialized = true;327 _isInitialized = true;
318 328
319 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();329 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
320 entityScriptingInterface->init();330 entityScriptingInterface->init();
321 331
322 // register various meta-types332 // register various meta-types
323 registerMetaTypes(this);333 registerMetaTypes(this);
324 registerMIDIMetaTypes(this);334 registerMIDIMetaTypes(this);
@@ -327,30 +337,30 @@ void ScriptEngine::init() { · 2 unmodified lines
327 registerAnimationTypes(this);337 registerAnimationTypes(this);
328 registerAvatarTypes(this);338 registerAvatarTypes(this);
329 registerAudioMetaTypes(this);339 registerAudioMetaTypes(this);
330 340
331 qScriptRegisterMetaType(this, EntityPropertyFlagsToScriptValue, EntityPropertyFlagsFromScriptValue);341 qScriptRegisterMetaType(this, EntityPropertyFlagsToScriptValue, EntityPropertyFlagsFromScriptValue);
332 qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly);342 qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly);
333 qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);343 qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);
334 qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue);344 qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue);
335 qScriptRegisterSequenceMetaType<QVector<QUuid>>(this);345 qScriptRegisterSequenceMetaType<QVector<QUuid>>(this);
336 qScriptRegisterSequenceMetaType<QVector<EntityItemID>>(this);346 qScriptRegisterSequenceMetaType<QVector<EntityItemID>>(this);
337 347
338 qScriptRegisterSequenceMetaType<QVector<glm::vec2> >(this);348 qScriptRegisterSequenceMetaType<QVector<glm::vec2> >(this);
339 qScriptRegisterSequenceMetaType<QVector<glm::quat> >(this);349 qScriptRegisterSequenceMetaType<QVector<glm::quat> >(this);
340 qScriptRegisterSequenceMetaType<QVector<QString> >(this);350 qScriptRegisterSequenceMetaType<QVector<QString> >(this);
341 351
342 QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor);352 QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor);
343 globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue);353 globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue);
344 354
345 QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor);355 QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor);
346 globalObject().setProperty("WebSocket", webSocketConstructorValue);356 globalObject().setProperty("WebSocket", webSocketConstructorValue);
347 357
348 QScriptValue printConstructorValue = newFunction(debugPrint);358 QScriptValue printConstructorValue = newFunction(debugPrint);
349 globalObject().setProperty("print", printConstructorValue);359 globalObject().setProperty("print", printConstructorValue);
350 360
351 QScriptValue audioEffectOptionsConstructorValue = newFunction(AudioEffectOptions::constructor);361 QScriptValue audioEffectOptionsConstructorValue = newFunction(AudioEffectOptions::constructor);
352 globalObject().setProperty("AudioEffectOptions", audioEffectOptionsConstructorValue);362 globalObject().setProperty("AudioEffectOptions", audioEffectOptionsConstructorValue);
353 363
354 qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue);364 qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue);
355 qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue);365 qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue);
356 qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue);366 qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue);
@@ -358,7 +368,7 @@ void ScriptEngine::init() { · 1 unmodified line
358 qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue);368 qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue);
359 qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue);369 qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue);
360 qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue);370 qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue);
361 371
362 registerGlobalObject("Script", this);372 registerGlobalObject("Script", this);
363 registerGlobalObject("Audio", &AudioScriptingInterface::getInstance());373 registerGlobalObject("Audio", &AudioScriptingInterface::getInstance());
364 registerGlobalObject("Entities", entityScriptingInterface.data());374 registerGlobalObject("Entities", entityScriptingInterface.data());
@@ -366,15 +376,22 @@ void ScriptEngine::init() { · 1 unmodified line
366 registerGlobalObject("Vec3", &_vec3Library);376 registerGlobalObject("Vec3", &_vec3Library);
367 registerGlobalObject("Uuid", &_uuidLibrary);377 registerGlobalObject("Uuid", &_uuidLibrary);
368 registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());378 registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
379 registerGlobalObject("Messages", DependencyManager::get<MessagesClient>().data());
369 qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);380 qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
370 qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);381 qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
371 382
372 // constants383 // constants
373 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));384 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
374 385
375 auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();386 auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
376 registerGlobalObject("Controller", scriptingInterface.data());387 registerGlobalObject("Controller", scriptingInterface.data());
377 UserInputMapper::registerControllerTypes(this);388 UserInputMapper::registerControllerTypes(this);
389
390 auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
391 registerGlobalObject("Recording", recordingInterface.data());
392
393 registerGlobalObject("Assets", &_assetScriptingInterface);
394
378}395}
379 396
380void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {397void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
@@ -387,11 +404,11 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { · 6 unmodified lines
387 Q_ARG(QScriptValue, value));404 Q_ARG(QScriptValue, value));
388 return;405 return;
389 }406 }
390 407
391 QStringList pathToValue = valueName.split(".");408 QStringList pathToValue = valueName.split(".");
392 int partsToGo = pathToValue.length();409 int partsToGo = pathToValue.length();
393 QScriptValue partObject = globalObject();410 QScriptValue partObject = globalObject();
394 411
395 for (const auto& pathPart : pathToValue) {412 for (const auto& pathPart : pathToValue) {
396 partsToGo--;413 partsToGo--;
397 if (!partObject.property(pathPart).isValid()) {414 if (!partObject.property(pathPart).isValid()) {
@@ -421,7 +438,7 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { · 23 unmodified lines
421#ifdef THREAD_DEBUGGING438#ifdef THREAD_DEBUGGING
422 qDebug() << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name;439 qDebug() << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name;
423#endif440#endif
424 441
425 if (!globalObject().property(name).isValid()) {442 if (!globalObject().property(name).isValid()) {
426 if (object) {443 if (object) {
427 QScriptValue value = newQObject(object);444 QScriptValue value = newQObject(object);
@@ -446,7 +463,7 @@ void ScriptEngine::registerFunction(const QString& name, QScriptEngine::Function · 18 unmodified lines
446#ifdef THREAD_DEBUGGING463#ifdef THREAD_DEBUGGING
447 qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name;464 qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name;
448#endif465#endif
449 466
450 QScriptValue scriptFun = newFunction(functionSignature, numArguments);467 QScriptValue scriptFun = newFunction(functionSignature, numArguments);
451 globalObject().setProperty(name, scriptFun);468 globalObject().setProperty(name, scriptFun);
452}469}
@@ -465,7 +482,7 @@ void ScriptEngine::registerFunction(const QString& parent, const QString& name, · 12 unmodified lines
465#ifdef THREAD_DEBUGGING482#ifdef THREAD_DEBUGGING
466 qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name;483 qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name;
467#endif484#endif
468 485
469 QScriptValue object = globalObject().property(parent);486 QScriptValue object = globalObject().property(parent);
470 if (object.isValid()) {487 if (object.isValid()) {
471 QScriptValue scriptFun = newFunction(functionSignature, numArguments);488 QScriptValue scriptFun = newFunction(functionSignature, numArguments);
@@ -490,10 +507,10 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func · 18 unmodified lines
490#ifdef THREAD_DEBUGGING507#ifdef THREAD_DEBUGGING
491 qDebug() << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;508 qDebug() << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent;
492#endif509#endif
493 510
494 QScriptValue setterFunction = newFunction(setter, 1);511 QScriptValue setterFunction = newFunction(setter, 1);
495 QScriptValue getterFunction = newFunction(getter);512 QScriptValue getterFunction = newFunction(getter);
496 513
497 if (!parent.isNull() && !parent.isEmpty()) {514 if (!parent.isNull() && !parent.isEmpty()) {
498 QScriptValue object = globalObject().property(parent);515 QScriptValue object = globalObject().property(parent);
499 if (object.isValid()) {516 if (object.isValid()) {
@@ -522,7 +539,7 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin · 22 unmodified lines
522#ifdef THREAD_DEBUGGING539#ifdef THREAD_DEBUGGING
523 qDebug() << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;540 qDebug() << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;
524#endif541#endif
525 542
526 if (!_registeredHandlers.contains(entityID)) {543 if (!_registeredHandlers.contains(entityID)) {
527 return;544 return;
528 }545 }
@@ -543,7 +560,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 14 unmodified lines
543 qDebug() << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "560 qDebug() << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
544 "entityID:" << entityID << " eventName:" << eventName;561 "entityID:" << entityID << " eventName:" << eventName;
545#endif562#endif
546 563
547 QMetaObject::invokeMethod(this, "addEventHandler",564 QMetaObject::invokeMethod(this, "addEventHandler",
548 Q_ARG(const EntityItemID&, entityID),565 Q_ARG(const EntityItemID&, entityID),
549 Q_ARG(const QString&, eventName),566 Q_ARG(const QString&, eventName),
@@ -553,7 +570,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 3 unmodified lines
553#ifdef THREAD_DEBUGGING570#ifdef THREAD_DEBUGGING
554 qDebug() << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;571 qDebug() << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName;
555#endif572#endif
556 573
557 if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script...574 if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script...
558 // Connect up ALL the handlers to the global entities object's signals.575 // Connect up ALL the handlers to the global entities object's signals.
559 // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)576 // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.)
@@ -561,7 +578,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 1 unmodified line
561 connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) {578 connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) {
562 _registeredHandlers.remove(entityID);579 _registeredHandlers.remove(entityID);
563 });580 });
564 581
565 // Two common cases of event handler, differing only in argument signature.582 // Two common cases of event handler, differing only in argument signature.
566 using SingleEntityHandler = std::function<void(const EntityItemID&)>;583 using SingleEntityHandler = std::function<void(const EntityItemID&)>;
567 auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler {584 auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler {
@@ -569,37 +586,37 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& · 1 unmodified line
569 forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) });586 forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) });
570 };587 };
571 };588 };
572 589
573 using MouseHandler = std::function<void(const EntityItemID&, const MouseEvent&)>;590 using MouseHandler = std::function<void(const EntityItemID&, const MouseEvent&)>;
574 auto makeMouseHandler = [this](QString eventName) -> MouseHandler {591 auto makeMouseHandler = [this](QString eventName) -> MouseHandler {
575 return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) {592 return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) {
576 forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });593 forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
577 };594 };
578 };595 };
579 596
580 using CollisionHandler = std::function<void(const EntityItemID&, const EntityItemID&, const Collision&)>;597 using CollisionHandler = std::function<void(const EntityItemID&, const EntityItemID&, const Collision&)>;
581 auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler {598 auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler {
582 return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) {599 return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) {
583 forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this),600 forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this),
584 collisionToScriptValue(this, collision) });601 collisionToScriptValue(this, collision) });
585 };602 };
586 };603 };
587 604
588 connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity"));605 connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity"));
589 connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity"));606 connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity"));
590 607
591 connect(entities.data(), &EntityScriptingInterface::mousePressOnEntity, this, makeMouseHandler("mousePressOnEntity"));608 connect(entities.data(), &EntityScriptingInterface::mousePressOnEntity, this, makeMouseHandler("mousePressOnEntity"));
592 connect(entities.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, makeMouseHandler("mouseMoveOnEntity"));609 connect(entities.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, makeMouseHandler("mouseMoveOnEntity"));
593 connect(entities.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, makeMouseHandler("mouseReleaseOnEntity"));610 connect(entities.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, makeMouseHandler("mouseReleaseOnEntity"));
594 611
595 connect(entities.data(), &EntityScriptingInterface::clickDownOnEntity, this, makeMouseHandler("clickDownOnEntity"));612 connect(entities.data(), &EntityScriptingInterface::clickDownOnEntity, this, makeMouseHandler("clickDownOnEntity"));
596 connect(entities.data(), &EntityScriptingInterface::holdingClickOnEntity, this, makeMouseHandler("holdingClickOnEntity"));613 connect(entities.data(), &EntityScriptingInterface::holdingClickOnEntity, this, makeMouseHandler("holdingClickOnEntity"));
597 connect(entities.data(), &EntityScriptingInterface::clickReleaseOnEntity, this, makeMouseHandler("clickReleaseOnEntity"));614 connect(entities.data(), &EntityScriptingInterface::clickReleaseOnEntity, this, makeMouseHandler("clickReleaseOnEntity"));
598 615
599 connect(entities.data(), &EntityScriptingInterface::hoverEnterEntity, this, makeMouseHandler("hoverEnterEntity"));616 connect(entities.data(), &EntityScriptingInterface::hoverEnterEntity, this, makeMouseHandler("hoverEnterEntity"));
600 connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity"));617 connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity"));
601 connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity"));618 connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity"));
602 619
603 connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity"));620 connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity"));
604 }621 }
605 if (!_registeredHandlers.contains(entityID)) {622 if (!_registeredHandlers.contains(entityID)) {
@@ -614,7 +631,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi · 8 unmodified lines
614 if (_stoppingAllScripts) {631 if (_stoppingAllScripts) {
615 return QScriptValue(); // bail early632 return QScriptValue(); // bail early
616 }633 }
617 634
618 if (QThread::currentThread() != thread()) {635 if (QThread::currentThread() != thread()) {
619 QScriptValue result;636 QScriptValue result;
620#ifdef THREAD_DEBUGGING637#ifdef THREAD_DEBUGGING
@@ -628,17 +645,17 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi · 7 unmodified lines
628 Q_ARG(int, lineNumber));645 Q_ARG(int, lineNumber));
629 return result;646 return result;
630 }647 }
631 648
632 // Check syntax649 // Check syntax
633 const QScriptProgram program(sourceCode, fileName, lineNumber);650 const QScriptProgram program(sourceCode, fileName, lineNumber);
634 if (!hasCorrectSyntax(program)) {651 if (!hasCorrectSyntax(program)) {
635 return QScriptValue();652 return QScriptValue();
636 }653 }
637 654
638 ++_evaluatesPending;655 ++_evaluatesPending;
639 const auto result = QScriptEngine::evaluate(program);656 const auto result = QScriptEngine::evaluate(program);
640 --_evaluatesPending;657 --_evaluatesPending;
641 658
642 const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName());659 const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName());
643 if (_wantSignals) {660 if (_wantSignals) {
644 emit evaluationFinished(result, hadUncaughtException);661 emit evaluationFinished(result, hadUncaughtException);
@@ -650,7 +667,7 @@ void ScriptEngine::run() { · 5 unmodified lines
650 if (_stoppingAllScripts) {667 if (_stoppingAllScripts) {
651 return; // bail early - avoid setting state in init(), as evaluate() will bail too668 return; // bail early - avoid setting state in init(), as evaluate() will bail too
652 }669 }
653 670
654 if (!_isInitialized) {671 if (!_isInitialized) {
655 init();672 init();
656 }673 }
@@ -660,68 +677,68 @@ void ScriptEngine::run() { · 3 unmodified lines
660 if (_wantSignals) {677 if (_wantSignals) {
661 emit runningStateChanged();678 emit runningStateChanged();
662 }679 }
663 680
664 QScriptValue result = evaluate(_scriptContents, _fileNameString);681 QScriptValue result = evaluate(_scriptContents, _fileNameString);
665 682
666 QElapsedTimer startTime;683 QElapsedTimer startTime;
667 startTime.start();684 startTime.start();
668 685
669 int thisFrame = 0;686 int thisFrame = 0;
670 687
671 auto nodeList = DependencyManager::get<NodeList>();688 auto nodeList = DependencyManager::get<NodeList>();
672 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();689 auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
673 690
674 qint64 lastUpdate = usecTimestampNow();691 qint64 lastUpdate = usecTimestampNow();
675 692
676 while (!_isFinished) {693 while (!_isFinished) {
677 int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec694 int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec
678 if (usecToSleep > 0) {695 if (usecToSleep > 0) {
679 usleep(usecToSleep);696 usleep(usecToSleep);
680 }697 }
681 698
682 if (_isFinished) {699 if (_isFinished) {
683 break;700 break;
684 }701 }
685 702
686 QCoreApplication::processEvents();703 QCoreApplication::processEvents();
687 704
688 if (_isFinished) {705 if (_isFinished) {
689 break;706 break;
690 }707 }
691 708
692 if (!_isFinished && entityScriptingInterface->getEntityPacketSender()->serversExist()) {709 if (!_isFinished && entityScriptingInterface->getEntityPacketSender()->serversExist()) {
693 // release the queue of edit entity messages.710 // release the queue of edit entity messages.
694 entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages();711 entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages();
695 712
696 // since we're in non-threaded mode, call process so that the packets are sent713 // since we're in non-threaded mode, call process so that the packets are sent
697 if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) {714 if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) {
698 entityScriptingInterface->getEntityPacketSender()->process();715 entityScriptingInterface->getEntityPacketSender()->process();
699 }716 }
700 }717 }
701 718
702 qint64 now = usecTimestampNow();719 qint64 now = usecTimestampNow();
703 float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND;720 float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND;
704 721
705 if (!_isFinished) {722 if (!_isFinished) {
706 if (_wantSignals) {723 if (_wantSignals) {
707 emit update(deltaTime);724 emit update(deltaTime);
708 }725 }
709 }726 }
710 lastUpdate = now;727 lastUpdate = now;
711 728
712 // Debug and clear exceptions729 // Debug and clear exceptions
713 hadUncaughtExceptions(*this, _fileNameString);730 hadUncaughtExceptions(*this, _fileNameString);
714 }731 }
715 732
716 stopAllTimers(); // make sure all our timers are stopped if the script is ending733 stopAllTimers(); // make sure all our timers are stopped if the script is ending
717 if (_wantSignals) {734 if (_wantSignals) {
718 emit scriptEnding();735 emit scriptEnding();
719 }736 }
720 737
721 if (entityScriptingInterface->getEntityPacketSender()->serversExist()) {738 if (entityScriptingInterface->getEntityPacketSender()->serversExist()) {
722 // release the queue of edit entity messages.739 // release the queue of edit entity messages.
723 entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages();740 entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages();
724 741
725 // since we're in non-threaded mode, call process so that the packets are sent742 // since we're in non-threaded mode, call process so that the packets are sent
726 if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) {743 if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) {
727 // wait here till the edit packet sender is completely done sending744 // wait here till the edit packet sender is completely done sending
@@ -733,18 +750,16 @@ void ScriptEngine::run() { · 5 unmodified lines
733 // FIXME - do we need to have a similar "wait here" loop for non-threaded packet senders?750 // FIXME - do we need to have a similar "wait here" loop for non-threaded packet senders?
734 }751 }
735 }752 }
736 753
737 if (_wantSignals) {754 if (_wantSignals) {
738 emit finished(_fileNameString);755 emit finished(_fileNameString);
739 }756 }
740 757
741 _isRunning = false;758 _isRunning = false;
742 if (_wantSignals) {759 if (_wantSignals) {
743 emit runningStateChanged();760 emit runningStateChanged();
744 emit doneRunning();761 emit doneRunning();
745 }762 }
746
747 _doneRunningThisScript = true;
748}763}
749 764
750// NOTE: This is private because it must be called on the same thread that created the timers, which is why765// NOTE: This is private because it must be called on the same thread that created the timers, which is why
@@ -791,13 +806,13 @@ void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantM · 40 unmodified lines
791void ScriptEngine::timerFired() {806void ScriptEngine::timerFired() {
792 QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());807 QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
793 QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);808 QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);
794 809
795 if (!callingTimer->isActive()) {810 if (!callingTimer->isActive()) {
796 // this timer is done, we can kill it811 // this timer is done, we can kill it
797 _timerFunctionMap.remove(callingTimer);812 _timerFunctionMap.remove(callingTimer);
798 delete callingTimer;813 delete callingTimer;
799 }814 }
800 815
801 // call the associated JS function, if it exists816 // call the associated JS function, if it exists
802 if (timerFunction.isValid()) {817 if (timerFunction.isValid()) {
803 timerFunction.call();818 timerFunction.call();
@@ -808,14 +823,14 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int · 4 unmodified lines
808 // create the timer, add it to the map, and start it823 // create the timer, add it to the map, and start it
809 QTimer* newTimer = new QTimer(this);824 QTimer* newTimer = new QTimer(this);
810 newTimer->setSingleShot(isSingleShot);825 newTimer->setSingleShot(isSingleShot);
811 826
812 connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired);827 connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired);
813 828
814 // make sure the timer stops when the script does829 // make sure the timer stops when the script does
815 connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);830 connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);
816 831
817 _timerFunctionMap.insert(newTimer, function);832 _timerFunctionMap.insert(newTimer, function);
818 833
819 newTimer->start(intervalMS);834 newTimer->start(intervalMS);
820 return newTimer;835 return newTimer;
821}836}
@@ -825,7 +840,7 @@ QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) · 3 unmodified lines
825 qCDebug(scriptengine) << "Script.setInterval() while shutting down is ignored... parent script:" << getFilename();840 qCDebug(scriptengine) << "Script.setInterval() while shutting down is ignored... parent script:" << getFilename();
826 return NULL; // bail early841 return NULL; // bail early
827 }842 }
828 843
829 return setupTimerWithInterval(function, intervalMS, false);844 return setupTimerWithInterval(function, intervalMS, false);
830}845}
831 846
@@ -834,7 +849,7 @@ QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) { · 2 unmodified lines
834 qCDebug(scriptengine) << "Script.setTimeout() while shutting down is ignored... parent script:" << getFilename();849 qCDebug(scriptengine) << "Script.setTimeout() while shutting down is ignored... parent script:" << getFilename();
835 return NULL; // bail early850 return NULL; // bail early
836 }851 }
837 852
838 return setupTimerWithInterval(function, timeoutMS, true);853 return setupTimerWithInterval(function, timeoutMS, true);
839}854}
840 855
@@ -852,7 +867,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { · 11 unmodified lines
852 if (!url.scheme().isEmpty()) {867 if (!url.scheme().isEmpty()) {
853 return url;868 return url;
854 }869 }
855 870
856 // we apparently weren't a fully qualified url, so, let's assume we're relative871 // we apparently weren't a fully qualified url, so, let's assume we're relative
857 // to the original URL of our script872 // to the original URL of our script
858 QUrl parentURL;873 QUrl parentURL;
@@ -865,7 +880,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { · 6 unmodified lines
865 if (parentURL.scheme().isEmpty()) {880 if (parentURL.scheme().isEmpty()) {
866 parentURL = QUrl::fromLocalFile(_fileNameString);881 parentURL = QUrl::fromLocalFile(_fileNameString);
867 }882 }
868 883
869 // at this point we should have a legitimate fully qualified URL for our parent884 // at this point we should have a legitimate fully qualified URL for our parent
870 url = parentURL.resolved(url);885 url = parentURL.resolved(url);
871 return url;886 return url;
@@ -898,9 +913,9 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 26 unmodified lines
898 qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL;913 qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL;
899 }914 }
900 }915 }
901 916
902 BatchLoader* loader = new BatchLoader(urls);917 BatchLoader* loader = new BatchLoader(urls);
903 918
904 auto evaluateScripts = [=](const QMap<QUrl, QString>& data) {919 auto evaluateScripts = [=](const QMap<QUrl, QString>& data) {
905 auto parentURL = _parentURL;920 auto parentURL = _parentURL;
906 for (QUrl url : urls) {921 for (QUrl url : urls) {
@@ -919,17 +934,17 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac · 12 unmodified lines
919 if (callback.isFunction()) {934 if (callback.isFunction()) {
920 QScriptValue(callback).call();935 QScriptValue(callback).call();
921 }936 }
922 937
923 loader->deleteLater();938 loader->deleteLater();
924 };939 };
925 940
926 connect(loader, &BatchLoader::finished, this, evaluateScripts);941 connect(loader, &BatchLoader::finished, this, evaluateScripts);
927 942
928 // If we are destroyed before the loader completes, make sure to clean it up943 // If we are destroyed before the loader completes, make sure to clean it up
929 connect(this, &QObject::destroyed, loader, &QObject::deleteLater);944 connect(this, &QObject::destroyed, loader, &QObject::deleteLater);
930 945
931 loader->start();946 loader->start();
932 947
933 if (!callback.isFunction() && !loader->isFinished()) {948 if (!callback.isFunction() && !loader->isFinished()) {
934 QEventLoop loop;949 QEventLoop loop;
935 QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit);950 QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit);
@@ -943,7 +958,7 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { · 7 unmodified lines
943 << "includeFile:" << includeFile << "parent script:" << getFilename();958 << "includeFile:" << includeFile << "parent script:" << getFilename();
944 return; // bail early959 return; // bail early
945 }960 }
946 961
947 QStringList urls;962 QStringList urls;
948 urls.append(includeFile);963 urls.append(includeFile);
949 include(urls, callback);964 include(urls, callback);
@@ -958,7 +973,7 @@ void ScriptEngine::load(const QString& loadFile) { · 8 unmodified lines
958 << "loadFile:" << loadFile << "parent script:" << getFilename();973 << "loadFile:" << loadFile << "parent script:" << getFilename();
959 return; // bail early974 return; // bail early
960 }975 }
961 976
962 QUrl url = resolvePath(loadFile);977 QUrl url = resolvePath(loadFile);
963 if (_isReloading) {978 if (_isReloading) {
964 auto scriptCache = DependencyManager::get<ScriptCache>();979 auto scriptCache = DependencyManager::get<ScriptCache>();
@@ -1003,7 +1018,7 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& · 38 unmodified lines
1003 qDebug() << "*** WARNING *** ScriptEngine::loadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1018 qDebug() << "*** WARNING *** ScriptEngine::loadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1004 "entityID:" << entityID << "entityScript:" << entityScript <<"forceRedownload:" << forceRedownload;1019 "entityID:" << entityID << "entityScript:" << entityScript <<"forceRedownload:" << forceRedownload;
1005#endif1020#endif
1006 1021
1007 QMetaObject::invokeMethod(this, "loadEntityScript",1022 QMetaObject::invokeMethod(this, "loadEntityScript",
1008 Q_ARG(const EntityItemID&, entityID),1023 Q_ARG(const EntityItemID&, entityID),
1009 Q_ARG(const QString&, entityScript),1024 Q_ARG(const QString&, entityScript),
@@ -1014,10 +1029,10 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& · 4 unmodified lines
1014 qDebug() << "ScriptEngine::loadEntityScript() called on correct thread [" << thread() << "] "1029 qDebug() << "ScriptEngine::loadEntityScript() called on correct thread [" << thread() << "] "
1015 "entityID:" << entityID << "entityScript:" << entityScript << "forceRedownload:" << forceRedownload;1030 "entityID:" << entityID << "entityScript:" << entityScript << "forceRedownload:" << forceRedownload;
1016#endif1031#endif
1017 1032
1018 // If we've been called our known entityScripts should not know about us..1033 // If we've been called our known entityScripts should not know about us..
1019 assert(!_entityScripts.contains(entityID));1034 assert(!_entityScripts.contains(entityID));
1020 1035
1021#ifdef THREAD_DEBUGGING1036#ifdef THREAD_DEBUGGING
1022 qDebug() << "ScriptEngine::loadEntityScript() calling scriptCache->getScriptContents() on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";1037 qDebug() << "ScriptEngine::loadEntityScript() calling scriptCache->getScriptContents() on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
1023#endif1038#endif
@@ -1025,7 +1040,7 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& · 1 unmodified line
1025#ifdef THREAD_DEBUGGING1040#ifdef THREAD_DEBUGGING
1026 qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";1041 qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
1027#endif1042#endif
1028 1043
1029 this->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);1044 this->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
1030 }, forceRedownload);1045 }, forceRedownload);
1031}1046}
@@ -1038,7 +1053,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 6 unmodified lines
1038 qDebug() << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1053 qDebug() << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1039 "entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:" << contents << "isURL:" << isURL << "success:" << success;1054 "entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:" << contents << "isURL:" << isURL << "success:" << success;
1040#endif1055#endif
1041 1056
1042 QMetaObject::invokeMethod(this, "entityScriptContentAvailable",1057 QMetaObject::invokeMethod(this, "entityScriptContentAvailable",
1043 Q_ARG(const EntityItemID&, entityID),1058 Q_ARG(const EntityItemID&, entityID),
1044 Q_ARG(const QString&, scriptOrURL),1059 Q_ARG(const QString&, scriptOrURL),
@@ -1047,15 +1062,15 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 2 unmodified lines
1047 Q_ARG(bool, success));1062 Q_ARG(bool, success));
1048 return;1063 return;
1049 }1064 }
1050 1065
1051#ifdef THREAD_DEBUGGING1066#ifdef THREAD_DEBUGGING
1052 qDebug() << "ScriptEngine::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";1067 qDebug() << "ScriptEngine::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]";
1053#endif1068#endif
1054 1069
1055 auto scriptCache = DependencyManager::get<ScriptCache>();1070 auto scriptCache = DependencyManager::get<ScriptCache>();
1056 bool isFileUrl = isURL && scriptOrURL.startsWith("file://");1071 bool isFileUrl = isURL && scriptOrURL.startsWith("file://");
1057 auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript");1072 auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript");
1058 1073
1059 QScriptProgram program(contents, fileName);1074 QScriptProgram program(contents, fileName);
1060 if (!hasCorrectSyntax(program)) {1075 if (!hasCorrectSyntax(program)) {
1061 if (!isFileUrl) {1076 if (!isFileUrl) {
@@ -1063,17 +1078,17 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 1 unmodified line
1063 }1078 }
1064 return; // done processing script1079 return; // done processing script
1065 }1080 }
1066 1081
1067 if (isURL) {1082 if (isURL) {
1068 setParentURL(scriptOrURL);1083 setParentURL(scriptOrURL);
1069 }1084 }
1070 1085
1071 QScriptEngine sandbox;1086 QScriptEngine sandbox;
1072 QScriptValue testConstructor = sandbox.evaluate(program);1087 QScriptValue testConstructor = sandbox.evaluate(program);
1073 if (hadUncaughtExceptions(sandbox, program.fileName())) {1088 if (hadUncaughtExceptions(sandbox, program.fileName())) {
1074 return;1089 return;
1075 }1090 }
1076 1091
1077 if (!testConstructor.isFunction()) {1092 if (!testConstructor.isFunction()) {
1078 qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n"1093 qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n"
1079 " NOT CONSTRUCTOR\n"1094 " NOT CONSTRUCTOR\n"
@@ -1083,13 +1098,13 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 3 unmodified lines
1083 }1098 }
1084 return; // done processing script1099 return; // done processing script
1085 }1100 }
1086 1101
1087 int64_t lastModified = 0;1102 int64_t lastModified = 0;
1088 if (isFileUrl) {1103 if (isFileUrl) {
1089 QString file = QUrl(scriptOrURL).toLocalFile();1104 QString file = QUrl(scriptOrURL).toLocalFile();
1090 lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();1105 lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();
1091 }1106 }
1092 1107
1093 QScriptValue entityScriptConstructor = evaluate(contents, fileName);1108 QScriptValue entityScriptConstructor = evaluate(contents, fileName);
1094 QScriptValue entityScriptObject = entityScriptConstructor.construct();1109 QScriptValue entityScriptObject = entityScriptConstructor.construct();
1095 EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };1110 EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };
@@ -1097,7 +1112,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 1 unmodified line
1097 if (isURL) {1112 if (isURL) {
1098 setParentURL("");1113 setParentURL("");
1099 }1114 }
1100 1115
1101 // if we got this far, then call the preload method1116 // if we got this far, then call the preload method
1102 callEntityScriptMethod(entityID, "preload");1117 callEntityScriptMethod(entityID, "preload");
1103}1118}
@@ -1108,7 +1123,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { · 4 unmodified lines
1108 qDebug() << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1123 qDebug() << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1109 "entityID:" << entityID;1124 "entityID:" << entityID;
1110#endif1125#endif
1111 1126
1112 QMetaObject::invokeMethod(this, "unloadEntityScript",1127 QMetaObject::invokeMethod(this, "unloadEntityScript",
1113 Q_ARG(const EntityItemID&, entityID));1128 Q_ARG(const EntityItemID&, entityID));
1114 return;1129 return;
@@ -1117,7 +1132,7 @@ void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { · 2 unmodified lines
1117 qDebug() << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] "1132 qDebug() << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] "
1118 "entityID:" << entityID;1133 "entityID:" << entityID;
1119#endif1134#endif
1120 1135
1121 if (_entityScripts.contains(entityID)) {1136 if (_entityScripts.contains(entityID)) {
1122 callEntityScriptMethod(entityID, "unload");1137 callEntityScriptMethod(entityID, "unload");
1123 _entityScripts.remove(entityID);1138 _entityScripts.remove(entityID);
@@ -1129,7 +1144,7 @@ void ScriptEngine::unloadAllEntityScripts() { · 5 unmodified lines
1129#ifdef THREAD_DEBUGGING1144#ifdef THREAD_DEBUGGING
1130 qDebug() << "*** WARNING *** ScriptEngine::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";1145 qDebug() << "*** WARNING *** ScriptEngine::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
1131#endif1146#endif
1132 1147
1133 QMetaObject::invokeMethod(this, "unloadAllEntityScripts");1148 QMetaObject::invokeMethod(this, "unloadAllEntityScripts");
1134 return;1149 return;
1135 }1150 }
@@ -1146,21 +1161,21 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { · 10 unmodified lines
1146 if (!_entityScripts.contains(entityID)) {1161 if (!_entityScripts.contains(entityID)) {
1147 return;1162 return;
1148 }1163 }
1149 1164
1150 static bool recurseGuard = false;1165 static bool recurseGuard = false;
1151 if (recurseGuard) {1166 if (recurseGuard) {
1152 return;1167 return;
1153 }1168 }
1154 recurseGuard = true;1169 recurseGuard = true;
1155 1170
1156 EntityScriptDetails details = _entityScripts[entityID];1171 EntityScriptDetails details = _entityScripts[entityID];
1157 // Check to see if a file based script needs to be reloaded (easier debugging)1172 // Check to see if a file based script needs to be reloaded (easier debugging)
1158 if (details.lastModified > 0) {1173 if (details.lastModified > 0) {
1159 QString filePath = QUrl(details.scriptText).toLocalFile();1174 QString filePath = QUrl(details.scriptText).toLocalFile();
1160 auto lastModified = QFileInfo(filePath).lastModified().toMSecsSinceEpoch();1175 auto lastModified = QFileInfo(filePath).lastModified().toMSecsSinceEpoch();
1161 if (lastModified > details.lastModified) {1176 if (lastModified > details.lastModified) {
1162 qDebug() << "Reloading modified script " << details.scriptText;1177 qCDebug(scriptengine) << "Reloading modified script " << details.scriptText;
1163 1178
1164 QFile file(filePath);1179 QFile file(filePath);
1165 file.open(QIODevice::ReadOnly);1180 file.open(QIODevice::ReadOnly);
1166 QString scriptContents = QTextStream(&file).readAll();1181 QString scriptContents = QTextStream(&file).readAll();
@@ -1182,7 +1197,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 15 unmodified lines
1182 qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1197 qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1183 "entityID:" << entityID << "methodName:" << methodName;1198 "entityID:" << entityID << "methodName:" << methodName;
1184#endif1199#endif
1185 1200
1186 QMetaObject::invokeMethod(this, "callEntityScriptMethod",1201 QMetaObject::invokeMethod(this, "callEntityScriptMethod",
1187 Q_ARG(const EntityItemID&, entityID),1202 Q_ARG(const EntityItemID&, entityID),
1188 Q_ARG(const QString&, methodName),1203 Q_ARG(const QString&, methodName),
@@ -1193,7 +1208,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 4 unmodified lines
1193 qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "1208 qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
1194 "entityID:" << entityID << "methodName:" << methodName;1209 "entityID:" << entityID << "methodName:" << methodName;
1195#endif1210#endif
1196 1211
1197 refreshFileScript(entityID);1212 refreshFileScript(entityID);
1198 if (_entityScripts.contains(entityID)) {1213 if (_entityScripts.contains(entityID)) {
1199 EntityScriptDetails details = _entityScripts[entityID];1214 EntityScriptDetails details = _entityScripts[entityID];
@@ -1204,7 +1219,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 4 unmodified lines
1204 args << qScriptValueFromSequence(this, params);1219 args << qScriptValueFromSequence(this, params);
1205 entityScript.property(methodName).call(entityScript, args);1220 entityScript.property(methodName).call(entityScript, args);
1206 }1221 }
1207 1222
1208 }1223 }
1209}1224}
1210 1225
@@ -1214,7 +1229,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 3 unmodified lines
1214 qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1229 qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1215 "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";1230 "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";
1216#endif1231#endif
1217 1232
1218 QMetaObject::invokeMethod(this, "callEntityScriptMethod",1233 QMetaObject::invokeMethod(this, "callEntityScriptMethod",
1219 Q_ARG(const EntityItemID&, entityID),1234 Q_ARG(const EntityItemID&, entityID),
1220 Q_ARG(const QString&, methodName),1235 Q_ARG(const QString&, methodName),
@@ -1225,7 +1240,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 4 unmodified lines
1225 qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "1240 qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
1226 "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";1241 "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";
1227#endif1242#endif
1228 1243
1229 refreshFileScript(entityID);1244 refreshFileScript(entityID);
1230 if (_entityScripts.contains(entityID)) {1245 if (_entityScripts.contains(entityID)) {
1231 EntityScriptDetails details = _entityScripts[entityID];1246 EntityScriptDetails details = _entityScripts[entityID];
@@ -1246,7 +1261,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 14 unmodified lines
1246 qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "1261 qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
1247 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";1262 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
1248#endif1263#endif
1249 1264
1250 QMetaObject::invokeMethod(this, "callEntityScriptMethod",1265 QMetaObject::invokeMethod(this, "callEntityScriptMethod",
1251 Q_ARG(const EntityItemID&, entityID),1266 Q_ARG(const EntityItemID&, entityID),
1252 Q_ARG(const QString&, methodName),1267 Q_ARG(const QString&, methodName),
@@ -1271,4 +1286,4 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS · 18 unmodified lines
1271 entityScript.property(methodName).call(entityScript, args);1286 entityScript.property(methodName).call(entityScript, args);
1272 }1287 }
1273 }1288 }
1274}1289}