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
1929int ScriptManager::getNumRunningEntityScripts() const {1929int ScriptManager::getNumRunningEntityScripts() const {
1930 QReadLocker locker { &_entityScriptsLock };1930 QReadLocker locker { &_entityScriptsLock };
1931 int sum = 0;1931 int sum = 0;
1932 for (const auto& st : _entityScripts) {1932 for (const auto& entityScripts : _entityScripts) {
1933 if (st.status == EntityScriptStatus::RUNNING) {1933 for (const auto& script : entityScripts) {
1934 ++sum;1934 if (script.status == EntityScriptStatus::RUNNING) {
1935 ++sum;
1936 }
1935 }1937 }
1936 }1938 }
1937 return sum;1939 return sum;
1938}1940}
1939 1941
1940void ScriptManager::setEntityScriptDetails(const EntityItemID& entityID, const EntityScriptDetails& details) {1942void ScriptManager::setEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL, const EntityScriptDetails& details) {
1941 {1943 {
1942 QWriteLocker locker { &_entityScriptsLock };1944 QWriteLocker locker { &_entityScriptsLock };
1943 _entityScripts[entityID] = details;1945 _entityScripts[entityID][scriptURL] = details;
1944 }1946 }
1945 emit entityScriptDetailsUpdated();1947 emit entityScriptDetailsUpdated();
1946}1948}
1947 1949
1948void ScriptManager::updateEntityScriptStatus(const EntityItemID& entityID, const EntityScriptStatus &status, const QString& errorInfo) {1950void ScriptManager::updateEntityScriptStatus(const EntityItemID& entityID, const QString& scriptURL, const EntityScriptStatus &status, const QString& errorInfo) {
1949 {1951 {
1950 QWriteLocker locker { &_entityScriptsLock };1952 QWriteLocker locker { &_entityScriptsLock };
1951 EntityScriptDetails& details = _entityScripts[entityID];1953 EntityScriptDetails& details = _entityScripts[entityID][scriptURL];
1952 details.status = status;1954 details.status = status;
1953 details.errorInfo = errorInfo;1955 details.errorInfo = errorInfo;
1954 }1956 }
1955 emit entityScriptDetailsUpdated();1957 emit entityScriptDetailsUpdated();
1956}1958}
1957 1959
1958QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) {1960QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL) {
1959 static const QVariant NULL_VARIANT = QVariant::fromValue(nullptr);1961 static const QVariant NULL_VARIANT = QVariant::fromValue(nullptr);
1960 QVariantMap map;1962 QVariantMap map;
1961 if (entityID.isNull()) {1963 if (entityID.isNull()) {
@@ -1967,11 +1969,11 @@ QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) { · 5 unmodified lines
1967 qCDebug(scriptengine) << "cloneEntityScriptDetails" << entityID << QThread::currentThread();1969 qCDebug(scriptengine) << "cloneEntityScriptDetails" << entityID << QThread::currentThread();
1968#endif1970#endif
1969 EntityScriptDetails scriptDetails;1971 EntityScriptDetails scriptDetails;
1970 if (getEntityScriptDetails(entityID, scriptDetails)) {1972 if (getEntityScriptDetails(entityID, scriptURL, scriptDetails)) {
1971#ifdef DEBUG_ENTITY_STATES1973#ifdef DEBUG_ENTITY_STATES
1972 qCDebug(scriptengine) << "gotEntityScriptDetails" << scriptDetails.status << QThread::currentThread();1974 qCDebug(scriptengine) << "gotEntityScriptDetails" << scriptDetails.status << QThread::currentThread();
1973#endif1975#endif
1974 map["isRunning"] = isEntityScriptRunning(entityID);1976 map["isRunning"] = isEntityScriptRunning(entityID, scriptURL);
1975 map["status"] = EntityScriptStatus_::valueToKey(scriptDetails.status).toLower();1977 map["status"] = EntityScriptStatus_::valueToKey(scriptDetails.status).toLower();
1976 map["errorInfo"] = scriptDetails.errorInfo;1978 map["errorInfo"] = scriptDetails.errorInfo;
1977 map["entityID"] = entityID.toString();1979 map["entityID"] = entityID.toString();
@@ -1997,23 +1999,31 @@ QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) { · 19 unmodified lines
1997 return map;1999 return map;
1998}2000}
1999 2001
2000QFuture<QVariant> ScriptManager::getLocalEntityScriptDetails(const EntityItemID& entityID) {2002QFuture<QVariant> ScriptManager::getLocalEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL) {
2001 return QtConcurrent::run(this, &ScriptManager::cloneEntityScriptDetails, entityID);2003 return QtConcurrent::run(this, &ScriptManager::cloneEntityScriptDetails, entityID, scriptURL);
2002}2004}
2003 2005
2004bool ScriptManager::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const {2006bool ScriptManager::getEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL, EntityScriptDetails &details) const {
2005 QReadLocker locker { &_entityScriptsLock };2007 QReadLocker locker { &_entityScriptsLock };
2006 auto it = _entityScripts.constFind(entityID);2008 auto it = _entityScripts.constFind(entityID);
2007 if (it == _entityScripts.constEnd()) {2009 if (it == _entityScripts.constEnd()) {
2008 return false;2010 return false;
2009 }2011 }
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();
2011 return true;2017 return true;
2012}2018}
2013 2019
2014bool ScriptManager::hasEntityScriptDetails(const EntityItemID& entityID) const {2020bool ScriptManager::hasEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL) const {
2015 QReadLocker locker { &_entityScriptsLock };2021 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);
2017}2027}
2018 2028
2019void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {2029void 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
2034 return;2044 return;
2035 }2045 }
2036 2046
2037 if (!hasEntityScriptDetails(entityID)) {2047 if (!hasEntityScriptDetails(entityID, entityScript)) {
2038 // make sure EntityScriptDetails has an entry for this UUID right away2048 // make sure EntityScriptDetails has an entry for this UUID right away
2039 // (which allows bailing from the loading/provisioning process early if the Entity gets deleted mid-flight)2049 // (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...");
2041 }2051 }
2042 2052
2043#ifdef DEBUG_ENTITY_STATES2053#ifdef DEBUG_ENTITY_STATES
2044 {2054 {
2045 EntityScriptDetails details;2055 EntityScriptDetails details;
2046 bool hasEntityScript = getEntityScriptDetails(entityID, details);2056 bool hasEntityScript = getEntityScriptDetails(entityID, entityScript, details);
2047 qCDebug(scriptengine) << "loadEntityScript.LOADING: " << entityID.toString()2057 qCDebug(scriptengine) << "loadEntityScript.LOADING: " << entityID.toString()
2048 << "(previous: " << (hasEntityScript ? details.status : EntityScriptStatus::PENDING) << ")";2058 << "(previous: " << (hasEntityScript ? details.status : EntityScriptStatus::PENDING) << ")";
2049 }2059 }
@@ -2053,7 +2063,7 @@ void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString · 3 unmodified lines
2053 newDetails.scriptText = entityScript;2063 newDetails.scriptText = entityScript;
2054 newDetails.status = EntityScriptStatus::LOADING;2064 newDetails.status = EntityScriptStatus::LOADING;
2055 newDetails.definingSandboxURL = currentSandboxURL;2065 newDetails.definingSandboxURL = currentSandboxURL;
2056 setEntityScriptDetails(entityID, newDetails);2066 setEntityScriptDetails(entityID, entityScript, newDetails);
2057 2067
2058 auto scriptCache = DependencyManager::get<ScriptCache>();2068 auto scriptCache = DependencyManager::get<ScriptCache>();
2059 // note: see EntityTreeRenderer.cpp for shared pointer lifecycle management2069 // note: see EntityTreeRenderer.cpp for shared pointer lifecycle management
@@ -2075,7 +2085,7 @@ void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString · 15 unmodified lines
2075#ifdef DEBUG_ENTITY_STATES2085#ifdef DEBUG_ENTITY_STATES
2076 qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << entityID.toString();2086 qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << entityID.toString();
2077#endif2087#endif
2078 if (!isStopping() && hasEntityScriptDetails(entityID)) {2088 if (!isStopping() && hasEntityScriptDetails(entityID, entityScript)) {
2079 _contentAvailableQueue[entityID] = { entityID, url, contents, isURL, success, status };2089 _contentAvailableQueue[entityID] = { entityID, url, contents, isURL, success, status };
2080 } else {2090 } else {
2081#ifdef DEBUG_ENTITY_STATES2091#ifdef DEBUG_ENTITY_STATES
@@ -2116,7 +2126,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 34 unmodified lines
2116 QString entityScript;2126 QString entityScript;
2117 {2127 {
2118 QWriteLocker locker { &_entityScriptsLock };2128 QWriteLocker locker { &_entityScriptsLock };
2119 entityScript = _entityScripts[entityID].scriptText;2129 entityScript = _entityScripts[entityID][scriptOrURL].scriptText;
2120 }2130 }
2121 2131
2122 EntityScriptDetails newDetails;2132 EntityScriptDetails newDetails;
@@ -2127,7 +2137,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 4 unmodified lines
2127 auto setError = [&](const QString &errorInfo, const EntityScriptStatus& status) {2137 auto setError = [&](const QString &errorInfo, const EntityScriptStatus& status) {
2128 newDetails.errorInfo = errorInfo;2138 newDetails.errorInfo = errorInfo;
2129 newDetails.status = status;2139 newDetails.status = status;
2130 setEntityScriptDetails(entityID, newDetails);2140 setEntityScriptDetails(entityID, scriptOrURL, newDetails);
2131 };2141 };
2132 2142
2133 // NETWORK / FILESYSTEM ERRORS2143 // NETWORK / FILESYSTEM ERRORS
@@ -2417,14 +2427,14 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 283 unmodified lines
2417 newDetails.scriptObject = entityScriptObject;2427 newDetails.scriptObject = entityScriptObject;
2418 newDetails.lastModified = lastModified;2428 newDetails.lastModified = lastModified;
2419 newDetails.definingSandboxURL = sandboxURL;2429 newDetails.definingSandboxURL = sandboxURL;
2420 setEntityScriptDetails(entityID, newDetails);2430 setEntityScriptDetails(entityID, scriptOrURL, newDetails);
2421 2431
2422 if (isURL) {2432 if (isURL) {
2423 setParentURL("");2433 setParentURL("");
2424 }2434 }
2425 2435
2426 // if we got this far, then call the preload method2436 // if we got this far, then call the preload method
2427 callEntityScriptMethod(entityID, "preload");2437 callEntityScriptMethodForScript(entityID, scriptOrURL, "preload");
2428 2438
2429 emit entityScriptPreloadFinished(entityID);2439 emit entityScriptPreloadFinished(entityID);
2430}2440}
@@ -2438,7 +2448,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 7 unmodified lines
2438 * @returns {Signal}2448 * @returns {Signal}
2439 */2449 */
2440// The JSDoc is for the callEntityScriptMethod() call in this method.2450// The JSDoc is for the callEntityScriptMethod() call in this method.
2441void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) {2451void ScriptManager::unloadEntityScript(const EntityItemID& entityID, const QString& scriptURL, bool shouldRemoveFromMap) {
2442 if (QThread::currentThread() != thread()) {2452 if (QThread::currentThread() != thread()) {
2443#ifdef THREAD_DEBUGGING2453#ifdef THREAD_DEBUGGING
2444 qCDebug(scriptengine) << "*** WARNING *** ScriptManager::unloadEntityScript() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "2454 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
2447 2457
2448 // Lambda is necessary there to keep shared_ptr counter above zero2458 // Lambda is necessary there to keep shared_ptr counter above zero
2449 QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{2459 QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
2450 manager->unloadEntityScript(entityID, shouldRemoveFromMap);2460 manager->unloadEntityScript(entityID, scriptURL, shouldRemoveFromMap);
2451 });2461 });
2452 return;2462 return;
2453 }2463 }
@@ -2457,11 +2467,11 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 3 unmodified lines
2457#endif2467#endif
2458 2468
2459 EntityScriptDetails oldDetails;2469 EntityScriptDetails oldDetails;
2460 if (getEntityScriptDetails(entityID, oldDetails)) {2470 if (getEntityScriptDetails(entityID, scriptURL, oldDetails)) {
2461 auto scriptText = oldDetails.scriptText;2471 const auto& scriptText = oldDetails.scriptText;
2462 2472
2463 if (isEntityScriptRunning(entityID)) {2473 if (isEntityScriptRunning(entityID, scriptURL)) {
2464 callEntityScriptMethod(entityID, "unload");2474 callEntityScriptMethodForScript(entityID, scriptURL, "unload");
2465 }2475 }
2466#ifdef DEBUG_ENTITY_STATES2476#ifdef DEBUG_ENTITY_STATES
2467 else {2477 else {
@@ -2472,6 +2482,71 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 4 unmodified lines
2472 // this was a deleted entity, we've been asked to remove it from the map2482 // this was a deleted entity, we've been asked to remove it from the map
2473 {2483 {
2474 QWriteLocker locker { &_entityScriptsLock };2484 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
2505void 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 };
2475 _entityScripts.remove(entityID);2550 _entityScripts.remove(entityID);
2476 }2551 }
2477 emit entityScriptDetailsUpdated();2552 emit entityScriptDetailsUpdated();
@@ -2481,7 +2556,7 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 3 unmodified lines
2481 newDetails.lastModified = QDateTime::currentMSecsSinceEpoch();2556 newDetails.lastModified = QDateTime::currentMSecsSinceEpoch();
2482 // keep scriptText populated for the current need to "debouce" duplicate calls to unloadEntityScript2557 // keep scriptText populated for the current need to "debouce" duplicate calls to unloadEntityScript
2483 newDetails.scriptText = scriptText;2558 newDetails.scriptText = scriptText;
2484 setEntityScriptDetails(entityID, newDetails);2559 setEntityScriptDetails(entityID, scriptText, newDetails);
2485 }2560 }
2486 2561
2487 stopAllTimersForEntityScript(entityID);2562 stopAllTimersForEntityScript(entityID);
@@ -2508,13 +2583,17 @@ void ScriptManager::unloadAllEntityScripts(bool blockingCall) { · 20 unmodified lines
2508 qCDebug(scriptengine) << "ScriptManager::unloadAllEntityScripts() called on correct thread [" << thread() << "]";2583 qCDebug(scriptengine) << "ScriptManager::unloadAllEntityScripts() called on correct thread [" << thread() << "]";
2509#endif2584#endif
2510 2585
2511 QList<EntityItemID> keys;2586 std::vector<std::pair<EntityItemID, EntityScriptDetails>> scripts;
2512 {2587 {
2513 QReadLocker locker{ &_entityScriptsLock };2588 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 }
2515 }2594 }
2516 foreach(const EntityItemID& entityID, keys) {2595 foreach (const auto& idAndScript, scripts) {
2517 unloadEntityScript(entityID);2596 unloadEntityScript(idAndScript.first, idAndScript.second.scriptText, true);
2518 }2597 }
2519 {2598 {
2520 QWriteLocker locker{ &_entityScriptsLock };2599 QWriteLocker locker{ &_entityScriptsLock };
@@ -2531,8 +2610,8 @@ void ScriptManager::unloadAllEntityScripts(bool blockingCall) { · 10 unmodified lines
2531#endif // DEBUG_ENGINE_STATE2610#endif // DEBUG_ENGINE_STATE
2532}2611}
2533 2612
2534void ScriptManager::refreshFileScript(const EntityItemID& entityID) {2613void ScriptManager::refreshFileScript(const EntityItemID& entityID, const QString& scriptURL) {
2535 if (!HIFI_AUTOREFRESH_FILE_SCRIPTS || !hasEntityScriptDetails(entityID)) {2614 if (!HIFI_AUTOREFRESH_FILE_SCRIPTS || !hasEntityScriptDetails(entityID, scriptURL)) {
2536 return;2615 return;
2537 }2616 }
2538 2617
@@ -2545,7 +2624,7 @@ void ScriptManager::refreshFileScript(const EntityItemID& entityID) { · 6 unmodified lines
2545 EntityScriptDetails details;2624 EntityScriptDetails details;
2546 {2625 {
2547 QWriteLocker locker { &_entityScriptsLock };2626 QWriteLocker locker { &_entityScriptsLock };
2548 details = _entityScripts[entityID];2627 details = _entityScripts[entityID][scriptURL];
2549 }2628 }
2550 // Check to see if a file based script needs to be reloaded (easier debugging)2629 // Check to see if a file based script needs to be reloaded (easier debugging)
2551 if (details.lastModified > 0) {2630 if (details.lastModified > 0) {
@@ -2605,15 +2684,94 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 53 unmodified lines
2605 "entityID:" << entityID << "methodName:" << methodName;2684 "entityID:" << entityID << "methodName:" << methodName;
2606#endif2685#endif
2607 2686
2608 if (HIFI_AUTOREFRESH_FILE_SCRIPTS && methodName != "unload") {2687 std::vector<EntityScriptDetails> scriptDetails;
2609 refreshFileScript(entityID);2688 {
2689 QWriteLocker locker { &_entityScriptsLock };
2690 for (const auto& detail : _entityScripts[entityID]) {
2691 scriptDetails.push_back(detail);
2692 }
2610 }2693 }
2611 if (isEntityScriptRunning(entityID)) {2694 for (const auto& details : scriptDetails) {
2612 EntityScriptDetails details;2695 if (HIFI_AUTOREFRESH_FILE_SCRIPTS && methodName != "unload") {
2613 {2696 refreshFileScript(entityID, details.scriptText);
2614 QWriteLocker locker { &_entityScriptsLock };
2615 details = _entityScripts[entityID];
2616 }2697 }
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
2745void 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)) {
2617 ScriptValue entityScript = details.scriptObject; // previously loaded2775 ScriptValue entityScript = details.scriptObject; // previously loaded
2618 2776
2619 // If this is a remote call, we need to check to see if the function is remotely callable2777 // 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
2648 args << scriptValueFromSequence(scriptEngine, params);2806 args << scriptValueFromSequence(scriptEngine, params);
2649 2807
2650 ScriptValue oldData = scriptEngine->globalObject().property("Script").property("remoteCallerID");2808 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.2809 scriptEngine->globalObject()
2652 callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);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);
2653 scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", oldData);2815 scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", oldData);
2654 }2816 }
2655 }2817 }
2656}2818}
2657 2819
2820void 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
2658void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) {2863void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) {
2659 if (QThread::currentThread() != thread()) {2864 if (QThread::currentThread() != thread()) {
2660#ifdef THREAD_DEBUGGING2865#ifdef THREAD_DEBUGGING
@@ -2673,23 +2878,27 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 12 unmodified lines
2673 "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent";2878 "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent";
2674#endif2879#endif
2675 2880
2676 if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {2881 std::vector<EntityScriptDetails> scriptDetails;
2677 refreshFileScript(entityID);2882 {
2883 QWriteLocker locker{ &_entityScriptsLock };
2884 for (const auto& detail : _entityScripts[entityID]) {
2885 scriptDetails.push_back(detail);
2886 }
2678 }2887 }
2679 if (isEntityScriptRunning(entityID)) {2888 for (const auto& details : scriptDetails) {
2680 EntityScriptDetails details;2889 if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
2681 {2890 refreshFileScript(entityID, details.scriptText);
2682 QWriteLocker locker { &_entityScriptsLock };
2683 details = _entityScripts[entityID];
2684 }2891 }
2685 ScriptValue entityScript = details.scriptObject; // previously loaded2892 if (isEntityScriptRunning(entityID, details.scriptText)) {
2686 if (entityScript.property(methodName).isFunction()) {2893 ScriptValue entityScript = details.scriptObject; // previously loaded
2687 auto scriptEngine = engine().get();2894 if (entityScript.property(methodName).isFunction()) {
2895 auto scriptEngine = engine().get();
2688 2896
2689 ScriptValueList args;2897 ScriptValueList args;
2690 args << EntityItemIDtoScriptValue(scriptEngine, entityID);2898 args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2691 args << event.toScriptValue(scriptEngine);2899 args << event.toScriptValue(scriptEngine);
2692 callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);2900 callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
2901 }
2693 }2902 }
2694 }2903 }
2695}2904}
@@ -2712,24 +2921,29 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 16 unmodified lines
2712 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";2921 "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
2713#endif2922#endif
2714 2923
2715 if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {2924 std::vector<EntityScriptDetails> scriptDetails;
2716 refreshFileScript(entityID);2925 {
2926 QWriteLocker locker{ &_entityScriptsLock };
2927 for (const auto& detail : _entityScripts[entityID]) {
2928 scriptDetails.push_back(detail);
2929 }
2717 }2930 }
2718 if (isEntityScriptRunning(entityID)) {2931 for (const auto& details : scriptDetails) {
2719 EntityScriptDetails details;2932 if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
2720 {2933 refreshFileScript(entityID, details.scriptText);
2721 QWriteLocker locker { &_entityScriptsLock };
2722 details = _entityScripts[entityID];
2723 }2934 }
2724 ScriptValue entityScript = details.scriptObject; // previously loaded2935 if (isEntityScriptRunning(entityID, details.scriptText)) {
2725 if (entityScript.property(methodName).isFunction()) {2936 ScriptValue entityScript = details.scriptObject; // previously loaded
2726 auto scriptEngine = engine().get();2937 if (entityScript.property(methodName).isFunction()) {
2938 auto scriptEngine = engine().get();
2727 2939
2728 ScriptValueList args;2940 ScriptValueList args;
2729 args << EntityItemIDtoScriptValue(scriptEngine, entityID);2941 args << EntityItemIDtoScriptValue(scriptEngine, entityID);
2730 args << EntityItemIDtoScriptValue(scriptEngine, otherID);2942 args << EntityItemIDtoScriptValue(scriptEngine, otherID);
2731 args << collisionToScriptValue(scriptEngine, collision);2943 args << collisionToScriptValue(scriptEngine, collision);
2732 callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);2944 callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript,
2945 args);
2946 }
2733 }2947 }
2734 }2948 }
2735}2949}