Showing the changes to libraries/script-engine/src/ScriptManager.cpp (1 of 53 files). Show all
Unified · Split
| @@ -24,55 +24,36 @@ · 23 unmodified lines | |||
| 24 | #include <QtCore/QFuture> | 24 | #include <QtCore/QFuture> |
| 25 | #include <QtConcurrent/QtConcurrentRun> | 25 | #include <QtConcurrent/QtConcurrentRun> |
| 26 | 26 | ||
| 27 | #include <QtWidgets/QMainWindow> | ||
| 28 | #include <QtWidgets/QApplication> | ||
| 29 | #include <QtWidgets/QMenuBar> | ||
| 30 | #include <QtWidgets/QMenu> | ||
| 31 | |||
| 32 | #include <QtNetwork/QNetworkRequest> | ||
| 33 | #include <QtNetwork/QNetworkReply> | ||
| 34 | |||
| 35 | #include <shared/LocalFileAccessGate.h> | 27 | #include <shared/LocalFileAccessGate.h> |
| 36 | #include <shared/QtHelpers.h> | ||
| 37 | #include <shared/AbstractLoggerInterface.h> | 28 | #include <shared/AbstractLoggerInterface.h> |
| 38 | #include <AudioConstants.h> | ||
| 39 | #include <AudioEffectOptions.h> | ||
| 40 | #include <AvatarData.h> | ||
| 41 | #include <DebugDraw.h> | 29 | #include <DebugDraw.h> |
| 42 | #include <EntityScriptingInterface.h> | ||
| 43 | #include <MessagesClient.h> | 30 | #include <MessagesClient.h> |
| 44 | #include <NetworkAccessManager.h> | 31 | #include <OctreeConstants.h> |
| 45 | #include <PathUtils.h> | 32 | #include <PathUtils.h> |
| 33 | #include <PortableHighResolutionClock.h> | ||
| 34 | #include <ResourceCache.h> | ||
| 35 | #include <ResourceManager.h> | ||
| 46 | #include <ResourceScriptingInterface.h> | 36 | #include <ResourceScriptingInterface.h> |
| 47 | #include <UserActivityLoggerScriptingInterface.h> | 37 | #include <UserActivityLoggerScriptingInterface.h> |
| 48 | #include <NodeList.h> | 38 | #include <NodeList.h> |
| 49 | #include <ScriptAvatarData.h> | ||
| 50 | #include <udt/PacketHeaders.h> | ||
| 51 | #include <UUID.h> | ||
| 52 | |||
| 53 | #include <controllers/ScriptingInterface.h> | ||
| 54 | #include <AnimationObject.h> | ||
| 55 | 39 | ||
| 56 | #include "AudioScriptingInterface.h" | ||
| 57 | #include "AssetScriptingInterface.h" | 40 | #include "AssetScriptingInterface.h" |
| 58 | #include "BatchLoader.h" | 41 | #include "BatchLoader.h" |
| 59 | #include "EventTypes.h" | 42 | #include "EventTypes.h" |
| 60 | #include "FileScriptingInterface.h" // unzip project | 43 | #include "FileScriptingInterface.h" // unzip project |
| 61 | #include "MenuItemProperties.h" | 44 | #include "MenuItemProperties.h" |
| 62 | #include "ScriptAudioInjector.h" | ||
| 63 | #include "ScriptAvatarData.h" | ||
| 64 | #include "ScriptCache.h" | 45 | #include "ScriptCache.h" |
| 65 | #include "ScriptContext.h" | 46 | #include "ScriptContext.h" |
| 66 | #include "ScriptEngineCast.h" | ||
| 67 | #include "ScriptEngineLogging.h" | ||
| 68 | #include "XMLHttpRequestClass.h" | 47 | #include "XMLHttpRequestClass.h" |
| 69 | #include "WebSocketClass.h" | 48 | #include "WebSocketClass.h" |
| 70 | #include "RecordingScriptingInterface.h" | 49 | #include "ScriptEngine.h" |
| 50 | #include "ScriptEngineCast.h" | ||
| 51 | #include "ScriptEngineLogging.h" | ||
| 71 | #include "ScriptEngines.h" | 52 | #include "ScriptEngines.h" |
| 72 | #include "StackTestScriptingInterface.h" | 53 | #include "StackTestScriptingInterface.h" |
| 73 | #include "ModelScriptingInterface.h" | ||
| 74 | #include "ScriptValue.h" | 54 | #include "ScriptValue.h" |
| 75 | #include "ScriptValueIterator.h" | 55 | #include "ScriptValueIterator.h" |
| 56 | #include "ScriptValueUtils.h" | ||
| 76 | 57 | ||
| 77 | #include <Profile.h> | 58 | #include <Profile.h> |
| 78 | 59 | ||
| @@ -94,8 +75,6 @@ const QString ScriptManager::SCRIPT_BACKTRACE_SEP{ "\n " }; · 15 unmodified lines | |||
| 94 | static const int MAX_MODULE_ID_LENGTH { 4096 }; | 75 | static const int MAX_MODULE_ID_LENGTH { 4096 }; |
| 95 | static const int MAX_DEBUG_VALUE_LENGTH { 80 }; | 76 | static const int MAX_DEBUG_VALUE_LENGTH { 80 }; |
| 96 | 77 | ||
| 97 | static const ScriptEngine::QObjectWrapOptions DEFAULT_QOBJECT_WRAP_OPTIONS = | ||
| 98 | ScriptEngine::ExcludeDeleteLater | ScriptEngine::ExcludeChildObjects; | ||
| 99 | static const ScriptValue::PropertyFlags READONLY_PROP_FLAGS{ ScriptValue::ReadOnly | ScriptValue::Undeletable }; | 78 | static const ScriptValue::PropertyFlags READONLY_PROP_FLAGS{ ScriptValue::ReadOnly | ScriptValue::Undeletable }; |
| 100 | static const ScriptValue::PropertyFlags READONLY_HIDDEN_PROP_FLAGS{ READONLY_PROP_FLAGS | ScriptValue::SkipInEnumeration }; | 79 | static const ScriptValue::PropertyFlags READONLY_HIDDEN_PROP_FLAGS{ READONLY_PROP_FLAGS | ScriptValue::SkipInEnumeration }; |
| 101 | 80 | ||
| @@ -108,6 +87,26 @@ int scriptManagerPointerMetaID = qRegisterMetaType<ScriptManagerPointer>(); · 6 unmodified lines | |||
| 108 | 87 | ||
| 109 | Q_DECLARE_METATYPE(ExternalResource::Bucket); | 88 | Q_DECLARE_METATYPE(ExternalResource::Bucket); |
| 110 | 89 | ||
| 90 | // --- Static script initialization registry | ||
| 91 | |||
| 92 | static ScriptManager::StaticInitializerNode* rootInitializer = nullptr; | ||
| 93 | |||
| 94 | void ScriptManager::registerNewStaticInitializer(StaticInitializerNode* dest) { | ||
| 95 | // this function is assumed to be called on LoadLibrary, where we are explicitly operating in single-threaded mode | ||
| 96 | // Therefore there is no mutex or threadsafety here and the structure is assumed not to change after loading | ||
| 97 | dest->prev = rootInitializer; | ||
| 98 | rootInitializer = dest; | ||
| 99 | } | ||
| 100 | static void runStaticInitializers(ScriptManager* manager) { | ||
| 101 | ScriptManager::StaticInitializerNode* here = rootInitializer; | ||
| 102 | while (here != nullptr) { | ||
| 103 | (*here->init)(manager); | ||
| 104 | here = here->prev; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | // --- | ||
| 109 | |||
| 111 | static ScriptValuePointer debugPrint(ScriptContext* context, ScriptEngine* engine) { | 110 | static ScriptValuePointer debugPrint(ScriptContext* context, ScriptEngine* engine) { |
| 112 | // assemble the message by concatenating our arguments | 111 | // assemble the message by concatenating our arguments |
| 113 | QString message = ""; | 112 | QString message = ""; |
| @@ -172,17 +171,6 @@ static ScriptValuePointer debugPrint(ScriptContext* context, ScriptEngine* engin · 58 unmodified lines | |||
| 172 | return ScriptValuePointer(); | 171 | return ScriptValuePointer(); |
| 173 | } | 172 | } |
| 174 | 173 | ||
| 175 | Q_DECLARE_METATYPE(controller::InputController*) | ||
| 176 | //static int inputControllerPointerId = qRegisterMetaType<controller::InputController*>(); | ||
| 177 | |||
| 178 | ScriptValuePointer inputControllerToScriptValue(ScriptEngine* engine, controller::InputController* const& in) { | ||
| 179 | return engine->newQObject(in, ScriptEngine::QtOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); | ||
| 180 | } | ||
| 181 | |||
| 182 | void inputControllerFromScriptValue(const ScriptValuePointer& object, controller::InputController*& out) { | ||
| 183 | out = qobject_cast<controller::InputController*>(object->toQObject()); | ||
| 184 | } | ||
| 185 | |||
| 186 | // FIXME Come up with a way to properly encode entity IDs in filename | 174 | // FIXME Come up with a way to properly encode entity IDs in filename |
| 187 | // The purpose of the following two function is to embed entity ids into entity script filenames | 175 | // The purpose of the following two function is to embed entity ids into entity script filenames |
| 188 | // so that they show up in stacktraces | 176 | // so that they show up in stacktraces |
| @@ -522,28 +510,6 @@ void ScriptManager::clearDebugLogWindow() { · 333 unmodified lines | |||
| 522 | emit clearDebugWindow(); | 510 | emit clearDebugWindow(); |
| 523 | } | 511 | } |
| 524 | 512 | ||
| 525 | // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of | ||
| 526 | // callAnimationStateHandler requires that the type be registered. | ||
| 527 | // These two are meaningful, if we ever do want to use them... | ||
| 528 | static ScriptValuePointer animVarMapToScriptValue(ScriptEngine* engine, const AnimVariantMap& parameters) { | ||
| 529 | QStringList unused; | ||
| 530 | return parameters.animVariantMapToScriptValue(engine, unused, false); | ||
| 531 | } | ||
| 532 | static void animVarMapFromScriptValue(const ScriptValuePointer& value, AnimVariantMap& parameters) { | ||
| 533 | parameters.animVariantMapFromScriptValue(value); | ||
| 534 | } | ||
| 535 | // ... while these two are not. But none of the four are ever used. | ||
| 536 | static ScriptValuePointer resultHandlerToScriptValue(ScriptEngine* engine, | ||
| 537 | const AnimVariantResultHandler& resultHandler) { | ||
| 538 | qCCritical(scriptengine) << "Attempt to marshall result handler to javascript"; | ||
| 539 | assert(false); | ||
| 540 | return ScriptValuePointer(); | ||
| 541 | } | ||
| 542 | static void resultHandlerFromScriptValue(const ScriptValuePointer& value, AnimVariantResultHandler& resultHandler) { | ||
| 543 | qCCritical(scriptengine) << "Attempt to marshall result handler from javascript"; | ||
| 544 | assert(false); | ||
| 545 | } | ||
| 546 | |||
| 547 | // Templated qScriptRegisterMetaType fails to compile with raw pointers | 513 | // Templated qScriptRegisterMetaType fails to compile with raw pointers |
| 548 | using ScriptableResourceRawPtr = ScriptableResource*; | 514 | using ScriptableResourceRawPtr = ScriptableResource*; |
| 549 | 515 | ||
| @@ -562,13 +528,10 @@ static ScriptValuePointer scriptableResourceToScriptValue(ScriptEngine* engine, · 12 unmodified lines | |||
| 562 | auto manager = engine->manager(); | 528 | auto manager = engine->manager(); |
| 563 | if (data && manager && !resource->isInScript()) { | 529 | if (data && manager && !resource->isInScript()) { |
| 564 | resource->setInScript(true); | 530 | resource->setInScript(true); |
| 565 | QObject::connect(data.data(), SIGNAL(updateSize(qint64)), manager, SLOT(updateMemoryCost(qint64))); | 531 | QObject::connect(data.data(), &Resource::updateSize, manager, &ScriptManager::updateMemoryCost); |
| 566 | } | 532 | } |
| 567 | 533 | ||
| 568 | auto object = engine->newQObject( | 534 | auto object = engine->newQObject(const_cast<ScriptableResourceRawPtr>(resource), ScriptEngine::ScriptOwnership); |
| 569 | const_cast<ScriptableResourceRawPtr>(resource), | ||
| 570 | ScriptEngine::ScriptOwnership, | ||
| 571 | DEFAULT_QOBJECT_WRAP_OPTIONS); | ||
| 572 | return object; | 535 | return object; |
| 573 | } | 536 | } |
| 574 | 537 | ||
| @@ -602,22 +565,12 @@ static ScriptValuePointer createScriptableResourcePrototype(ScriptManagerPointer · 27 unmodified lines | |||
| 602 | } | 565 | } |
| 603 | 566 | ||
| 604 | auto prototypeState = engine->newQObject(state, ScriptEngine::QtOwnership, | 567 | auto prototypeState = engine->newQObject(state, ScriptEngine::QtOwnership, |
| 605 | ScriptEngine::ExcludeDeleteLater | ScriptEngine::ExcludeSlots | ScriptEngine::ExcludeSuperClassMethods); | 568 | ScriptEngine::ExcludeSlots | ScriptEngine::ExcludeSuperClassMethods); |
| 606 | prototype->setProperty("State", prototypeState); | 569 | prototype->setProperty("State", prototypeState); |
| 607 | 570 | ||
| 608 | return prototype; | 571 | return prototype; |
| 609 | } | 572 | } |
| 610 | 573 | ||
| 611 | ScriptValuePointer avatarDataToScriptValue(ScriptEngine* engine, ScriptAvatarData* const& in) { | ||
| 612 | return engine->newQObject(in, ScriptEngine::ScriptOwnership, DEFAULT_QOBJECT_WRAP_OPTIONS); | ||
| 613 | } | ||
| 614 | |||
| 615 | void avatarDataFromScriptValue(const ScriptValuePointer& object, ScriptAvatarData*& out) { | ||
| 616 | // This is not implemented because there are no slots/properties that take an AvatarSharedPointer from a script | ||
| 617 | assert(false); | ||
| 618 | out = nullptr; | ||
| 619 | } | ||
| 620 | |||
| 621 | ScriptValuePointer externalResourceBucketToScriptValue(ScriptEngine* engine, ExternalResource::Bucket const& in) { | 574 | ScriptValuePointer externalResourceBucketToScriptValue(ScriptEngine* engine, ExternalResource::Bucket const& in) { |
| 622 | return engine->newValue((int)in); | 575 | return engine->newValue((int)in); |
| 623 | } | 576 | } |
| @@ -666,27 +619,15 @@ void ScriptManager::init() { · 42 unmodified lines | |||
| 666 | } | 619 | } |
| 667 | 620 | ||
| 668 | _isInitialized = true; | 621 | _isInitialized = true; |
| 669 | auto scriptEngine = _engine.data(); | 622 | runStaticInitializers(this); |
| 670 | 623 | ||
| 671 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | 624 | auto scriptEngine = _engine.data(); |
| 672 | entityScriptingInterface->init(); | ||
| 673 | 625 | ||
| 674 | // register various meta-types | 626 | // register various meta-types |
| 675 | registerMetaTypes(scriptEngine); | ||
| 676 | registerMIDIMetaTypes(scriptEngine); | 627 | registerMIDIMetaTypes(scriptEngine); |
| 677 | registerEventTypes(scriptEngine); | 628 | registerEventTypes(scriptEngine); |
| 678 | registerMenuItemProperties(scriptEngine); | 629 | registerMenuItemProperties(scriptEngine); |
| 679 | registerAnimationTypes(scriptEngine); | 630 | |
| 680 | registerAvatarTypes(scriptEngine); | ||
| 681 | registerAudioMetaTypes(scriptEngine); | ||
| 682 | |||
| 683 | scriptRegisterMetaType(scriptEngine, EntityPropertyFlagsToScriptValue, EntityPropertyFlagsFromScriptValue); | ||
| 684 | scriptRegisterMetaType(scriptEngine, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); | ||
| 685 | scriptRegisterMetaType(scriptEngine, EntityPropertyInfoToScriptValue, EntityPropertyInfoFromScriptValue); | ||
| 686 | scriptRegisterMetaType(scriptEngine, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); | ||
| 687 | scriptRegisterMetaType(scriptEngine, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue); | ||
| 688 | scriptRegisterMetaType(scriptEngine, RayToAvatarIntersectionResultToScriptValue, RayToAvatarIntersectionResultFromScriptValue); | ||
| 689 | scriptRegisterMetaType(scriptEngine, AvatarEntityMapToScriptValue, AvatarEntityMapFromScriptValue); | ||
| 690 | scriptRegisterSequenceMetaType<QVector<QUuid>>(scriptEngine); | 631 | scriptRegisterSequenceMetaType<QVector<QUuid>>(scriptEngine); |
| 691 | scriptRegisterSequenceMetaType<QVector<EntityItemID>>(scriptEngine); | 632 | scriptRegisterSequenceMetaType<QVector<EntityItemID>>(scriptEngine); |
| 692 | 633 | ||
| @@ -709,12 +650,6 @@ void ScriptManager::init() { · 16 unmodified lines | |||
| 709 | */ | 650 | */ |
| 710 | scriptEngine->globalObject()->setProperty("print", scriptEngine->newFunction(debugPrint)); | 651 | scriptEngine->globalObject()->setProperty("print", scriptEngine->newFunction(debugPrint)); |
| 711 | 652 | ||
| 712 | ScriptValuePointer audioEffectOptionsConstructorValue = scriptEngine->newFunction(AudioEffectOptions::constructor); | ||
| 713 | scriptEngine->globalObject()->setProperty("AudioEffectOptions", audioEffectOptionsConstructorValue); | ||
| 714 | |||
| 715 | scriptRegisterMetaType(scriptEngine, injectorToScriptValue, injectorFromScriptValue); | ||
| 716 | scriptRegisterMetaType(scriptEngine, inputControllerToScriptValue, inputControllerFromScriptValue); | ||
| 717 | scriptRegisterMetaType(scriptEngine, avatarDataToScriptValue, avatarDataFromScriptValue); | ||
| 718 | scriptRegisterMetaType(scriptEngine, animationDetailsToScriptValue, animationDetailsFromScriptValue); | 653 | scriptRegisterMetaType(scriptEngine, animationDetailsToScriptValue, animationDetailsFromScriptValue); |
| 719 | scriptRegisterMetaType(scriptEngine, webSocketToScriptValue, webSocketFromScriptValue); | 654 | scriptRegisterMetaType(scriptEngine, webSocketToScriptValue, webSocketFromScriptValue); |
| 720 | scriptRegisterMetaType(scriptEngine, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); | 655 | scriptRegisterMetaType(scriptEngine, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); |
| @@ -737,13 +672,8 @@ void ScriptManager::init() { · 16 unmodified lines | |||
| 737 | scriptRegisterMetaType(scriptEngine, externalResourceBucketToScriptValue, externalResourceBucketFromScriptValue); | 672 | scriptRegisterMetaType(scriptEngine, externalResourceBucketToScriptValue, externalResourceBucketFromScriptValue); |
| 738 | scriptEngine->registerEnum("Script.ExternalPaths", QMetaEnum::fromType<ExternalResource::Bucket>()); | 673 | scriptEngine->registerEnum("Script.ExternalPaths", QMetaEnum::fromType<ExternalResource::Bucket>()); |
| 739 | 674 | ||
| 740 | scriptEngine->registerGlobalObject("Audio", DependencyManager::get<AudioScriptingInterface>().data()); | ||
| 741 | |||
| 742 | scriptEngine->registerGlobalObject("Midi", DependencyManager::get<Midi>().data()); | 675 | scriptEngine->registerGlobalObject("Midi", DependencyManager::get<Midi>().data()); |
| 743 | 676 | ||
| 744 | scriptEngine->registerGlobalObject("Entities", entityScriptingInterface.data()); | ||
| 745 | scriptEngine->registerFunction("Entities", "getMultipleEntityProperties", | ||
| 746 | EntityScriptingInterface::getMultipleEntityProperties); | ||
| 747 | scriptEngine->registerGlobalObject("Quat", &_quatLibrary); | 677 | scriptEngine->registerGlobalObject("Quat", &_quatLibrary); |
| 748 | scriptEngine->registerGlobalObject("Vec3", &_vec3Library); | 678 | scriptEngine->registerGlobalObject("Vec3", &_vec3Library); |
| 749 | scriptEngine->registerGlobalObject("Mat4", &_mat4Library); | 679 | scriptEngine->registerGlobalObject("Mat4", &_mat4Library); |
| @@ -762,9 +692,6 @@ void ScriptManager::init() { · 12 unmodified lines | |||
| 762 | scriptEngine->registerFunction("console", "groupCollapsed", ConsoleScriptingInterface::groupCollapsed, 1); | 692 | scriptEngine->registerFunction("console", "groupCollapsed", ConsoleScriptingInterface::groupCollapsed, 1); |
| 763 | scriptEngine->registerFunction("console", "groupEnd", ConsoleScriptingInterface::groupEnd, 0); | 693 | scriptEngine->registerFunction("console", "groupEnd", ConsoleScriptingInterface::groupEnd, 0); |
| 764 | 694 | ||
| 765 | scriptRegisterMetaType(scriptEngine, animVarMapToScriptValue, animVarMapFromScriptValue); | ||
| 766 | scriptRegisterMetaType(scriptEngine, resultHandlerToScriptValue, resultHandlerFromScriptValue); | ||
| 767 | |||
| 768 | // Scriptable cache access | 695 | // Scriptable cache access |
| 769 | auto resourcePrototype = createScriptableResourcePrototype(qSharedPointerCast<ScriptManager>(sharedFromThis())); | 696 | auto resourcePrototype = createScriptableResourcePrototype(qSharedPointerCast<ScriptManager>(sharedFromThis())); |
| 770 | scriptEngine->globalObject()->setProperty("Resource", resourcePrototype); | 697 | scriptEngine->globalObject()->setProperty("Resource", resourcePrototype); |
| @@ -779,7 +706,6 @@ void ScriptManager::init() { · 8 unmodified lines | |||
| 779 | 706 | ||
| 780 | scriptEngine->registerGlobalObject("DebugDraw", &DebugDraw::getInstance()); | 707 | scriptEngine->registerGlobalObject("DebugDraw", &DebugDraw::getInstance()); |
| 781 | 708 | ||
| 782 | scriptEngine->registerGlobalObject("Model", new ModelScriptingInterface(this)); | ||
| 783 | scriptRegisterMetaType(scriptEngine, meshToScriptValue, meshFromScriptValue); | 709 | scriptRegisterMetaType(scriptEngine, meshToScriptValue, meshFromScriptValue); |
| 784 | scriptRegisterMetaType(scriptEngine, meshesToScriptValue, meshesFromScriptValue); | 710 | scriptRegisterMetaType(scriptEngine, meshesToScriptValue, meshesFromScriptValue); |
| 785 | 711 | ||
| @@ -825,6 +751,22 @@ void ScriptManager::removeEventHandler(const EntityItemID& entityID, const QStri · 39 unmodified lines | |||
| 825 | } | 751 | } |
| 826 | } | 752 | } |
| 827 | } | 753 | } |
| 754 | |||
| 755 | // Unregister all event handlers for the specified entityID (i.e. the entity is being removed) | ||
| 756 | void ScriptManager::removeAllEventHandlers(const EntityItemID& entityID) { | ||
| 757 | if (QThread::currentThread() != thread()) { | ||
| 758 | #ifdef THREAD_DEBUGGING | ||
| 759 | qCDebug(scriptengine) << "*** WARNING *** ScriptManager::removeAllEventHandlers() called on wrong thread [" << QThread::currentThread() << ", correct thread is " << thread() << " ], ignoring " | ||
| 760 | "entityID:" << entityID; | ||
| 761 | #endif | ||
| 762 | return; | ||
| 763 | } | ||
| 764 | |||
| 765 | if (_registeredHandlers.contains(entityID)) { | ||
| 766 | _registeredHandlers.remove(entityID); | ||
| 767 | } | ||
| 768 | } | ||
| 769 | |||
| 828 | // Register the handler. | 770 | // Register the handler. |
| 829 | void ScriptManager::addEventHandler(const EntityItemID& entityID, const QString& eventName, ScriptValuePointer handler) { | 771 | void ScriptManager::addEventHandler(const EntityItemID& entityID, const QString& eventName, ScriptValuePointer handler) { |
| 830 | if (QThread::currentThread() != thread()) { | 772 | if (QThread::currentThread() != thread()) { |
| @@ -843,117 +785,9 @@ void ScriptManager::addEventHandler(const EntityItemID& entityID, const QString& · 12 unmodified lines | |||
| 843 | qCDebug(scriptengine) << "ScriptManager::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; | 785 | qCDebug(scriptengine) << "ScriptManager::addEventHandler() called on thread [" << QThread::currentThread() << "] entityID:" << entityID << " eventName : " << eventName; |
| 844 | #endif | 786 | #endif |
| 845 | 787 | ||
| 846 | if (_registeredHandlers.count() == 0) { // First time any per-entity handler has been added in this script... | 788 | if (_registeredHandlers.count() == 0) { |
| 847 | // Connect up ALL the handlers to the global entities object's signals. | 789 | // First time any per-entity handler has been added in this script... |
| 848 | // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.) | 790 | emit attachDefaultEventHandlers(); |
| 849 | auto entities = DependencyManager::get<EntityScriptingInterface>(); | ||
| 850 | // Bug? These handlers are deleted when entityID is deleted, which is nice. | ||
| 851 | // But if they are created by an entity script on a different entity, should they also be deleted when the entity script unloads? | ||
| 852 | // E.g., suppose a bow has an entity script that causes arrows to be created with a potential lifetime greater than the bow, | ||
| 853 | // and that the entity script adds (e.g., collision) handlers to the arrows. Should those handlers fire if the bow is unloaded? | ||
| 854 | // Also, what about when the entity script is REloaded? | ||
| 855 | // For now, we are leaving them around. Changing that would require some non-trivial digging around to find the | ||
| 856 | // handlers that were added while a given currentEntityIdentifier was in place. I don't think this is dangerous. Just perhaps unexpected. -HRS | ||
| 857 | connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) { | ||
| 858 | _registeredHandlers.remove(entityID); | ||
| 859 | }); | ||
| 860 | |||
| 861 | // Two common cases of event handler, differing only in argument signature. | ||
| 862 | |||
| 863 | /*@jsdoc | ||
| 864 | * Called when an entity event occurs on an entity as registered with {@link Script.addEventHandler}. | ||
| 865 | * @callback Script~entityEventCallback | ||
| 866 | * @param {Uuid} entityID - The ID of the entity the event has occured on. | ||
| 867 | */ | ||
| 868 | using SingleEntityHandler = std::function<void(const EntityItemID&)>; | ||
| 869 | auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler { | ||
| 870 | return [this, eventName](const EntityItemID& entityItemID) { | ||
| 871 | forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(_engine.data()) }); | ||
| 872 | }; | ||
| 873 | }; | ||
| 874 | |||
| 875 | /*@jsdoc | ||
| 876 | * Called when a pointer event occurs on an entity as registered with {@link Script.addEventHandler}. | ||
| 877 | * @callback Script~pointerEventCallback | ||
| 878 | * @param {Uuid} entityID - The ID of the entity the event has occurred on. | ||
| 879 | * @param {PointerEvent} pointerEvent - Details of the event. | ||
| 880 | */ | ||
| 881 | using PointerHandler = std::function<void(const EntityItemID&, const PointerEvent&)>; | ||
| 882 | auto makePointerHandler = [this](QString eventName) -> PointerHandler { | ||
| 883 | return [this, eventName](const EntityItemID& entityItemID, const PointerEvent& event) { | ||
| 884 | if (!EntityTree::areEntityClicksCaptured()) { | ||
| 885 | forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(_engine.data()), event.toScriptValue(_engine.data()) }); | ||
| 886 | } | ||
| 887 | }; | ||
| 888 | }; | ||
| 889 | |||
| 890 | /*@jsdoc | ||
| 891 | * Called when a collision event occurs on an entity as registered with {@link Script.addEventHandler}. | ||
| 892 | * @callback Script~collisionEventCallback | ||
| 893 | * @param {Uuid} entityA - The ID of one entity in the collision. | ||
| 894 | * @param {Uuid} entityB - The ID of the other entity in the collision. | ||
| 895 | * @param {Collision} collisionEvent - Details of the collision. | ||
| 896 | */ | ||
| 897 | using CollisionHandler = std::function<void(const EntityItemID&, const EntityItemID&, const Collision&)>; | ||
| 898 | auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler { | ||
| 899 | return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { | ||
| 900 | forwardHandlerCall(idA, eventName, { idA.toScriptValue(_engine.data()), idB.toScriptValue(_engine.data()), | ||
| 901 | collisionToScriptValue(_engine.data(), collision) }); | ||
| 902 | }; | ||
| 903 | }; | ||
| 904 | |||
| 905 | /*@jsdoc | ||
| 906 | * <p>The name of an entity event. When the entity event occurs, any function that has been registered for that event | ||
| 907 | * via {@link Script.addEventHandler} is called with parameters per the entity event.</p> | ||
| 908 | * <table> | ||
| 909 | * <thead> | ||
| 910 | * <tr><th>Event Name</th><th>Callback Type</th><th>Entity Event</th></tr> | ||
| 911 | * </thead> | ||
| 912 | * <tbody> | ||
| 913 | * <tr><td><code>"enterEntity"</code></td><td>{@link Script~entityEventCallback|entityEventCallback}</td> | ||
| 914 | * <td>{@link Entities.enterEntity}</td></tr> | ||
| 915 | * <tr><td><code>"leaveEntity"</code></td><td>{@link Script~entityEventCallback|entityEventCallback}</td> | ||
| 916 | * <td>{@link Entities.leaveEntity}</td></tr> | ||
| 917 | * <tr><td><code>"mousePressOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | ||
| 918 | * <td>{@link Entities.mousePressOnEntity}</td></tr> | ||
| 919 | * <tr><td><code>"mouseMoveOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | ||
| 920 | * <td>{@link Entities.mouseMoveOnEntity}</td></tr> | ||
| 921 | * <tr><td><code>"mouseReleaseOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | ||
| 922 | * <td>{@link Entities.mouseReleaseOnEntity}</td></tr> | ||
| 923 | * <tr><td><code>"clickDownOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | ||
| 924 | * <td>{@link Entities.clickDownOnEntity}</td></tr> | ||
| 925 | * <tr><td><code>"holdingClickOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | ||
| 926 | * <td>{@link Entities.holdingClickOnEntity}</td></tr> | ||
| 927 | * <tr><td><code>"clickReleaseOnEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | ||
| 928 | * <td>{@link Entities.clickReleaseOnEntity}</td></tr> | ||
| 929 | * <tr><td><code>"hoverEnterEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | ||
| 930 | * <td>{@link Entities.hoverEnterEntity}</td></tr> | ||
| 931 | * <tr><td><code>"hoverOverEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | ||
| 932 | * <td>{@link Entities.hoverOverEntity}</td></tr> | ||
| 933 | * <tr><td><code>"hoverLeaveEntity"</code></td><td>{@link Script~pointerEventCallback|pointerEventCallback}</td> | ||
| 934 | * <td>{@link Entities.hoverLeaveEntity}</td></tr> | ||
| 935 | * <tr><td><code>"collisionWithEntity"</code><td>{@link Script~collisionEventCallback|collisionEventCallback}</td> | ||
| 936 | * </td><td>{@link Entities.collisionWithEntity}</td></tr> | ||
| 937 | * </tbody> | ||
| 938 | * </table> | ||
| 939 | * @typedef {string} Script.EntityEvent | ||
| 940 | */ | ||
| 941 | connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity")); | ||
| 942 | connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity")); | ||
| 943 | |||
| 944 | connect(entities.data(), &EntityScriptingInterface::mousePressOnEntity, this, makePointerHandler("mousePressOnEntity")); | ||
| 945 | connect(entities.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, makePointerHandler("mouseMoveOnEntity")); | ||
| 946 | connect(entities.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, makePointerHandler("mouseReleaseOnEntity")); | ||
| 947 | |||
| 948 | connect(entities.data(), &EntityScriptingInterface::clickDownOnEntity, this, makePointerHandler("clickDownOnEntity")); | ||
| 949 | connect(entities.data(), &EntityScriptingInterface::holdingClickOnEntity, this, makePointerHandler("holdingClickOnEntity")); | ||
| 950 | connect(entities.data(), &EntityScriptingInterface::clickReleaseOnEntity, this, makePointerHandler("clickReleaseOnEntity")); | ||
| 951 | |||
| 952 | connect(entities.data(), &EntityScriptingInterface::hoverEnterEntity, this, makePointerHandler("hoverEnterEntity")); | ||
| 953 | connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makePointerHandler("hoverOverEntity")); | ||
| 954 | connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makePointerHandler("hoverLeaveEntity")); | ||
| 955 | |||
| 956 | connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity")); | ||
| 957 | } | 791 | } |
| 958 | if (!_registeredHandlers.contains(entityID)) { | 792 | if (!_registeredHandlers.contains(entityID)) { |
| 959 | _registeredHandlers[entityID] = RegisteredEventHandlers(); | 793 | _registeredHandlers[entityID] = RegisteredEventHandlers(); |
| @@ -1008,9 +842,6 @@ void ScriptManager::run() { · 48 unmodified lines | |||
| 1008 | clock::time_point startTime = clock::now(); | 842 | clock::time_point startTime = clock::now(); |
| 1009 | int thisFrame = 0; | 843 | int thisFrame = 0; |
| 1010 | 844 | ||
| 1011 | auto nodeList = DependencyManager::get<NodeList>(); | ||
| 1012 | auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); | ||
| 1013 | |||
| 1014 | _lastUpdate = usecTimestampNow(); | 845 | _lastUpdate = usecTimestampNow(); |
| 1015 | 846 | ||
| 1016 | std::chrono::microseconds totalUpdates(0); | 847 | std::chrono::microseconds totalUpdates(0); |
| @@ -1050,7 +881,7 @@ void ScriptManager::run() { · 33 unmodified lines | |||
| 1050 | QEventLoop loop; | 881 | QEventLoop loop; |
| 1051 | QTimer timer; | 882 | QTimer timer; |
| 1052 | timer.setSingleShot(true); | 883 | timer.setSingleShot(true); |
| 1053 | connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); | 884 | connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); |
| 1054 | timer.start(sleepFor.count()); | 885 | timer.start(sleepFor.count()); |
| 1055 | loop.exec(); | 886 | loop.exec(); |
| 1056 | } else { | 887 | } else { |
| @@ -1092,14 +923,8 @@ void ScriptManager::run() { · 35 unmodified lines | |||
| 1092 | break; | 923 | break; |
| 1093 | } | 924 | } |
| 1094 | 925 | ||
| 1095 | if (!_isFinished && entityScriptingInterface->getEntityPacketSender()->serversExist()) { | 926 | if (!_isFinished) { |
| 1096 | // release the queue of edit entity messages. | 927 | emit releaseEntityPacketSenderMessages(false); |
| 1097 | entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages(); | ||
| 1098 | |||
| 1099 | // since we're in non-threaded mode, call process so that the packets are sent | ||
| 1100 | if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) { | ||
| 1101 | entityScriptingInterface->getEntityPacketSender()->process(); | ||
| 1102 | } | ||
| 1103 | } | 928 | } |
| 1104 | 929 | ||
| 1105 | qint64 now = usecTimestampNow(); | 930 | qint64 now = usecTimestampNow(); |
| @@ -1133,21 +958,7 @@ void ScriptManager::run() { · 27 unmodified lines | |||
| 1133 | stopAllTimers(); // make sure all our timers are stopped if the script is ending | 958 | stopAllTimers(); // make sure all our timers are stopped if the script is ending |
| 1134 | emit scriptEnding(); | 959 | emit scriptEnding(); |
| 1135 | 960 | ||
| 1136 | if (entityScriptingInterface->getEntityPacketSender()->serversExist()) { | 961 | emit releaseEntityPacketSenderMessages(true); |
| 1137 | // release the queue of edit entity messages. | ||
| 1138 | entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages(); | ||
| 1139 | |||
| 1140 | // since we're in non-threaded mode, call process so that the packets are sent | ||
| 1141 | if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) { | ||
| 1142 | // wait here till the edit packet sender is completely done sending | ||
| 1143 | while (entityScriptingInterface->getEntityPacketSender()->hasPacketsToSend()) { | ||
| 1144 | entityScriptingInterface->getEntityPacketSender()->process(); | ||
| 1145 | QCoreApplication::processEvents(); | ||
| 1146 | } | ||
| 1147 | } else { | ||
| 1148 | // FIXME - do we need to have a similar "wait here" loop for non-threaded packet senders? | ||
| 1149 | } | ||
| 1150 | } | ||
| 1151 | 962 | ||
| 1152 | emit finished(_fileNameString, qSharedPointerCast<ScriptManager>(sharedFromThis())); | 963 | emit finished(_fileNameString, qSharedPointerCast<ScriptManager>(sharedFromThis())); |
| 1153 | 964 | ||
| @@ -1203,34 +1014,6 @@ void ScriptManager::stop(bool marshal) { · 49 unmodified lines | |||
| 1203 | } | 1014 | } |
| 1204 | } | 1015 | } |
| 1205 | 1016 | ||
| 1206 | // Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread. | ||
| 1207 | void ScriptManager::callAnimationStateHandler(ScriptValuePointer callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) { | ||
| 1208 | if (QThread::currentThread() != thread()) { | ||
| 1209 | #ifdef THREAD_DEBUGGING | ||
| 1210 | qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; | ||
| 1211 | #endif | ||
| 1212 | QMetaObject::invokeMethod(this, "callAnimationStateHandler", | ||
| 1213 | Q_ARG(ScriptValuePointer, callback), | ||
| 1214 | Q_ARG(AnimVariantMap, parameters), | ||
| 1215 | Q_ARG(QStringList, names), | ||
| 1216 | Q_ARG(bool, useNames), | ||
| 1217 | Q_ARG(AnimVariantResultHandler, resultHandler)); | ||
| 1218 | return; | ||
| 1219 | } | ||
| 1220 | ScriptValuePointer javascriptParameters = parameters.animVariantMapToScriptValue(_engine.get(), names, useNames); | ||
| 1221 | ScriptValueList callingArguments; | ||
| 1222 | callingArguments << javascriptParameters; | ||
| 1223 | assert(currentEntityIdentifier.isInvalidID()); // No animation state handlers from entity scripts. | ||
| 1224 | ScriptValuePointer result = callback->call(ScriptValuePointer(), callingArguments); | ||
| 1225 | |||
| 1226 | // validate result from callback function. | ||
| 1227 | if (result->isValid() && result->isObject()) { | ||
| 1228 | resultHandler(result); | ||
| 1229 | } else { | ||
| 1230 | qCWarning(scriptengine) << "ScriptManager::callAnimationStateHandler invalid return argument from callback, expected an object"; | ||
| 1231 | } | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | void ScriptManager::updateMemoryCost(const qint64& deltaSize) { | 1017 | void ScriptManager::updateMemoryCost(const qint64& deltaSize) { |
| 1235 | _engine->updateMemoryCost(deltaSize); | 1018 | _engine->updateMemoryCost(deltaSize); |
| 1236 | } | 1019 | } |