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 | int ScriptManager::getNumRunningEntityScripts() const { | 1929 | int 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 | ||
| 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 | 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 | ||
| 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 | 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 | ||
| 1958 | QVariant ScriptManager::cloneEntityScriptDetails(const EntityItemID& entityID) { | 1960 | QVariant 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 | #endif | 1970 | #endif |
| 1969 | EntityScriptDetails scriptDetails; | 1971 | EntityScriptDetails scriptDetails; |
| 1970 | if (getEntityScriptDetails(entityID, scriptDetails)) { | 1972 | if (getEntityScriptDetails(entityID, scriptURL, scriptDetails)) { |
| 1971 | #ifdef DEBUG_ENTITY_STATES | 1973 | #ifdef DEBUG_ENTITY_STATES |
| 1972 | qCDebug(scriptengine) << "gotEntityScriptDetails" << scriptDetails.status << QThread::currentThread(); | 1974 | qCDebug(scriptengine) << "gotEntityScriptDetails" << scriptDetails.status << QThread::currentThread(); |
| 1973 | #endif | 1975 | #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 | ||
| 2000 | QFuture<QVariant> ScriptManager::getLocalEntityScriptDetails(const EntityItemID& entityID) { | 2002 | QFuture<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 | ||
| 2004 | bool ScriptManager::getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const { | 2006 | bool 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 | ||
| 2014 | bool ScriptManager::hasEntityScriptDetails(const EntityItemID& entityID) const { | 2020 | bool 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 | ||
| 2019 | void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) { | 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 | 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 away | 2048 | // 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_STATES | 2053 | #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 management | 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 | #ifdef DEBUG_ENTITY_STATES | 2085 | #ifdef DEBUG_ENTITY_STATES |
| 2076 | qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << entityID.toString(); | 2086 | qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << entityID.toString(); |
| 2077 | #endif | 2087 | #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_STATES | 2091 | #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 ERRORS | 2143 | // 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 method | 2436 | // 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. |
| 2441 | void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) { | 2451 | void ScriptManager::unloadEntityScript(const EntityItemID& entityID, const QString& scriptURL, bool shouldRemoveFromMap) { |
| 2442 | if (QThread::currentThread() != thread()) { | 2452 | if (QThread::currentThread() != thread()) { |
| 2443 | #ifdef THREAD_DEBUGGING | 2453 | #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 zero | 2458 | // 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 | #endif | 2467 | #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_STATES | 2476 | #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 map | 2482 | // 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 | |||
| 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 | _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 unloadEntityScript | 2557 | // 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 | #endif | 2584 | #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_STATE | 2610 | #endif // DEBUG_ENGINE_STATE |
| 2532 | } | 2611 | } |
| 2533 | 2612 | ||
| 2534 | void ScriptManager::refreshFileScript(const EntityItemID& entityID) { | 2613 | void 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 | #endif | 2685 | #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 | |||
| 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 | ScriptValue entityScript = details.scriptObject; // previously loaded | 2775 | 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 callable | 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 | 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 | ||
| 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 | void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) { | 2863 | void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) { |
| 2659 | if (QThread::currentThread() != thread()) { | 2864 | if (QThread::currentThread() != thread()) { |
| 2660 | #ifdef THREAD_DEBUGGING | 2865 | #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 | #endif | 2879 | #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 loaded | 2892 | 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 | #endif | 2922 | #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 loaded | 2935 | 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 | } |