History

script entities

HifiExperiments committed 1 year ago

Commit efd26391 parent e9d4e2738 files · +1661 −143Browse files

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

Unified · Split

libraries/script-engine/src/ScriptManager.cpp+291−77
@@ -1929,33 +1929,35 @@ void ScriptManager::forwardHandlerCall(const EntityItemID& entityID, const QStri · 1928 unmodified lines
19291929 int ScriptManager::getNumRunningEntityScripts() const {
19301930 QReadLocker locker { &_entityScriptsLock };
19311931 int sum = 0;
1932− for (const auto& st : _entityScripts) {
1933− if (st.status == EntityScriptStatus::RUNNING) {
1934− ++sum;
1932+ for (const auto& entityScripts : _entityScripts) {
1933+ for (const auto& script : entityScripts) {
1934+ if (script.status == EntityScriptStatus::RUNNING) {
1935+ ++sum;
1936+ }
19351937 }
19361938 }
19371939 return sum;
19381940 }
19391941
1940−void ScriptManager::setEntityScriptDetails(const EntityItemID& entityID, const EntityScriptDetails& details) {
1942+void ScriptManager::setEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL, const EntityScriptDetails& details) {
19411943 {
19421944 QWriteLocker locker { &_entityScriptsLock };
1943− _entityScripts[entityID] = details;
1945+ _entityScripts[entityID][scriptURL] = details;
19441946 }
19451947 emit entityScriptDetailsUpdated();
19461948 }
19471949
1948−void ScriptManager::updateEntityScriptStatus(const EntityItemID& entityID, const EntityScriptStatus &status, const QString& errorInfo) {
1950+void ScriptManager::updateEntityScriptStatus(const EntityItemID& entityID, const QString& scriptURL, const EntityScriptStatus &status, const QString& errorInfo) {
19491951 {
19501952 QWriteLocker locker { &_entityScriptsLock };
1951− EntityScriptDetails& details = _entityScripts[entityID];
1953+ EntityScriptDetails& details = _entityScripts[entityID][scriptURL];
19521954 details.status = status;
19531955 details.errorInfo = errorInfo;
19541956 }
19551957 emit entityScriptDetailsUpdated();
19561958 }
19571959
1958−QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) {
1960+QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL) {
19591961 static const QVariant NULL_VARIANT = QVariant::fromValue(nullptr);
19601962 QVariantMap map;
19611963 if (entityID.isNull()) {
@@ -1967,11 +1969,11 @@ QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) { · 5 unmodified lines
19671969 qCDebug(scriptengine) << "cloneEntityScriptDetails" << entityID << QThread::currentThread();
19681970 #endif
19691971 EntityScriptDetails scriptDetails;
1970− if (getEntityScriptDetails(entityID, scriptDetails)) {
1972+ if (getEntityScriptDetails(entityID, scriptURL, scriptDetails)) {
19711973 #ifdef DEBUG_ENTITY_STATES
19721974 qCDebug(scriptengine) << "gotEntityScriptDetails" << scriptDetails.status << QThread::currentThread();
19731975 #endif
1974− map["isRunning"] = isEntityScriptRunning(entityID);
1976+ map["isRunning"] = isEntityScriptRunning(entityID, scriptURL);
19751977 map["status"] = EntityScriptStatus_::valueToKey(scriptDetails.status).toLower();
19761978 map["errorInfo"] = scriptDetails.errorInfo;
19771979 map["entityID"] = entityID.toString();
@@ -1997,23 +1999,31 @@ QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) { · 19 unmodified lines
19971999 return map;
19982000 }
19992001
2000−QFuture<QVariant> ScriptManager::getLocalEntityScriptDetails(const EntityItemID& entityID) {
2001− return QtConcurrent::run(this, &ScriptManager::cloneEntityScriptDetails, entityID);
2002+QFuture<QVariant> ScriptManager::getLocalEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL) {
2003+ return QtConcurrent::run(this, &ScriptManager::cloneEntityScriptDetails, entityID, scriptURL);
20022004 }
20032005
2004−bool ScriptManager::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const {
2006+bool ScriptManager::getEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL, EntityScriptDetails &details) const {
20052007 QReadLocker locker { &_entityScriptsLock };
20062008 auto it = _entityScripts.constFind(entityID);
20072009 if (it == _entityScripts.constEnd()) {
20082010 return false;
20092011 }
2010− details = it.value();
2012+ auto it2 = it.value().constFind(scriptURL);
2013+ if (it2 == it.value().constEnd()) {
2014+ return false;
2015+ }
2016+ details = it2.value();
20112017 return true;
20122018 }
20132019
2014−bool ScriptManager::hasEntityScriptDetails(const EntityItemID& entityID) const {
2020+bool ScriptManager::hasEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL) const {
20152021 QReadLocker locker { &_entityScriptsLock };
2016− return _entityScripts.contains(entityID);
2022+ auto it = _entityScripts.constFind(entityID);
2023+ if (it == _entityScripts.constEnd()) {
2024+ return false;
2025+ }
2026+ return it.value().contains(scriptURL);
20172027 }
20182028
20192029 void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
@@ -2034,16 +2044,16 @@ void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString · 14 unmodified lines
20342044 return;
20352045 }
20362046
2037− if (!hasEntityScriptDetails(entityID)) {
2047+ if (!hasEntityScriptDetails(entityID, entityScript)) {
20382048 // make sure EntityScriptDetails has an entry for this UUID right away
20392049 // (which allows bailing from the loading/provisioning process early if the Entity gets deleted mid-flight)
2040− updateEntityScriptStatus(entityID, EntityScriptStatus::PENDING, "...pending...");
2050+ updateEntityScriptStatus(entityID, entityScript, EntityScriptStatus::PENDING, "...pending...");
20412051 }
20422052
20432053 #ifdef DEBUG_ENTITY_STATES
20442054 {
20452055 EntityScriptDetails details;
2046− bool hasEntityScript = getEntityScriptDetails(entityID, details);
2056+ bool hasEntityScript = getEntityScriptDetails(entityID, entityScript, details);
20472057 qCDebug(scriptengine) << "loadEntityScript.LOADING: " << entityID.toString()
20482058 << "(previous: " << (hasEntityScript ? details.status : EntityScriptStatus::PENDING) << ")";
20492059 }
@@ -2053,7 +2063,7 @@ void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString · 3 unmodified lines
20532063 newDetails.scriptText = entityScript;
20542064 newDetails.status = EntityScriptStatus::LOADING;
20552065 newDetails.definingSandboxURL = currentSandboxURL;
2056− setEntityScriptDetails(entityID, newDetails);
2066+ setEntityScriptDetails(entityID, entityScript, newDetails);
20572067
20582068 auto scriptCache = DependencyManager::get<ScriptCache>();
20592069 // note: see EntityTreeRenderer.cpp for shared pointer lifecycle management
@@ -2075,7 +2085,7 @@ void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString · 15 unmodified lines
20752085 #ifdef DEBUG_ENTITY_STATES
20762086 qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << entityID.toString();
20772087 #endif
2078− if (!isStopping() && hasEntityScriptDetails(entityID)) {
2088+ if (!isStopping() && hasEntityScriptDetails(entityID, entityScript)) {
20792089 _contentAvailableQueue[entityID] = { entityID, url, contents, isURL, success, status };
20802090 } else {
20812091 #ifdef DEBUG_ENTITY_STATES
@@ -2116,7 +2126,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 34 unmodified lines
21162126 QString entityScript;
21172127 {
21182128 QWriteLocker locker { &_entityScriptsLock };
2119− entityScript = _entityScripts[entityID].scriptText;
2129+ entityScript = _entityScripts[entityID][scriptOrURL].scriptText;
21202130 }
21212131
21222132 EntityScriptDetails newDetails;
@@ -2127,7 +2137,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 4 unmodified lines
21272137 auto setError = [&](const QString &errorInfo, const EntityScriptStatus& status) {
21282138 newDetails.errorInfo = errorInfo;
21292139 newDetails.status = status;
2130− setEntityScriptDetails(entityID, newDetails);
2140+ setEntityScriptDetails(entityID, scriptOrURL, newDetails);
21312141 };
21322142
21332143 // NETWORK / FILESYSTEM ERRORS
@@ -2417,14 +2427,14 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 283 unmodified lines
24172427 newDetails.scriptObject = entityScriptObject;
24182428 newDetails.lastModified = lastModified;
24192429 newDetails.definingSandboxURL = sandboxURL;
2420− setEntityScriptDetails(entityID, newDetails);
2430+ setEntityScriptDetails(entityID, scriptOrURL, newDetails);
24212431
24222432 if (isURL) {
24232433 setParentURL("");
24242434 }
24252435
24262436 // if we got this far, then call the preload method
2427− callEntityScriptMethod(entityID, "preload");
2437+ callEntityScriptMethodForScript(entityID, scriptOrURL, "preload");
24282438
24292439 emit entityScriptPreloadFinished(entityID);
24302440 }
@@ -2438,7 +2448,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 7 unmodified lines
24382448 * @returns {Signal}
24392449 */
24402450 // The JSDoc is for the callEntityScriptMethod() call in this method.
2441−void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) {
2451+void ScriptManager::unloadEntityScript(const EntityItemID& entityID, const QString& scriptURL, bool shouldRemoveFromMap) {
24422452 if (QThread::currentThread() != thread()) {
24432453 #ifdef THREAD_DEBUGGING
24442454 qCDebug(scriptengine) << "*** WARNING *** ScriptManager::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
@@ -2447,7 +2457,7 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 2 unmodified lines
24472457
24482458 // Lambda is necessary there to keep shared_ptr counter above zero
24492459 QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
2450− manager->unloadEntityScript(entityID, shouldRemoveFromMap);
2460+ manager->unloadEntityScript(entityID, scriptURL, shouldRemoveFromMap);
24512461 });
24522462 return;
24532463 }
@@ -2457,11 +2467,11 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 3 unmodified lines
24572467 #endif
24582468
24592469 EntityScriptDetails oldDetails;
2460− if (getEntityScriptDetails(entityID, oldDetails)) {
2461− auto scriptText = oldDetails.scriptText;
2470+ if (getEntityScriptDetails(entityID, scriptURL, oldDetails)) {
2471+ const auto& scriptText = oldDetails.scriptText;
24622472
2463− if (isEntityScriptRunning(entityID)) {
2464− callEntityScriptMethod(entityID, "unload");
2473+ if (isEntityScriptRunning(entityID, scriptURL)) {
2474+ callEntityScriptMethodForScript(entityID, scriptURL, "unload");
24652475 }
24662476 #ifdef DEBUG_ENTITY_STATES
24672477 else {
@@ -2472,6 +2482,71 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 4 unmodified lines
24722482 // this was a deleted entity, we've been asked to remove it from the map
24732483 {
24742484 QWriteLocker locker { &_entityScriptsLock };
2485+ auto& entityScripts = _entityScripts[entityID];
2486+ entityScripts.remove(scriptText);
2487+ if (entityScripts.size() == 0) {
2488+ _entityScripts.remove(entityID);
2489+ }
2490+ }
2491+ emit entityScriptDetailsUpdated();
2492+ } else if (oldDetails.status != EntityScriptStatus::UNLOADED) {
2493+ EntityScriptDetails newDetails;
2494+ newDetails.status = EntityScriptStatus::UNLOADED;
2495+ newDetails.lastModified = QDateTime::currentMSecsSinceEpoch();
2496+ // keep scriptText populated for the current need to "debouce" duplicate calls to unloadEntityScript
2497+ newDetails.scriptText = scriptText;
2498+ setEntityScriptDetails(entityID, scriptURL, newDetails);
2499+ }
2500+
2501+ stopAllTimersForEntityScript(entityID);
2502+ }
2503+}
2504+
2505+void ScriptManager::unloadAllEntityScriptsForEntity(const EntityItemID& entityID, bool shouldRemoveFromMap) {
2506+ if (QThread::currentThread() != thread()) {
2507+#ifdef THREAD_DEBUGGING
2508+ qCDebug(scriptengine) << "*** WARNING *** ScriptManager::unloadAllEntityScriptsForEntity() called on wrong thread ["
2509+ << QThread::currentThread() << "], invoking on correct thread [" << thread()
2510+ << "] "
2511+ "entityID:"
2512+ << entityID;
2513+#endif
2514+
2515+ // Lambda is necessary there to keep shared_ptr counter above zero
2516+ QMetaObject::invokeMethod(this, [=, manager = shared_from_this()] {
2517+ manager->unloadAllEntityScriptsForEntity(entityID, shouldRemoveFromMap);
2518+ });
2519+ return;
2520+ }
2521+#ifdef THREAD_DEBUGGING
2522+ qCDebug(scriptengine) << "ScriptManager::unloadAllEntityScriptsForEntity() called on correct thread [" << thread()
2523+ << "] "
2524+ "entityID:"
2525+ << entityID;
2526+#endif
2527+
2528+ std::vector<EntityScriptDetails> scriptDetails;
2529+ {
2530+ QWriteLocker locker { &_entityScriptsLock };
2531+ for (const auto& detail : _entityScripts[entityID]) {
2532+ scriptDetails.push_back(detail);
2533+ }
2534+ }
2535+ for (const auto& oldDetails : scriptDetails) {
2536+ const auto& scriptText = oldDetails.scriptText;
2537+
2538+ if (isEntityScriptRunning(entityID, scriptText)) {
2539+ callEntityScriptMethodForScript(entityID, scriptText, "unload");
2540+ }
2541+#ifdef DEBUG_ENTITY_STATES
2542+ else {
2543+ qCDebug(scriptengine) << "unload called while !running" << entityID << oldDetails.status;
2544+ }
2545+#endif
2546+ if (shouldRemoveFromMap) {
2547+ // this was a deleted entity, we've been asked to remove it from the map
2548+ {
2549+ QWriteLocker locker{ &_entityScriptsLock };
24752550 _entityScripts.remove(entityID);
24762551 }
24772552 emit entityScriptDetailsUpdated();
@@ -2481,7 +2556,7 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 3 unmodified lines
24812556 newDetails.lastModified = QDateTime::currentMSecsSinceEpoch();
24822557 // keep scriptText populated for the current need to "debouce" duplicate calls to unloadEntityScript
24832558 newDetails.scriptText = scriptText;
2484− setEntityScriptDetails(entityID, newDetails);
2559+ setEntityScriptDetails(entityID, scriptText, newDetails);
24852560 }
24862561
24872562 stopAllTimersForEntityScript(entityID);
@@ -2508,13 +2583,17 @@ void ScriptManager::unloadAllEntityScripts(bool blockingCall) { · 20 unmodified lines
25082583 qCDebug(scriptengine) << "ScriptManager::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
25092584 #endif
25102585
2511− QList<EntityItemID> keys;
2586+ std::vector<std::pair<EntityItemID, EntityScriptDetails>> scripts;
25122587 {
25132588 QReadLocker locker{ &_entityScriptsLock };
2514− keys = _entityScripts.keys();
2589+ for (const auto& entityID : _entityScripts.keys()) {
2590+ for (const auto& scriptDetails : _entityScripts[entityID]) {
2591+ scripts.push_back(std::make_pair(entityID, scriptDetails));
2592+ }
2593+ }
25152594 }
2516− foreach(const EntityItemID& entityID, keys) {
2517− unloadEntityScript(entityID);
2595+ foreach (const auto& idAndScript, scripts) {
2596+ unloadEntityScript(idAndScript.first, idAndScript.second.scriptText, true);
25182597 }
25192598 {
25202599 QWriteLocker locker{ &_entityScriptsLock };
@@ -2531,8 +2610,8 @@ void ScriptManager::unloadAllEntityScripts(bool blockingCall) { · 10 unmodified lines
25312610 #endif // DEBUG_ENGINE_STATE
25322611 }
25332612
2534−void ScriptManager::refreshFileScript(const EntityItemID& entityID) {
2535− if (!HIFI_AUTOREFRESH_FILE_SCRIPTS || !hasEntityScriptDetails(entityID)) {
2613+void ScriptManager::refreshFileScript(const EntityItemID& entityID, const QString& scriptURL) {
2614+ if (!HIFI_AUTOREFRESH_FILE_SCRIPTS || !hasEntityScriptDetails(entityID, scriptURL)) {
25362615 return;
25372616 }
25382617
@@ -2545,7 +2624,7 @@ void ScriptManager::refreshFileScript(const EntityItemID& entityID) { · 6 unmodified lines
25452624 EntityScriptDetails details;
25462625 {
25472626 QWriteLocker locker { &_entityScriptsLock };
2548− details = _entityScripts[entityID];
2627+ details = _entityScripts[entityID][scriptURL];
25492628 }
25502629 // Check to see if a file based script needs to be reloaded (easier debugging)
25512630 if (details.lastModified > 0) {
@@ -2605,15 +2684,94 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 53 unmodified lines
26052684 "entityID:" << entityID << "methodName:" << methodName;
26062685 #endif
26072686
2608− if (HIFI_AUTOREFRESH_FILE_SCRIPTS && methodName != "unload") {
2609− refreshFileScript(entityID);
2687+ std::vector<EntityScriptDetails> scriptDetails;
2688+ {
2689+ QWriteLocker locker { &_entityScriptsLock };
2690+ for (const auto& detail : _entityScripts[entityID]) {
2691+ scriptDetails.push_back(detail);
2692+ }
26102693 }
2611− if (isEntityScriptRunning(entityID)) {
2612− EntityScriptDetails details;
2613− {
2614− QWriteLocker locker { &_entityScriptsLock };
2615− details = _entityScripts[entityID];
2694+ for (const auto& details : scriptDetails) {
2695+ if (HIFI_AUTOREFRESH_FILE_SCRIPTS && methodName != "unload") {
2696+ refreshFileScript(entityID, details.scriptText);
26162697 }
2698+ if (isEntityScriptRunning(entityID, details.scriptText)) {
2699+ ScriptValue entityScript = details.scriptObject; // previously loaded
2700+
2701+ // If this is a remote call, we need to check to see if the function is remotely callable
2702+ // we do this by checking for the existance of the 'remotelyCallable' property on the
2703+ // entityScript. And we confirm that the method name is included. If this fails, the
2704+ // function will not be called.
2705+ bool callAllowed = false;
2706+ if (remoteCallerID == QUuid()) {
2707+ callAllowed = true;
2708+ } else {
2709+ if (entityScript.property("remotelyCallable").isArray()) {
2710+ auto callables = entityScript.property("remotelyCallable");
2711+ auto callableCount = callables.property("length").toInteger();
2712+ for (int i = 0; i < callableCount; i++) {
2713+ auto callable = callables.property(i).toString();
2714+ if (callable == methodName) {
2715+ callAllowed = true;
2716+ break;
2717+ }
2718+ }
2719+ }
2720+ if (!callAllowed) {
2721+ qCDebug(scriptengine) << "Method [" << methodName << "] not remotely callable.";
2722+ }
2723+ }
2724+
2725+ if (callAllowed && entityScript.property(methodName).isFunction()) {
2726+ auto scriptEngine = engine().get();
2727+
2728+ ScriptValueList args;
2729+ args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2730+ args << scriptValueFromSequence(scriptEngine, params);
2731+
2732+ ScriptValue oldData = scriptEngine->globalObject().property("Script").property("remoteCallerID");
2733+ scriptEngine->globalObject()
2734+ .property("Script")
2735+ .setProperty("remoteCallerID",
2736+ remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global.
2737+ callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript,
2738+ args);
2739+ scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", oldData);
2740+ }
2741+ }
2742+ }
2743+}
2744+
2745+void ScriptManager::callEntityScriptMethodForScript(const EntityItemID& entityID,
2746+ const QString& scriptURL,
2747+ const QString& methodName,
2748+ const QStringList& params,
2749+ const QUuid& remoteCallerID) {
2750+ if (QThread::currentThread() != thread()) {
2751+#ifdef THREAD_DEBUGGING
2752+ qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethodForScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
2753+ "entityID:" << entityID << "script:" << scriptURL << "methodName:" << methodName;
2754+#endif
2755+ // Lambda is necessary there to keep shared_ptr counter above zero
2756+ QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
2757+ manager->callEntityScriptMethodForScript(entityID, scriptURL, methodName, params, remoteCallerID);
2758+ });
2759+ return;
2760+ }
2761+#ifdef THREAD_DEBUGGING
2762+ qCDebug(scriptengine) << "ScriptManager::callEntityScriptMethodForScript() called on correct thread [" << thread() << "] "
2763+ "entityID:" << entityID << "methodName:" << methodName;
2764+#endif
2765+
2766+ EntityScriptDetails details;
2767+ {
2768+ QWriteLocker locker { &_entityScriptsLock };
2769+ details = _entityScripts[entityID][scriptURL];
2770+ }
2771+ if (HIFI_AUTOREFRESH_FILE_SCRIPTS && methodName != "unload") {
2772+ refreshFileScript(entityID, scriptURL);
2773+ }
2774+ if (isEntityScriptRunning(entityID, scriptURL)) {
26172775 ScriptValue entityScript = details.scriptObject; // previously loaded
26182776
26192777 // If this is a remote call, we need to check to see if the function is remotely callable
@@ -2648,13 +2806,60 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 28 unmodified lines
26482806 args << scriptValueFromSequence(scriptEngine, params);
26492807
26502808 ScriptValue oldData = scriptEngine->globalObject().property("Script").property("remoteCallerID");
2651− scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global.
2652− callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
2809+ scriptEngine->globalObject()
2810+ .property("Script")
2811+ .setProperty("remoteCallerID",
2812+ remoteCallerID.toString()); // Make the remoteCallerID available to javascript as a global.
2813+ callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript,
2814+ args);
26532815 scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", oldData);
26542816 }
26552817 }
26562818 }
26572819
2820+void ScriptManager::callEntityScriptMethodForScript(const EntityItemID& entityID,
2821+ const QString& scriptURL,
2822+ const QString& methodName,
2823+ const QString& param) {
2824+ if (QThread::currentThread() != thread()) {
2825+#ifdef THREAD_DEBUGGING
2826+ qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethodForScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
2827+ "entityID:" << entityID << "script:" << scriptURL << "methodName:" << methodName;
2828+#endif
2829+ // Lambda is necessary there to keep shared_ptr counter above zero
2830+ QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
2831+ manager->callEntityScriptMethodForScript(entityID, scriptURL, methodName, param);
2832+ });
2833+ return;
2834+ }
2835+#ifdef THREAD_DEBUGGING
2836+ qCDebug(scriptengine) << "ScriptManager::callEntityScriptMethodForScript() called on correct thread [" << thread() << "] "
2837+ "entityID:" << entityID << "methodName:" << methodName;
2838+#endif
2839+
2840+ EntityScriptDetails details;
2841+ {
2842+ QWriteLocker locker { &_entityScriptsLock };
2843+ details = _entityScripts[entityID][scriptURL];
2844+ }
2845+ if (HIFI_AUTOREFRESH_FILE_SCRIPTS && methodName != "unload") {
2846+ refreshFileScript(entityID, scriptURL);
2847+ }
2848+ if (isEntityScriptRunning(entityID, scriptURL)) {
2849+ ScriptValue entityScript = details.scriptObject; // previously loaded
2850+
2851+ if (entityScript.property(methodName).isFunction()) {
2852+ auto scriptEngine = engine().get();
2853+
2854+ ScriptValueList args;
2855+ args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2856+ args << scriptValueFromValue(scriptEngine, param);
2857+ callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript,
2858+ args);
2859+ }
2860+ }
2861+}
2862+
26582863 void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) {
26592864 if (QThread::currentThread() != thread()) {
26602865 #ifdef THREAD_DEBUGGING
@@ -2673,23 +2878,27 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 12 unmodified lines
26732878 "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent";
26742879 #endif
26752880
2676− if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
2677− refreshFileScript(entityID);
2881+ std::vector<EntityScriptDetails> scriptDetails;
2882+ {
2883+ QWriteLocker locker{ &_entityScriptsLock };
2884+ for (const auto& detail : _entityScripts[entityID]) {
2885+ scriptDetails.push_back(detail);
2886+ }
26782887 }
2679− if (isEntityScriptRunning(entityID)) {
2680− EntityScriptDetails details;
2681− {
2682− QWriteLocker locker { &_entityScriptsLock };
2683− details = _entityScripts[entityID];
2888+ for (const auto& details : scriptDetails) {
2889+ if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
2890+ refreshFileScript(entityID, details.scriptText);
26842891 }
2685− ScriptValue entityScript = details.scriptObject; // previously loaded
2686− if (entityScript.property(methodName).isFunction()) {
2687− auto scriptEngine = engine().get();
2892+ if (isEntityScriptRunning(entityID, details.scriptText)) {
2893+ ScriptValue entityScript = details.scriptObject; // previously loaded
2894+ if (entityScript.property(methodName).isFunction()) {
2895+ auto scriptEngine = engine().get();
26882896
2689− ScriptValueList args;
2690− args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2691− args << event.toScriptValue(scriptEngine);
2692− callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
2897+ ScriptValueList args;
2898+ args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2899+ args << event.toScriptValue(scriptEngine);
2900+ callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
2901+ }
26932902 }
26942903 }
26952904 }
@@ -2712,24 +2921,29 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 16 unmodified lines
27122921 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
27132922 #endif
27142923
2715− if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
2716− refreshFileScript(entityID);
2924+ std::vector<EntityScriptDetails> scriptDetails;
2925+ {
2926+ QWriteLocker locker{ &_entityScriptsLock };
2927+ for (const auto& detail : _entityScripts[entityID]) {
2928+ scriptDetails.push_back(detail);
2929+ }
27172930 }
2718− if (isEntityScriptRunning(entityID)) {
2719− EntityScriptDetails details;
2720− {
2721− QWriteLocker locker { &_entityScriptsLock };
2722− details = _entityScripts[entityID];
2931+ for (const auto& details : scriptDetails) {
2932+ if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
2933+ refreshFileScript(entityID, details.scriptText);
27232934 }
2724− ScriptValue entityScript = details.scriptObject; // previously loaded
2725− if (entityScript.property(methodName).isFunction()) {
2726− auto scriptEngine = engine().get();
2935+ if (isEntityScriptRunning(entityID, details.scriptText)) {
2936+ ScriptValue entityScript = details.scriptObject; // previously loaded
2937+ if (entityScript.property(methodName).isFunction()) {
2938+ auto scriptEngine = engine().get();
27272939
2728− ScriptValueList args;
2729− args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2730− args << EntityItemIDtoScriptValue(scriptEngine, otherID);
2731− args << collisionToScriptValue(scriptEngine, collision);
2732− callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
2940+ ScriptValueList args;
2941+ args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2942+ args << EntityItemIDtoScriptValue(scriptEngine, otherID);
2943+ args << collisionToScriptValue(scriptEngine, collision);
2944+ callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript,
2945+ args);
2946+ }
27332947 }
27342948 }
27352949 }