Showing the changes to libraries/script-engine/src/ScriptManager.cpp (1 of 38 files). Show all
Unified · Split
| @@ -1929,33 +1929,35 @@ void ScriptManager::forwardHandlerCall(const EntityItemID& entityID, const QStri · 1928 unmodified lines | ||
| 1929 | 1929 | int ScriptManager::getNumRunningEntityScripts() const { |
| 1930 | 1930 | QReadLocker locker { &_entityScriptsLock }; |
| 1931 | 1931 | 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 | + } | |
| 1935 | 1937 | } |
| 1936 | 1938 | } |
| 1937 | 1939 | return sum; |
| 1938 | 1940 | } |
| 1939 | 1941 | |
| 1940 | −void ScriptManager::setEntityScriptDetails(const EntityItemID& entityID, const EntityScriptDetails& details) { | |
| 1942 | +void ScriptManager::setEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL, const EntityScriptDetails& details) { | |
| 1941 | 1943 | { |
| 1942 | 1944 | QWriteLocker locker { &_entityScriptsLock }; |
| 1943 | − _entityScripts[entityID] = details; | |
| 1945 | + _entityScripts[entityID][scriptURL] = details; | |
| 1944 | 1946 | } |
| 1945 | 1947 | emit entityScriptDetailsUpdated(); |
| 1946 | 1948 | } |
| 1947 | 1949 | |
| 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) { | |
| 1949 | 1951 | { |
| 1950 | 1952 | QWriteLocker locker { &_entityScriptsLock }; |
| 1951 | − EntityScriptDetails& details = _entityScripts[entityID]; | |
| 1953 | + EntityScriptDetails& details = _entityScripts[entityID][scriptURL]; | |
| 1952 | 1954 | details.status = status; |
| 1953 | 1955 | details.errorInfo = errorInfo; |
| 1954 | 1956 | } |
| 1955 | 1957 | emit entityScriptDetailsUpdated(); |
| 1956 | 1958 | } |
| 1957 | 1959 | |
| 1958 | −QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) { | |
| 1960 | +QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL) { | |
| 1959 | 1961 | static const QVariant NULL_VARIANT = QVariant::fromValue(nullptr); |
| 1960 | 1962 | QVariantMap map; |
| 1961 | 1963 | if (entityID.isNull()) { |
| @@ -1967,11 +1969,11 @@ QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) { · 5 unmodified lines | ||
| 1967 | 1969 | qCDebug(scriptengine) << "cloneEntityScriptDetails" << entityID << QThread::currentThread(); |
| 1968 | 1970 | #endif |
| 1969 | 1971 | EntityScriptDetails scriptDetails; |
| 1970 | − if (getEntityScriptDetails(entityID, scriptDetails)) { | |
| 1972 | + if (getEntityScriptDetails(entityID, scriptURL, scriptDetails)) { | |
| 1971 | 1973 | #ifdef DEBUG_ENTITY_STATES |
| 1972 | 1974 | qCDebug(scriptengine) << "gotEntityScriptDetails" << scriptDetails.status << QThread::currentThread(); |
| 1973 | 1975 | #endif |
| 1974 | − map["isRunning"] = isEntityScriptRunning(entityID); | |
| 1976 | + map["isRunning"] = isEntityScriptRunning(entityID, scriptURL); | |
| 1975 | 1977 | map["status"] = EntityScriptStatus_::valueToKey(scriptDetails.status).toLower(); |
| 1976 | 1978 | map["errorInfo"] = scriptDetails.errorInfo; |
| 1977 | 1979 | map["entityID"] = entityID.toString(); |
| @@ -1997,23 +1999,31 @@ QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) { · 19 unmodified lines | ||
| 1997 | 1999 | return map; |
| 1998 | 2000 | } |
| 1999 | 2001 | |
| 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); | |
| 2002 | 2004 | } |
| 2003 | 2005 | |
| 2004 | −bool ScriptManager::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const { | |
| 2006 | +bool ScriptManager::getEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL, EntityScriptDetails &details) const { | |
| 2005 | 2007 | QReadLocker locker { &_entityScriptsLock }; |
| 2006 | 2008 | auto it = _entityScripts.constFind(entityID); |
| 2007 | 2009 | if (it == _entityScripts.constEnd()) { |
| 2008 | 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 | 2017 | return true; |
| 2012 | 2018 | } |
| 2013 | 2019 | |
| 2014 | −bool ScriptManager::hasEntityScriptDetails(const EntityItemID& entityID) const { | |
| 2020 | +bool ScriptManager::hasEntityScriptDetails(const EntityItemID& entityID, const QString& scriptURL) const { | |
| 2015 | 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 | |
| 2019 | 2029 | 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 | ||
| 2034 | 2044 | return; |
| 2035 | 2045 | } |
| 2036 | 2046 | |
| 2037 | − if (!hasEntityScriptDetails(entityID)) { | |
| 2047 | + if (!hasEntityScriptDetails(entityID, entityScript)) { | |
| 2038 | 2048 | // make sure EntityScriptDetails has an entry for this UUID right away |
| 2039 | 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 | 2053 | #ifdef DEBUG_ENTITY_STATES |
| 2044 | 2054 | { |
| 2045 | 2055 | EntityScriptDetails details; |
| 2046 | − bool hasEntityScript = getEntityScriptDetails(entityID, details); | |
| 2056 | + bool hasEntityScript = getEntityScriptDetails(entityID, entityScript, details); | |
| 2047 | 2057 | qCDebug(scriptengine) << "loadEntityScript.LOADING: " << entityID.toString() |
| 2048 | 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 | 2063 | newDetails.scriptText = entityScript; |
| 2054 | 2064 | newDetails.status = EntityScriptStatus::LOADING; |
| 2055 | 2065 | newDetails.definingSandboxURL = currentSandboxURL; |
| 2056 | − setEntityScriptDetails(entityID, newDetails); | |
| 2066 | + setEntityScriptDetails(entityID, entityScript, newDetails); | |
| 2057 | 2067 | |
| 2058 | 2068 | auto scriptCache = DependencyManager::get<ScriptCache>(); |
| 2059 | 2069 | // 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 | 2085 | #ifdef DEBUG_ENTITY_STATES |
| 2076 | 2086 | qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << entityID.toString(); |
| 2077 | 2087 | #endif |
| 2078 | − if (!isStopping() && hasEntityScriptDetails(entityID)) { | |
| 2088 | + if (!isStopping() && hasEntityScriptDetails(entityID, entityScript)) { | |
| 2079 | 2089 | _contentAvailableQueue[entityID] = { entityID, url, contents, isURL, success, status }; |
| 2080 | 2090 | } else { |
| 2081 | 2091 | #ifdef DEBUG_ENTITY_STATES |
| @@ -2116,7 +2126,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 34 unmodified lines | ||
| 2116 | 2126 | QString entityScript; |
| 2117 | 2127 | { |
| 2118 | 2128 | QWriteLocker locker { &_entityScriptsLock }; |
| 2119 | − entityScript = _entityScripts[entityID].scriptText; | |
| 2129 | + entityScript = _entityScripts[entityID][scriptOrURL].scriptText; | |
| 2120 | 2130 | } |
| 2121 | 2131 | |
| 2122 | 2132 | EntityScriptDetails newDetails; |
| @@ -2127,7 +2137,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 4 unmodified lines | ||
| 2127 | 2137 | auto setError = [&](const QString &errorInfo, const EntityScriptStatus& status) { |
| 2128 | 2138 | newDetails.errorInfo = errorInfo; |
| 2129 | 2139 | newDetails.status = status; |
| 2130 | − setEntityScriptDetails(entityID, newDetails); | |
| 2140 | + setEntityScriptDetails(entityID, scriptOrURL, newDetails); | |
| 2131 | 2141 | }; |
| 2132 | 2142 | |
| 2133 | 2143 | // NETWORK / FILESYSTEM ERRORS |
| @@ -2417,14 +2427,14 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 283 unmodified lines | ||
| 2417 | 2427 | newDetails.scriptObject = entityScriptObject; |
| 2418 | 2428 | newDetails.lastModified = lastModified; |
| 2419 | 2429 | newDetails.definingSandboxURL = sandboxURL; |
| 2420 | − setEntityScriptDetails(entityID, newDetails); | |
| 2430 | + setEntityScriptDetails(entityID, scriptOrURL, newDetails); | |
| 2421 | 2431 | |
| 2422 | 2432 | if (isURL) { |
| 2423 | 2433 | setParentURL(""); |
| 2424 | 2434 | } |
| 2425 | 2435 | |
| 2426 | 2436 | // if we got this far, then call the preload method |
| 2427 | − callEntityScriptMethod(entityID, "preload"); | |
| 2437 | + callEntityScriptMethodForScript(entityID, scriptOrURL, "preload"); | |
| 2428 | 2438 | |
| 2429 | 2439 | emit entityScriptPreloadFinished(entityID); |
| 2430 | 2440 | } |
| @@ -2438,7 +2448,7 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c · 7 unmodified lines | ||
| 2438 | 2448 | * @returns {Signal} |
| 2439 | 2449 | */ |
| 2440 | 2450 | // 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) { | |
| 2442 | 2452 | if (QThread::currentThread() != thread()) { |
| 2443 | 2453 | #ifdef THREAD_DEBUGGING |
| 2444 | 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 | 2458 | // Lambda is necessary there to keep shared_ptr counter above zero |
| 2449 | 2459 | QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{ |
| 2450 | − manager->unloadEntityScript(entityID, shouldRemoveFromMap); | |
| 2460 | + manager->unloadEntityScript(entityID, scriptURL, shouldRemoveFromMap); | |
| 2451 | 2461 | }); |
| 2452 | 2462 | return; |
| 2453 | 2463 | } |
| @@ -2457,11 +2467,11 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 3 unmodified lines | ||
| 2457 | 2467 | #endif |
| 2458 | 2468 | |
| 2459 | 2469 | EntityScriptDetails oldDetails; |
| 2460 | − if (getEntityScriptDetails(entityID, oldDetails)) { | |
| 2461 | − auto scriptText = oldDetails.scriptText; | |
| 2470 | + if (getEntityScriptDetails(entityID, scriptURL, oldDetails)) { | |
| 2471 | + const auto& scriptText = oldDetails.scriptText; | |
| 2462 | 2472 | |
| 2463 | − if (isEntityScriptRunning(entityID)) { | |
| 2464 | − callEntityScriptMethod(entityID, "unload"); | |
| 2473 | + if (isEntityScriptRunning(entityID, scriptURL)) { | |
| 2474 | + callEntityScriptMethodForScript(entityID, scriptURL, "unload"); | |
| 2465 | 2475 | } |
| 2466 | 2476 | #ifdef DEBUG_ENTITY_STATES |
| 2467 | 2477 | else { |
| @@ -2472,6 +2482,71 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 4 unmodified lines | ||
| 2472 | 2482 | // this was a deleted entity, we've been asked to remove it from the map |
| 2473 | 2483 | { |
| 2474 | 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 | + | |
| 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 }; | |
| 2475 | 2550 | _entityScripts.remove(entityID); |
| 2476 | 2551 | } |
| 2477 | 2552 | emit entityScriptDetailsUpdated(); |
| @@ -2481,7 +2556,7 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should · 3 unmodified lines | ||
| 2481 | 2556 | newDetails.lastModified = QDateTime::currentMSecsSinceEpoch(); |
| 2482 | 2557 | // keep scriptText populated for the current need to "debouce" duplicate calls to unloadEntityScript |
| 2483 | 2558 | newDetails.scriptText = scriptText; |
| 2484 | − setEntityScriptDetails(entityID, newDetails); | |
| 2559 | + setEntityScriptDetails(entityID, scriptText, newDetails); | |
| 2485 | 2560 | } |
| 2486 | 2561 | |
| 2487 | 2562 | stopAllTimersForEntityScript(entityID); |
| @@ -2508,13 +2583,17 @@ void ScriptManager::unloadAllEntityScripts(bool blockingCall) { · 20 unmodified lines | ||
| 2508 | 2583 | qCDebug(scriptengine) << "ScriptManager::unloadAllEntityScripts() called on correct thread [" << thread() << "]"; |
| 2509 | 2584 | #endif |
| 2510 | 2585 | |
| 2511 | − QList<EntityItemID> keys; | |
| 2586 | + std::vector<std::pair<EntityItemID, EntityScriptDetails>> scripts; | |
| 2512 | 2587 | { |
| 2513 | 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) { | |
| 2517 | − unloadEntityScript(entityID); | |
| 2595 | + foreach (const auto& idAndScript, scripts) { | |
| 2596 | + unloadEntityScript(idAndScript.first, idAndScript.second.scriptText, true); | |
| 2518 | 2597 | } |
| 2519 | 2598 | { |
| 2520 | 2599 | QWriteLocker locker{ &_entityScriptsLock }; |
| @@ -2531,8 +2610,8 @@ void ScriptManager::unloadAllEntityScripts(bool blockingCall) { · 10 unmodified lines | ||
| 2531 | 2610 | #endif // DEBUG_ENGINE_STATE |
| 2532 | 2611 | } |
| 2533 | 2612 | |
| 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)) { | |
| 2536 | 2615 | return; |
| 2537 | 2616 | } |
| 2538 | 2617 | |
| @@ -2545,7 +2624,7 @@ void ScriptManager::refreshFileScript(const EntityItemID& entityID) { · 6 unmodified lines | ||
| 2545 | 2624 | EntityScriptDetails details; |
| 2546 | 2625 | { |
| 2547 | 2626 | QWriteLocker locker { &_entityScriptsLock }; |
| 2548 | − details = _entityScripts[entityID]; | |
| 2627 | + details = _entityScripts[entityID][scriptURL]; | |
| 2549 | 2628 | } |
| 2550 | 2629 | // Check to see if a file based script needs to be reloaded (easier debugging) |
| 2551 | 2630 | if (details.lastModified > 0) { |
| @@ -2605,15 +2684,94 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 53 unmodified lines | ||
| 2605 | 2684 | "entityID:" << entityID << "methodName:" << methodName; |
| 2606 | 2685 | #endif |
| 2607 | 2686 | |
| 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 | + } | |
| 2610 | 2693 | } |
| 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); | |
| 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 | + | |
| 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)) { | |
| 2617 | 2775 | ScriptValue entityScript = details.scriptObject; // previously loaded |
| 2618 | 2776 | |
| 2619 | 2777 | // 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 | 2806 | args << scriptValueFromSequence(scriptEngine, params); |
| 2649 | 2807 | |
| 2650 | 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. | |
| 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); | |
| 2653 | 2815 | scriptEngine->globalObject().property("Script").setProperty("remoteCallerID", oldData); |
| 2654 | 2816 | } |
| 2655 | 2817 | } |
| 2656 | 2818 | } |
| 2657 | 2819 | |
| 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 | + | |
| 2658 | 2863 | void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) { |
| 2659 | 2864 | if (QThread::currentThread() != thread()) { |
| 2660 | 2865 | #ifdef THREAD_DEBUGGING |
| @@ -2673,23 +2878,27 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 12 unmodified lines | ||
| 2673 | 2878 | "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent"; |
| 2674 | 2879 | #endif |
| 2675 | 2880 | |
| 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 | + } | |
| 2678 | 2887 | } |
| 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); | |
| 2684 | 2891 | } |
| 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(); | |
| 2688 | 2896 | |
| 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 | + } | |
| 2693 | 2902 | } |
| 2694 | 2903 | } |
| 2695 | 2904 | } |
| @@ -2712,24 +2921,29 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q · 16 unmodified lines | ||
| 2712 | 2921 | "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; |
| 2713 | 2922 | #endif |
| 2714 | 2923 | |
| 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 | + } | |
| 2717 | 2930 | } |
| 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); | |
| 2723 | 2934 | } |
| 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(); | |
| 2727 | 2939 | |
| 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 | + } | |
| 2733 | 2947 | } |
| 2734 | 2948 | } |
| 2735 | 2949 | } |