diff --git a/examples/baseball/createBatButton.js b/examples/baseball/createBatButton.js new file mode 100644 index 0000000000..0ffc9cb829 --- /dev/null +++ b/examples/baseball/createBatButton.js @@ -0,0 +1,61 @@ +// +// createBatButton.js +// examples/baseball/moreBatsButton.js +// +// Created by Stephen Birarda on 10/28/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function(){ + this.clickReleaseOnEntity = function(entityID, mouseEvent) { + if (!mouseEvent.isLeftButton) { + return; + } + this.dropBats(); + }; + this.startNearGrabNonColliding = function() { + this.dropBats(); + }; + this.dropBats = function() { + // if the bat box is near us, grab it's position + var nearby = Entities.findEntities(this.position, 20); + + nearby.forEach(function(id) { + var properties = Entities.getEntityProperties(id, ["name", "position"]); + if (properties.name && properties.name == "Bat Box") { + boxPosition = properties.position; + } + }); + + var BAT_DROP_HEIGHT = 2.0; + + var dropPosition; + + if (!boxPosition) { + // we got no bat box position, drop in front of the avatar instead + } else { + // drop the bat above the bat box + dropPosition = Vec3.sum(boxPosition, { x: 0.0, y: BAT_DROP_HEIGHT, z: 0.0}); + } + + var BAT_MODEL = "atp:c47deaae09cca927f6bc9cca0e8bbe77fc618f8c3f2b49899406a63a59f885cb.fbx"; + var BAT_COLLISION_HULL = "atp:9eafceb7510c41d50661130090de7e0632aa4da236ebda84a0059a4be2130e0c.obj"; + + // add the fresh bat at the drop position + var bat = Entities.addEntity({ + name: 'Bat', + type: "Model", + modelURL: BAT_MODEL, + position: dropPosition, + compoundShapeURL: BAT_COLLISION_HULL, + collisionsWillMove: true, + velocity: { x: 0, y: 0.05, z: 0}, // workaround for gravity not taking effect on add + gravity: { x: 0, y: -9.81, z: 0}, + rotation: Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0), + userData: '{"grabbableKey":{"spatialKey":{"relativePosition":{"x":0.9,"y":0,"z":0},"relativeRotation":{"x":0,"y":0,"z":0.4617486000061035,"w":0.8870108127593994}},"kinematicGrab":true}}' + }); + } +}) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 00870c62c6..d63a6357d7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -545,7 +545,7 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { if (!_shouldRender) { return; // exit early } - + Avatar::render(renderArgs, cameraPosition); // don't display IK constraints in shadow mode @@ -1431,7 +1431,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl if (!_skeletonModel.isRenderable()) { return; // wait until all models are loaded } - + fixupModelsInScene(); // Render head so long as the camera isn't inside it diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ec33d22d8d..f5d8c35904 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -273,6 +273,7 @@ public slots: void setEnableAnimGraph(bool isEnabled); void setEnableDebugDrawBindPose(bool isEnabled); void setEnableDebugDrawAnimPose(bool isEnabled); + bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); } void setEnableMeshVisible(bool isEnabled); void setAnimGraphUrl(const QString& url) { _animGraphUrl = url; } @@ -295,7 +296,7 @@ private: virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override; virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override; virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; - void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } + void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); } bool getShouldRenderLocally() const { return _shouldRender; } bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; bool isMyAvatar() const override { return true; } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8cd7351329..4e53f6e60b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -60,13 +60,13 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ message += context->argument(i).toString(); } qCDebug(scriptengine) << "script:print()<<" << message; - + message = message.replace("\\", "\\\\") - .replace("\n", "\\n") - .replace("\r", "\\r") - .replace("'", "\\'"); + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("'", "\\'"); engine->evaluate("Script.print('" + message + "')"); - + return QScriptValue(); } @@ -151,21 +151,21 @@ void ScriptEngine::runInThread() { QThread* workerThread = new QThread(); // thread is not owned, so we need to manage the delete QString scriptEngineName = QString("Script Thread:") + getFilename(); workerThread->setObjectName(scriptEngineName); - + // when the worker thread is started, call our engine's run.. connect(workerThread, &QThread::started, this, &ScriptEngine::run); - + // tell the thread to stop when the script engine is done connect(this, &ScriptEngine::doneRunning, workerThread, &QThread::quit); - + // when the thread is finished, add thread to the deleteLater queue connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); - + // when the thread is finished, add scriptEngine to the deleteLater queue connect(workerThread, &QThread::finished, this, &ScriptEngine::deleteLater); - + moveToThread(workerThread); - + // Starts an event loop, and emits workerThread->started() workerThread->start(); } @@ -178,23 +178,23 @@ bool ScriptEngine::_doneRunningThisScript = false; void ScriptEngine::stopAllScripts(QObject* application) { _allScriptsMutex.lock(); _stoppingAllScripts = true; - + QMutableSetIterator i(_allKnownScriptEngines); while (i.hasNext()) { ScriptEngine* scriptEngine = i.next(); - + QString scriptName = scriptEngine->getFilename(); - + // NOTE: typically all script engines are running. But there's at least one known exception to this, the // "entities sandbox" which is only used to evaluate entities scripts to test their validity before using // them. We don't need to stop scripts that aren't running. if (scriptEngine->isRunning()) { - + // If the script is running, but still evaluating then we need to wait for its evaluation step to // complete. After that we can handle the stop process appropriately if (scriptEngine->evaluatePending()) { while (scriptEngine->evaluatePending()) { - + // This event loop allows any started, but not yet finished evaluate() calls to complete // we need to let these complete so that we can be guaranteed that the script engine isn't // in a partially setup state, which can confuse our shutdown unwinding. @@ -203,21 +203,21 @@ void ScriptEngine::stopAllScripts(QObject* application) { loop.exec(); } } - + // We disconnect any script engine signals from the application because we don't want to do any // extra stopScript/loadScript processing that the Application normally does when scripts start // and stop. We can safely short circuit this because we know we're in the "quitting" process scriptEngine->disconnect(application); - + // Calling stop on the script engine will set it's internal _isFinished state to true, and result // in the ScriptEngine gracefully ending it's run() method. scriptEngine->stop(); - + // We need to wait for the engine to be done running before we proceed, because we don't // want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing // any application state after we leave this stopAllScripts() method scriptEngine->waitTillDoneRunning(); - + // If the script is stopped, we can remove it from our set i.remove(); } @@ -230,15 +230,15 @@ void ScriptEngine::stopAllScripts(QObject* application) { void ScriptEngine::waitTillDoneRunning() { // If the script never started running or finished running before we got here, we don't need to wait for it if (_isRunning) { - + _doneRunningThisScript = false; // NOTE: this is static, we serialize our waiting for scripts to finish - + // NOTE: waitTillDoneRunning() will be called on the main Application thread, inside of stopAllScripts() // we want the application thread to continue to process events, because the scripts will likely need to // marshall messages across to the main thread. For example if they access Settings or Meny in any of their // shutdown code. while (!_doneRunningThisScript) { - + // process events for the main application thread, allowing invokeMethod calls to pass between threads QCoreApplication::processEvents(); } @@ -260,12 +260,12 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { if (_isRunning) { return; } - + _fileNameString = scriptURL.toString(); _isReloading = reload; - + QUrl url(scriptURL); - + bool isPending; auto scriptCache = DependencyManager::get(); scriptCache->getScript(url, this, isPending, reload); @@ -312,12 +312,12 @@ void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once } - + _isInitialized = true; auto entityScriptingInterface = DependencyManager::get(); entityScriptingInterface->init(); - + // register various meta-types registerMetaTypes(this); registerMIDIMetaTypes(this); @@ -326,30 +326,30 @@ void ScriptEngine::init() { registerAnimationTypes(this); registerAvatarTypes(this); registerAudioMetaTypes(this); - + qScriptRegisterMetaType(this, EntityPropertyFlagsToScriptValue, EntityPropertyFlagsFromScriptValue); qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue); qScriptRegisterSequenceMetaType>(this); qScriptRegisterSequenceMetaType>(this); - + qScriptRegisterSequenceMetaType >(this); qScriptRegisterSequenceMetaType >(this); qScriptRegisterSequenceMetaType >(this); - + QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor); globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); - + QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); globalObject().setProperty("WebSocket", webSocketConstructorValue); - + QScriptValue printConstructorValue = newFunction(debugPrint); globalObject().setProperty("print", printConstructorValue); - + QScriptValue audioEffectOptionsConstructorValue = newFunction(AudioEffectOptions::constructor); globalObject().setProperty("AudioEffectOptions", audioEffectOptionsConstructorValue); - + qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue); qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue); @@ -357,7 +357,7 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue); qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue); - + registerGlobalObject("Script", this); registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); registerGlobalObject("Entities", entityScriptingInterface.data()); @@ -382,15 +382,15 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; #endif QMetaObject::invokeMethod(this, "registerValue", - Q_ARG(const QString&, valueName), - Q_ARG(QScriptValue, value)); + Q_ARG(const QString&, valueName), + Q_ARG(QScriptValue, value)); return; } - + QStringList pathToValue = valueName.split("."); int partsToGo = pathToValue.length(); QScriptValue partObject = globalObject(); - + for (const auto& pathPart : pathToValue) { partsToGo--; if (!partObject.property(pathPart).isValid()) { @@ -409,18 +409,18 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::registerGlobalObject() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; - #endif +#endif QMetaObject::invokeMethod(this, "registerGlobalObject", - Q_ARG(const QString&, name), - Q_ARG(QObject*, object)); + Q_ARG(const QString&, name), + Q_ARG(QObject*, object)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::registerGlobalObject() called on thread [" << QThread::currentThread() << "] name:" << name; - #endif - +#endif + if (!globalObject().property(name).isValid()) { if (object) { QScriptValue value = newQObject(object); @@ -433,38 +433,38 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; - #endif +#endif QMetaObject::invokeMethod(this, "registerFunction", - Q_ARG(const QString&, name), - Q_ARG(QScriptEngine::FunctionSignature, functionSignature), - Q_ARG(int, numArguments)); + Q_ARG(const QString&, name), + Q_ARG(QScriptEngine::FunctionSignature, functionSignature), + Q_ARG(int, numArguments)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] name:" << name; - #endif - +#endif + QScriptValue scriptFun = newFunction(functionSignature, numArguments); globalObject().setProperty(name, scriptFun); } void ScriptEngine::registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::registerFunction() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] parent:" << parent << "name:" << name; - #endif +#endif QMetaObject::invokeMethod(this, "registerFunction", - Q_ARG(const QString&, name), - Q_ARG(QScriptEngine::FunctionSignature, functionSignature), - Q_ARG(int, numArguments)); + Q_ARG(const QString&, name), + Q_ARG(QScriptEngine::FunctionSignature, functionSignature), + Q_ARG(int, numArguments)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::registerFunction() called on thread [" << QThread::currentThread() << "] parent:" << parent << "name:" << name; - #endif - +#endif + QScriptValue object = globalObject().property(parent); if (object.isValid()) { QScriptValue scriptFun = newFunction(functionSignature, numArguments); @@ -473,26 +473,26 @@ void ScriptEngine::registerFunction(const QString& parent, const QString& name, } void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, - QScriptEngine::FunctionSignature setter, const QString& parent) { + QScriptEngine::FunctionSignature setter, const QString& parent) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::registerGetterSetter() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - " name:" << name << "parent:" << parent; - #endif + " name:" << name << "parent:" << parent; +#endif QMetaObject::invokeMethod(this, "registerGetterSetter", - Q_ARG(const QString&, name), - Q_ARG(QScriptEngine::FunctionSignature, getter), - Q_ARG(QScriptEngine::FunctionSignature, setter), - Q_ARG(const QString&, parent)); + Q_ARG(const QString&, name), + Q_ARG(QScriptEngine::FunctionSignature, getter), + Q_ARG(QScriptEngine::FunctionSignature, setter), + Q_ARG(const QString&, parent)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::registerGetterSetter() called on thread [" << QThread::currentThread() << "] name:" << name << "parent:" << parent; - #endif - +#endif + QScriptValue setterFunction = newFunction(setter, 1); QScriptValue getterFunction = newFunction(getter); - + if (!parent.isNull() && !parent.isEmpty()) { QScriptValue object = globalObject().property(parent); if (object.isValid()) { @@ -508,20 +508,20 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func // Unregister the handlers for this eventName and entityID. void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "entityID:" << entityID << " eventName:" << eventName; - #endif + "entityID:" << entityID << " eventName:" << eventName; +#endif QMetaObject::invokeMethod(this, "removeEventHandler", - Q_ARG(const EntityItemID&, entityID), - Q_ARG(const QString&, eventName), - Q_ARG(QScriptValue, handler)); + Q_ARG(const EntityItemID&, entityID), + Q_ARG(const QString&, eventName), + Q_ARG(QScriptValue, handler)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::removeEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; - #endif - +#endif + if (!_registeredHandlers.contains(entityID)) { return; } @@ -538,21 +538,21 @@ void ScriptEngine::removeEventHandler(const EntityItemID& entityID, const QStrin // Register the handler. void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::addEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "entityID:" << entityID << " eventName:" << eventName; - #endif - + "entityID:" << entityID << " eventName:" << eventName; +#endif + QMetaObject::invokeMethod(this, "addEventHandler", - Q_ARG(const EntityItemID&, entityID), - Q_ARG(const QString&, eventName), - Q_ARG(QScriptValue, handler)); + Q_ARG(const EntityItemID&, entityID), + Q_ARG(const QString&, eventName), + Q_ARG(QScriptValue, handler)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; - #endif - +#endif + if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script... // Connect up ALL the handlers to the global entities object's signals. // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.) @@ -586,19 +586,19 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity")); connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity")); - + connect(entities.data(), &EntityScriptingInterface::mousePressOnEntity, this, makeMouseHandler("mousePressOnEntity")); connect(entities.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, makeMouseHandler("mouseMoveOnEntity")); connect(entities.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, makeMouseHandler("mouseReleaseOnEntity")); - + connect(entities.data(), &EntityScriptingInterface::clickDownOnEntity, this, makeMouseHandler("clickDownOnEntity")); connect(entities.data(), &EntityScriptingInterface::holdingClickOnEntity, this, makeMouseHandler("holdingClickOnEntity")); connect(entities.data(), &EntityScriptingInterface::clickReleaseOnEntity, this, makeMouseHandler("clickReleaseOnEntity")); - + connect(entities.data(), &EntityScriptingInterface::hoverEnterEntity, this, makeMouseHandler("hoverEnterEntity")); connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity")); connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity")); - + connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity")); } if (!_registeredHandlers.contains(entityID)) { @@ -613,18 +613,18 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi if (_stoppingAllScripts) { return QScriptValue(); // bail early } - + if (QThread::currentThread() != thread()) { QScriptValue result; - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber; - #endif + "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber; +#endif QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, result), - Q_ARG(const QString&, sourceCode), - Q_ARG(const QString&, fileName), - Q_ARG(int, lineNumber)); + Q_RETURN_ARG(QScriptValue, result), + Q_ARG(const QString&, sourceCode), + Q_ARG(const QString&, fileName), + Q_ARG(int, lineNumber)); return result; } @@ -633,7 +633,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi if (!hasCorrectSyntax(program)) { return QScriptValue(); } - + ++_evaluatesPending; const auto result = QScriptEngine::evaluate(program); --_evaluatesPending; @@ -648,7 +648,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi void ScriptEngine::run() { // TODO: can we add a short circuit for _stoppingAllScripts here? What does it mean to not start running if // we're in the process of stopping? - + if (!_isInitialized) { init(); } @@ -658,48 +658,48 @@ void ScriptEngine::run() { if (_wantSignals) { emit runningStateChanged(); } - + QScriptValue result = evaluate(_scriptContents, _fileNameString); - + QElapsedTimer startTime; startTime.start(); - + int thisFrame = 0; - + auto nodeList = DependencyManager::get(); auto entityScriptingInterface = DependencyManager::get(); - + qint64 lastUpdate = usecTimestampNow(); - + while (!_isFinished) { int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec if (usecToSleep > 0) { usleep(usecToSleep); } - + if (_isFinished) { break; } - + QCoreApplication::processEvents(); - + if (_isFinished) { break; } - + if (!_isFinished && entityScriptingInterface->getEntityPacketSender()->serversExist()) { // release the queue of edit entity messages. entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages(); - + // since we're in non-threaded mode, call process so that the packets are sent if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) { entityScriptingInterface->getEntityPacketSender()->process(); } } - + qint64 now = usecTimestampNow(); float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND; - + if (!_isFinished) { if (_wantSignals) { emit update(deltaTime); @@ -710,16 +710,16 @@ void ScriptEngine::run() { // Debug and clear exceptions hadUncaughtExceptions(*this, _fileNameString); } - + stopAllTimers(); // make sure all our timers are stopped if the script is ending if (_wantSignals) { emit scriptEnding(); } - + if (entityScriptingInterface->getEntityPacketSender()->serversExist()) { // release the queue of edit entity messages. entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages(); - + // since we're in non-threaded mode, call process so that the packets are sent if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) { // wait here till the edit packet sender is completely done sending @@ -731,17 +731,17 @@ void ScriptEngine::run() { // FIXME - do we need to have a similar "wait here" loop for non-threaded packet senders? } } - + if (_wantSignals) { emit finished(_fileNameString); } - + _isRunning = false; if (_wantSignals) { emit runningStateChanged(); emit doneRunning(); } - + _doneRunningThisScript = true; } @@ -789,13 +789,13 @@ void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantM void ScriptEngine::timerFired() { QTimer* callingTimer = reinterpret_cast(sender()); QScriptValue timerFunction = _timerFunctionMap.value(callingTimer); - + if (!callingTimer->isActive()) { // this timer is done, we can kill it _timerFunctionMap.remove(callingTimer); delete callingTimer; } - + // call the associated JS function, if it exists if (timerFunction.isValid()) { timerFunction.call(); @@ -806,14 +806,14 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int // create the timer, add it to the map, and start it QTimer* newTimer = new QTimer(this); newTimer->setSingleShot(isSingleShot); - + connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired); - + // make sure the timer stops when the script does connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop); - + _timerFunctionMap.insert(newTimer, function); - + newTimer->start(intervalMS); return newTimer; } @@ -823,7 +823,7 @@ QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) qCDebug(scriptengine) << "Script.setInterval() while shutting down is ignored... parent script:" << getFilename(); return NULL; // bail early } - + return setupTimerWithInterval(function, intervalMS, false); } @@ -832,7 +832,7 @@ QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) { qCDebug(scriptengine) << "Script.setTimeout() while shutting down is ignored... parent script:" << getFilename(); return NULL; // bail early } - + return setupTimerWithInterval(function, timeoutMS, true); } @@ -850,7 +850,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { if (!url.scheme().isEmpty()) { return url; } - + // we apparently weren't a fully qualified url, so, let's assume we're relative // to the original URL of our script QUrl parentURL; @@ -863,7 +863,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { if (parentURL.scheme().isEmpty()) { parentURL = QUrl::fromLocalFile(_fileNameString); } - + // at this point we should have a legitimate fully qualified URL for our parent url = parentURL.resolved(url); return url; @@ -882,7 +882,7 @@ void ScriptEngine::print(const QString& message) { void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { if (_stoppingAllScripts) { qCDebug(scriptengine) << "Script.include() while shutting down is ignored..." - << "includeFiles:" << includeFiles << "parent script:" << getFilename(); + << "includeFiles:" << includeFiles << "parent script:" << getFilename(); return; // bail early } QList urls; @@ -896,9 +896,9 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL; } } - + BatchLoader* loader = new BatchLoader(urls); - + auto evaluateScripts = [=](const QMap& data) { for (QUrl url : urls) { QString contents = data[url]; @@ -908,21 +908,21 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac QScriptValue result = evaluate(contents, url.toString()); } } - + if (callback.isFunction()) { QScriptValue(callback).call(); } - + loader->deleteLater(); }; - + connect(loader, &BatchLoader::finished, this, evaluateScripts); - + // If we are destroyed before the loader completes, make sure to clean it up connect(this, &QObject::destroyed, loader, &QObject::deleteLater); - + loader->start(); - + if (!callback.isFunction() && !loader->isFinished()) { QEventLoop loop; QObject::connect(loader, &BatchLoader::finished, &loop, &QEventLoop::quit); @@ -933,10 +933,10 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { if (_stoppingAllScripts) { qCDebug(scriptengine) << "Script.include() while shutting down is ignored... " - << "includeFile:" << includeFile << "parent script:" << getFilename(); + << "includeFile:" << includeFile << "parent script:" << getFilename(); return; // bail early } - + QStringList urls; urls.append(includeFile); include(urls, callback); @@ -948,10 +948,10 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { void ScriptEngine::load(const QString& loadFile) { if (_stoppingAllScripts) { qCDebug(scriptengine) << "Script.load() while shutting down is ignored... " - << "loadFile:" << loadFile << "parent script:" << getFilename(); + << "loadFile:" << loadFile << "parent script:" << getFilename(); return; // bail early } - + QUrl url = resolvePath(loadFile); if (_isReloading) { auto scriptCache = DependencyManager::get(); @@ -966,37 +966,6 @@ void ScriptEngine::load(const QString& loadFile) { } } -bool ScriptEngine::checkSyntax(const QScriptProgram& program) { - const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); - if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { - const auto error = syntaxCheck.errorMessage(); - const auto line = QString::number(syntaxCheck.errorLineNumber()); - const auto column = QString::number(syntaxCheck.errorColumnNumber()); - const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column); - qCWarning(scriptengine) << qPrintable(message); - return false; - } - return true; -} - -bool ScriptEngine::checkExceptions(QScriptEngine& engine, const QString& fileName) { - if (engine.hasUncaughtException()) { - const auto backtrace = engine.uncaughtExceptionBacktrace(); - const auto exception = engine.uncaughtException().toString(); - const auto line = QString::number(engine.uncaughtExceptionLineNumber()); - engine.clearExceptions(); - - auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); - if (!backtrace.empty()) { - static const auto lineSeparator = "\n "; - message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); - } - qCWarning(scriptengine) << qPrintable(message); - return false; - } - return true; -} - // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) { if (QThread::currentThread() != thread()) { @@ -1023,76 +992,75 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin // for the download void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::loadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "entityID:" << entityID << "entityScript:" << entityScript <<"forceRedownload:" << forceRedownload; - #endif - + "entityID:" << entityID << "entityScript:" << entityScript <<"forceRedownload:" << forceRedownload; +#endif + QMetaObject::invokeMethod(this, "loadEntityScript", - Q_ARG(const EntityItemID&, entityID), - Q_ARG(const QString&, entityScript), - Q_ARG(bool, forceRedownload)); + Q_ARG(const EntityItemID&, entityID), + Q_ARG(const QString&, entityScript), + Q_ARG(bool, forceRedownload)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::loadEntityScript() called on correct thread [" << thread() << "] " - "entityID:" << entityID << "entityScript:" << entityScript << "forceRedownload:" << forceRedownload; - #endif - + "entityID:" << entityID << "entityScript:" << entityScript << "forceRedownload:" << forceRedownload; +#endif + // If we've been called our known entityScripts should not know about us.. assert(!_entityScripts.contains(entityID)); - - #ifdef THREAD_DEBUGGING + +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::loadEntityScript() calling scriptCache->getScriptContents() on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; - #endif +#endif DependencyManager::get()->getScriptContents(entityScript, [=](const QString& scriptOrURL, const QString& contents, bool isURL, bool success) { - #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; - #endif - - this->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success); - }, forceRedownload); +#ifdef THREAD_DEBUGGING + qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; +#endif + + this->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success); + }, forceRedownload); } // since all of these operations can be asynch we will always do the actual work in the response handler // for the download void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::entityScriptContentAvailable() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:" << contents << "isURL:" << isURL << "success:" << success; - #endif - + "entityID:" << entityID << "scriptOrURL:" << scriptOrURL << "contents:" << contents << "isURL:" << isURL << "success:" << success; +#endif + QMetaObject::invokeMethod(this, "entityScriptContentAvailable", - Q_ARG(const EntityItemID&, entityID), - Q_ARG(const QString&, scriptOrURL), - Q_ARG(const QString&, contents), - Q_ARG(bool, isURL), - Q_ARG(bool, success)); + Q_ARG(const EntityItemID&, entityID), + Q_ARG(const QString&, scriptOrURL), + Q_ARG(const QString&, contents), + Q_ARG(bool, isURL), + Q_ARG(bool, success)); return; } - - #ifdef THREAD_DEBUGGING + +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::entityScriptContentAvailable() thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; - #endif - +#endif + auto scriptCache = DependencyManager::get(); bool isFileUrl = isURL && scriptOrURL.startsWith("file://"); auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); - + QScriptProgram program(contents, fileName); - if (!hasCorrectSyntax(program)) { if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); } return; // done processing script } - + if (isURL) { setParentURL(scriptOrURL); } - + QScriptEngine sandbox; QScriptValue testConstructor = sandbox.evaluate(program); if (hadUncaughtExceptions(sandbox, program.fileName())) { @@ -1101,20 +1069,20 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (!testConstructor.isFunction()) { qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n" - " NOT CONSTRUCTOR\n" - " SCRIPT:" << scriptOrURL; + " NOT CONSTRUCTOR\n" + " SCRIPT:" << scriptOrURL; if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); } return; // done processing script } - + int64_t lastModified = 0; if (isFileUrl) { QString file = QUrl(scriptOrURL).toLocalFile(); lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch(); } - + QScriptValue entityScriptConstructor = evaluate(contents, fileName); QScriptValue entityScriptObject = entityScriptConstructor.construct(); EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified }; @@ -1122,27 +1090,27 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co if (isURL) { setParentURL(""); } - + // if we got this far, then call the preload method callEntityScriptMethod(entityID, "preload"); } void ScriptEngine::unloadEntityScript(const EntityItemID& entityID) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "entityID:" << entityID; - #endif - + "entityID:" << entityID; +#endif + QMetaObject::invokeMethod(this, "unloadEntityScript", - Q_ARG(const EntityItemID&, entityID)); + Q_ARG(const EntityItemID&, entityID)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::unloadEntityScript() called on correct thread [" << thread() << "] " - "entityID:" << entityID; - #endif - + "entityID:" << entityID; +#endif + if (_entityScripts.contains(entityID)) { callEntityScriptMethod(entityID, "unload"); _entityScripts.remove(entityID); @@ -1154,7 +1122,7 @@ void ScriptEngine::unloadAllEntityScripts() { #ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; #endif - + QMetaObject::invokeMethod(this, "unloadAllEntityScripts"); return; } @@ -1171,13 +1139,13 @@ 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) { @@ -1185,7 +1153,7 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { 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(); @@ -1204,21 +1172,21 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "entityID:" << entityID << "methodName:" << methodName; - #endif - + "entityID:" << entityID << "methodName:" << methodName; +#endif + QMetaObject::invokeMethod(this, "callEntityScriptMethod", - Q_ARG(const EntityItemID&, entityID), - Q_ARG(const QString&, methodName)); + Q_ARG(const EntityItemID&, entityID), + Q_ARG(const QString&, methodName)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " - "entityID:" << entityID << "methodName:" << methodName; - #endif - + "entityID:" << entityID << "methodName:" << methodName; +#endif + refreshFileScript(entityID); if (_entityScripts.contains(entityID)) { EntityScriptDetails details = _entityScripts[entityID]; @@ -1228,28 +1196,28 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS args << entityID.toScriptValue(this); entityScript.property(methodName).call(entityScript, args); } - + } } void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent"; - #endif - + "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent"; +#endif + QMetaObject::invokeMethod(this, "callEntityScriptMethod", - Q_ARG(const EntityItemID&, entityID), - Q_ARG(const QString&, methodName), - Q_ARG(const MouseEvent&, event)); + Q_ARG(const EntityItemID&, entityID), + Q_ARG(const QString&, methodName), + Q_ARG(const MouseEvent&, event)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " - "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent"; - #endif - + "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent"; +#endif + refreshFileScript(entityID); if (_entityScripts.contains(entityID)) { EntityScriptDetails details = _entityScripts[entityID]; @@ -1266,23 +1234,23 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision) { if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; - #endif - + "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; +#endif + QMetaObject::invokeMethod(this, "callEntityScriptMethod", - Q_ARG(const EntityItemID&, entityID), - Q_ARG(const QString&, methodName), - Q_ARG(const EntityItemID&, otherID), - Q_ARG(const Collision&, collision)); + Q_ARG(const EntityItemID&, entityID), + Q_ARG(const QString&, methodName), + Q_ARG(const EntityItemID&, otherID), + Q_ARG(const Collision&, collision)); return; } - #ifdef THREAD_DEBUGGING +#ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " - "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; - #endif - + "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; +#endif + refreshFileScript(entityID); if (_entityScripts.contains(entityID)) { EntityScriptDetails details = _entityScripts[entityID]; @@ -1295,4 +1263,4 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS entityScript.property(methodName).call(entityScript, args); } } -} +} \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 43fbdc1b0e..8ec06dd89d 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -55,62 +55,62 @@ public: ScriptEngine(const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString(""), bool wantSignals = true); - + ~ScriptEngine(); - + /// run the script in a dedicated thread. This will have the side effect of evalulating /// the current script contents and calling run(). Callers will likely want to register the script with external /// services before calling this. void runInThread(); - + /// run the script in the callers thread, exit when stop() is called. void run(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can // properly ensure they are only called on the correct thread - + /// registers a global object by name Q_INVOKABLE void registerGlobalObject(const QString& name, QObject* object); - + /// registers a global getter/setter Q_INVOKABLE void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, - QScriptEngine::FunctionSignature setter, const QString& parent = QString("")); + QScriptEngine::FunctionSignature setter, const QString& parent = QString("")); /// register a global function Q_INVOKABLE void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1); - + /// register a function as a method on a previously registered global object Q_INVOKABLE void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature fun, - int numArguments = -1); - + int numArguments = -1); + /// registers a global object by name Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value); - + /// evaluate some code in the context of the ScriptEngine and return the result Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget - + /// if the script engine is not already running, this will download the URL and start the process of seting it up - /// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed + /// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed /// to scripts. we may not need this to be invokable void loadURL(const QUrl& scriptURL, bool reload); - + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // NOTE - these are intended to be public interfaces available to scripts + // NOTE - these are intended to be public interfaces available to scripts Q_INVOKABLE void addEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler); Q_INVOKABLE void removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler); - + Q_INVOKABLE void load(const QString& loadfile); Q_INVOKABLE void include(const QStringList& includeFiles, QScriptValue callback = QScriptValue()); Q_INVOKABLE void include(const QString& includeFile, QScriptValue callback = QScriptValue()); - + Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS); Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS); Q_INVOKABLE void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); } Q_INVOKABLE void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); } Q_INVOKABLE void print(const QString& message); Q_INVOKABLE QUrl resolvePath(const QString& path) const; - + // Entity Script Related methods Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload = false); // will call the preload method once loaded Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method @@ -118,33 +118,33 @@ public: Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); - + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts Q_INVOKABLE void stop(); - + bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget bool isRunning() const { return _isRunning; } // used by ScriptWidget - + static void stopAllScripts(QObject* application); // used by Application on shutdown - + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - These are the callback implementations for ScriptUser the get called by ScriptCache when the contents // of a script are available. virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents); virtual void errorInLoadingScript(const QUrl& url); - + // These are currently used by Application to track if a script is user loaded or not. Consider finding a solution // inside of Application so that the ScriptEngine class is not polluted by this notion void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; } bool isUserLoaded() const { return _isUserLoaded; } - + // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } - + public slots: void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); - + signals: void scriptLoaded(const QString& scriptFilename); void errorLoadingScript(const QString& scriptFilename); @@ -159,7 +159,7 @@ signals: void loadScript(const QString& scriptName, bool isUserLoaded); void reloadScript(const QString& scriptName, bool isUserLoaded); void doneRunning(); - + protected: QString _scriptContents; QString _parentURL; @@ -179,30 +179,30 @@ private: void timerFired(); void stopAllTimers(); void refreshFileScript(const EntityItemID& entityID); - + void setParentURL(const QString& parentURL) { _parentURL = parentURL; } - + QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); - + QString _fileNameString; Quat _quatLibrary; Vec3 _vec3Library; ScriptUUID _uuidLibrary; bool _isUserLoaded; bool _isReloading; - + ArrayBufferClass* _arrayBufferClass; - + QHash _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); - + static QSet _allKnownScriptEngines; static QMutex _allScriptsMutex; static bool _stoppingAllScripts; static bool _doneRunningThisScript; - + }; -#endif // hifi_ScriptEngine_h +#endif // hifi_ScriptEngine_h \ No newline at end of file