mirror of
https://github.com/overte-org/overte.git
synced 2025-07-10 15:58:31 +02:00
1609 lines
47 KiB
C++
1609 lines
47 KiB
C++
//
|
|
// ScriptManager.h
|
|
// libraries/script-engine/src
|
|
//
|
|
// Created by Brad Hefta-Gaub on 12/14/13.
|
|
// Copyright 2013 High Fidelity, Inc.
|
|
// Copyright 2020 Vircadia contributors.
|
|
// Copyright 2022-2023 Overte e.V.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
#ifndef hifi_ScriptManager_h
|
|
#define hifi_ScriptManager_h
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <unordered_map>
|
|
#include <mutex>
|
|
|
|
#include <QtCore/QFuture>
|
|
#include <QtCore/QHash>
|
|
#include <QtCore/QObject>
|
|
#include <QtCore/QReadWriteLock>
|
|
#include <QtCore/QSet>
|
|
#include <QtCore/QSharedPointer>
|
|
#include <QtCore/QString>
|
|
#include <QtCore/QTimer>
|
|
#include <QtCore/QUrl>
|
|
#include <QtCore/QVariant>
|
|
|
|
#include "EntityItemID.h"
|
|
#include "EntitiesScriptEngineProvider.h"
|
|
#include "EntityScriptUtils.h"
|
|
#include <ExternalResource.h>
|
|
#include <SettingHandle.h>
|
|
|
|
#include "AssetScriptingInterface.h"
|
|
#include "ConsoleScriptingInterface.h"
|
|
#include "Mat4.h"
|
|
#include "PointerEvent.h"
|
|
#include "Quat.h"
|
|
#include "ScriptUUID.h"
|
|
#include "ScriptValue.h"
|
|
#include "ScriptException.h"
|
|
#include "Vec3.h"
|
|
|
|
static const QString NO_SCRIPT("");
|
|
|
|
static const int SCRIPT_FPS = 60;
|
|
static const int DEFAULT_MAX_ENTITY_PPS = 9000;
|
|
static const int DEFAULT_ENTITY_PPS_PER_SCRIPT = 900;
|
|
|
|
class ScriptEngine;
|
|
class ScriptEngines;
|
|
class ScriptManager;
|
|
class ScriptManagerScriptingInterface;
|
|
|
|
using ScriptEnginePointer = std::shared_ptr<ScriptEngine>;
|
|
using ScriptManagerPointer = std::shared_ptr<ScriptManager>;
|
|
using ScriptManagerScriptingInterfacePointer = std::shared_ptr<ScriptManagerScriptingInterface>;
|
|
using ScriptValueList = QList<ScriptValue>;
|
|
|
|
Q_DECLARE_METATYPE(ScriptManagerPointer)
|
|
|
|
const int QTREGISTER_QTimerStar = qRegisterMetaType<QTimer*>();
|
|
|
|
|
|
/**
|
|
* @brief Callback data for addEventHandler
|
|
*
|
|
*/
|
|
class CallbackData {
|
|
public:
|
|
/**
|
|
* @brief Function to call
|
|
*
|
|
*/
|
|
ScriptValue function;
|
|
|
|
/**
|
|
* @brief Entity ID
|
|
*
|
|
*/
|
|
EntityItemID definingEntityIdentifier;
|
|
|
|
/**
|
|
* @brief Sandbox URL for the script
|
|
*
|
|
*/
|
|
QUrl definingSandboxURL;
|
|
};
|
|
|
|
|
|
/**
|
|
* @brief DeferredLoadEntity
|
|
* @deprecated This appars unused
|
|
*
|
|
*/
|
|
class DeferredLoadEntity {
|
|
public:
|
|
EntityItemID entityID;
|
|
QString entityScript;
|
|
//bool forceRedownload;
|
|
};
|
|
|
|
/**
|
|
* @brief Entity with available script contents
|
|
*
|
|
*/
|
|
struct EntityScriptContentAvailable {
|
|
/**
|
|
* @brief Entity ID
|
|
*
|
|
*/
|
|
EntityItemID entityID;
|
|
|
|
/**
|
|
* @brief URL to the script, or the actual script if embedded in the URL field
|
|
*
|
|
*/
|
|
QString scriptOrURL;
|
|
|
|
/**
|
|
* @brief Contents of the script
|
|
*
|
|
*/
|
|
QString contents;
|
|
|
|
/**
|
|
* @brief Whether scriptOrURL contains an URL
|
|
*
|
|
*/
|
|
bool isURL;
|
|
|
|
/**
|
|
* @brief Whether the request has been successful
|
|
*
|
|
*/
|
|
bool success;
|
|
|
|
/**
|
|
* @brief Status as text
|
|
*
|
|
*/
|
|
QString status;
|
|
};
|
|
|
|
typedef std::unordered_map<EntityItemID, EntityScriptContentAvailable> EntityScriptContentAvailableMap;
|
|
|
|
typedef QList<CallbackData> CallbackList;
|
|
typedef QHash<QString, CallbackList> RegisteredEventHandlers;
|
|
|
|
|
|
/**
|
|
* @brief Details about an entity script
|
|
*
|
|
*/
|
|
class EntityScriptDetails {
|
|
public:
|
|
|
|
/**
|
|
* @brief Current status
|
|
*
|
|
*/
|
|
EntityScriptStatus status { EntityScriptStatus::PENDING };
|
|
|
|
/**
|
|
* @brief Error information
|
|
*
|
|
* If status indicates an error, this contains a human-readable string giving more information about the error.
|
|
*
|
|
*/
|
|
QString errorInfo { "" };
|
|
|
|
|
|
/**
|
|
* @brief The source code of the script
|
|
*
|
|
*/
|
|
QString scriptText { "" };
|
|
|
|
/**
|
|
* @brief The return value of the script
|
|
*
|
|
*/
|
|
ScriptValue scriptObject{ ScriptValue() };
|
|
|
|
/**
|
|
* @brief Last modified time of the underlying script file
|
|
*
|
|
* This is used to determine if the script needs reloading when it changes on disk.
|
|
*/
|
|
int64_t lastModified { 0 };
|
|
|
|
/**
|
|
* @brief URL under which the script is allowed to have access
|
|
*
|
|
* The script is allowed access below this URL (eg, sub-directories), but
|
|
* not to the parent context.
|
|
*/
|
|
QUrl definingSandboxURL { QUrl("about:EntityScript") };
|
|
};
|
|
|
|
// declare a static script initializers
|
|
#define STATIC_SCRIPT_TYPES_INITIALIZER(init) \
|
|
static ScriptManager::StaticTypesInitializerNode static_script_types_initializer_(init);
|
|
|
|
#define STATIC_SCRIPT_INITIALIZER(init) \
|
|
static ScriptManager::StaticInitializerNode static_script_initializer_(init);
|
|
|
|
|
|
|
|
|
|
/**
|
|
* @brief Manages a single scripting engine
|
|
*
|
|
* This class manages and sets up a single scripting engine to make it execute scripts.
|
|
*
|
|
* It passes the objects needed to expose the public API, provides console access and error
|
|
* reporting and event management.
|
|
*
|
|
* This manipulates a single underlying instance of ScriptEngine.
|
|
*
|
|
* Part of this class' functionality exists only to provide helper functions to the scripts that are
|
|
* run by the scripting engine, and shouldn't be considered part of the C++ API. Those are the functions
|
|
* in the "Script support methods", "Module support methods", "Entity Script methods", and "Scripting signals" sections.
|
|
*
|
|
* The script-facing interface is in ScriptManagerScriptingInterface and documented in JSDoc
|
|
* as the <a href="https://apidocs.overte.org/Script.html">Script</a> class.
|
|
*
|
|
* The ScriptManager provides the following functionality to scripts:
|
|
*
|
|
* * A math library: Quat, Vec3, Mat4
|
|
* * UUID generation: Uuid
|
|
* * Filesystem access: File
|
|
* * Console access: console, print
|
|
* * Resource access: Resource, Asset, Resources, ExternalPaths
|
|
* * Scripting system management: Script
|
|
* * Module loading: require
|
|
* * Web access: XMLHttpRequest, WebSocket
|
|
* * Other miscellaneous functionality.
|
|
*
|
|
* Example:
|
|
*
|
|
* @code {.cpp}
|
|
* #include "ScriptManager.h"
|
|
*
|
|
* // Scripts only stop running when Script.stop() is called.
|
|
* // In the normal environment this isn't needed, but for things like unit tests we need
|
|
* // to use it to make the ScriptManager return from run().
|
|
*
|
|
* QString scriptSource = "print(\"Hello, world!\"); Script.stop(true);";
|
|
* QString scriptFilename = "test.js";
|
|
*
|
|
* ScriptManagerPointer sm = newScriptManager(ScriptManager::NETWORKLESS_TEST_SCRIPT, scriptSource, scriptFilename);
|
|
* connect(sm.get(), &ScriptManager::printedMessage, [](const QString& message, const QString& engineName){
|
|
* qCDebug(scriptengine) << "Printed message from engine" << engineName << ": " << message;
|
|
* });
|
|
*
|
|
* qInfo() << "Running script!";
|
|
* sm->run();
|
|
* qInfo() << "Done!"
|
|
* @endcode
|
|
*
|
|
* @note
|
|
* Technically, the ScriptManager isn't generic enough -- it implements things that imitate
|
|
* Node.js for examine in the module loading code, which makes it JS specific. This code
|
|
* should probably be moved into the JS ScriptEngine class instead.
|
|
*
|
|
* The EntityScript functionality might also benefit from being split off into a separate
|
|
* class, for better organization.
|
|
*
|
|
* Some more functionality can be shifted to ScriptManagerScriptingInterface, since
|
|
* it only provides services to scripts and isn't called from C++.
|
|
*/
|
|
class ScriptManager : public QObject, public EntitiesScriptEngineProvider, public std::enable_shared_from_this<ScriptManager> {
|
|
Q_OBJECT
|
|
Q_PROPERTY(QString context READ getContext)
|
|
Q_PROPERTY(QString type READ getTypeAsString)
|
|
Q_PROPERTY(QString fileName MEMBER _fileNameString CONSTANT)
|
|
public:
|
|
static const QString SCRIPT_EXCEPTION_FORMAT;
|
|
static const QString SCRIPT_BACKTRACE_SEP;
|
|
|
|
/**
|
|
* @brief Context of the script
|
|
*
|
|
*/
|
|
enum Context {
|
|
/**
|
|
* @brief Client script.
|
|
* Allowed to access local HTML files on UI created from C++ calls.
|
|
*
|
|
*/
|
|
CLIENT_SCRIPT,
|
|
|
|
/**
|
|
* @brief Entity client script
|
|
*
|
|
*/
|
|
ENTITY_CLIENT_SCRIPT,
|
|
|
|
/**
|
|
* @brief Entity server script
|
|
*
|
|
*/
|
|
ENTITY_SERVER_SCRIPT,
|
|
|
|
/**
|
|
* @brief Agent script
|
|
*
|
|
*/
|
|
AGENT_SCRIPT,
|
|
|
|
/**
|
|
* @brief Network-less test system context.
|
|
* This is used for the QTest self-tests, and minimizes the API that is made available to
|
|
* the running script. It removes the need for network access, which makes for much faster
|
|
* test execution.
|
|
*
|
|
*
|
|
* @warning This is a development-targeted bit of functionality.
|
|
*
|
|
* @warning This is going to break functionality like loadURL and require
|
|
*/
|
|
NETWORKLESS_TEST_SCRIPT
|
|
};
|
|
|
|
/**
|
|
* @brief Type of the script
|
|
*
|
|
*/
|
|
enum Type {
|
|
/**
|
|
* @brief Client
|
|
*
|
|
*/
|
|
CLIENT,
|
|
|
|
/**
|
|
* @brief Entity client
|
|
* Receives the update event.
|
|
*/
|
|
ENTITY_CLIENT,
|
|
|
|
/**
|
|
* @brief Entity server
|
|
* Receives the update event
|
|
*
|
|
*/
|
|
ENTITY_SERVER,
|
|
|
|
/**
|
|
* @brief Agent script
|
|
*
|
|
*/
|
|
AGENT,
|
|
|
|
/**
|
|
* @brief Avatar script
|
|
*
|
|
*/
|
|
AVATAR,
|
|
|
|
/**
|
|
* @brief Test system script
|
|
*
|
|
* This is used for the QTest self-tests, and minimizes the API that is made available to
|
|
* the running script. It removes the need for network access, which makes for much faster
|
|
* test execution.
|
|
*
|
|
* @warning This is a development-targeted bit of functionality.
|
|
*/
|
|
NETWORKLESS_TEST
|
|
};
|
|
Q_ENUM(Type);
|
|
|
|
static int processLevelMaxRetries;
|
|
ScriptManager(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine"));
|
|
~ScriptManager();
|
|
|
|
// static initialization support
|
|
typedef void (*ScriptManagerInitializer)(ScriptManager*);
|
|
class StaticInitializerNode {
|
|
public:
|
|
ScriptManagerInitializer init;
|
|
StaticInitializerNode* prev;
|
|
inline StaticInitializerNode(ScriptManagerInitializer&& pInit) : init(std::move(pInit)),prev(nullptr) { registerNewStaticInitializer(this); }
|
|
};
|
|
static void registerNewStaticInitializer(StaticInitializerNode* dest);
|
|
|
|
class StaticTypesInitializerNode {
|
|
public:
|
|
ScriptManagerInitializer init;
|
|
StaticTypesInitializerNode* prev;
|
|
inline StaticTypesInitializerNode(ScriptManagerInitializer&& pInit) : init(std::move(pInit)),prev(nullptr) { registerNewStaticTypesInitializer(this); }
|
|
};
|
|
static void registerNewStaticTypesInitializer(StaticTypesInitializerNode* dest);
|
|
|
|
/**
|
|
* @brief Run the script in a dedicated thread
|
|
*
|
|
* This will have the side effect of evaluating the current script contents and calling run().
|
|
* Callers will likely want to register the script with external services before calling this.
|
|
*
|
|
* This function will return immediately, and work will continue on the newly created thread.
|
|
*
|
|
* @note Can't be called twice.
|
|
* @note The underlying thread is not accessible.
|
|
*/
|
|
void runInThread();
|
|
|
|
/**
|
|
* @brief Run the script in the caller's thread, exit when Script.stop() is called.
|
|
*
|
|
* Most scripts never stop running, so this function will never return for them.
|
|
*/
|
|
void run();
|
|
|
|
|
|
/**
|
|
* @brief Get the filename of the running script, without the path.
|
|
*
|
|
* @return QString Filename
|
|
*/
|
|
QString getFilename() const;
|
|
|
|
/**
|
|
* @brief Underlying scripting engine
|
|
*
|
|
* @return ScriptEnginePointer Scripting engine
|
|
*/
|
|
inline ScriptEnginePointer engine() { return _engine; }
|
|
|
|
QList<EntityItemID> getListOfEntityScriptIDs();
|
|
|
|
bool isStopped() const;
|
|
|
|
|
|
/**
|
|
* @name Script support functions
|
|
*
|
|
* These functions exist to support the scripting API
|
|
*/
|
|
|
|
///@{
|
|
|
|
/**
|
|
* @brief Stops and unloads the current script.
|
|
*
|
|
* @note This is part of the public scripting API for Agent scripts and local scripts, but not for EntityScripts
|
|
* @param marshal Deprecated
|
|
*/
|
|
Q_INVOKABLE void stop(bool marshal = false);
|
|
|
|
///@}
|
|
|
|
/**
|
|
* @brief Stop any evaluating scripts and wait for the scripting thread to finish.
|
|
*
|
|
* @param shutdown True if we are currently shutting down. Setting this to true will allow
|
|
* processing events emitted during the script's shutdown, such as scripts saving settings.
|
|
*
|
|
* @note This function has an internal timeout, and will forcefully abort the script if it
|
|
* takes too long.
|
|
*/
|
|
void waitTillDoneRunning(bool shutdown = false);
|
|
|
|
/**
|
|
* @brief Load a script from a given URL
|
|
*
|
|
* If the script engine is not already running, this will download the URL and start the process of seting it up
|
|
* to run.
|
|
*
|
|
*
|
|
* @param scriptURL URL where to load the script from. Can be http, https, atp, or file protocol. The file extension
|
|
* has to pass hasValidScriptSuffix().
|
|
* @param reload Load the script again even if it's in the cache.
|
|
*
|
|
* @note For file:// URLs, only URLs under the default scripts location are allowed.
|
|
* @see PathUtils::defaultScriptsLocation
|
|
*/
|
|
void loadURL(const QUrl& scriptURL, bool reload);
|
|
|
|
/**
|
|
* @brief Determines whether a script filename has the right suffix
|
|
*
|
|
*
|
|
* @param scriptFileName
|
|
* @return true When the script has the right file extension (eg, .js)
|
|
* @return false Otherwise
|
|
*/
|
|
bool hasValidScriptSuffix(const QString& scriptFileName);
|
|
|
|
|
|
/**
|
|
* @name Script support methods
|
|
*
|
|
* These functions exist to support the scripting API
|
|
*/
|
|
|
|
///@{
|
|
|
|
/**
|
|
* @brief Gets the context that the script is running in: Interface/avatar, client entity, server entity, or assignment client.
|
|
*
|
|
* @note This is part of the public JS API
|
|
* @return QString
|
|
*/
|
|
Q_INVOKABLE QString getContext() const;
|
|
|
|
/**
|
|
* @brief Checks whether the script is running as an Interface or avatar script.
|
|
* @note This is part of the public JS API
|
|
* @return bool
|
|
*/
|
|
Q_INVOKABLE bool isClientScript() const { return _context == CLIENT_SCRIPT; }
|
|
|
|
/**
|
|
* @brief Checks whether the application was compiled as a debug build.
|
|
* @note This is part of the public JS API
|
|
* @return bool
|
|
*/
|
|
Q_INVOKABLE bool isDebugMode() const;
|
|
|
|
/**
|
|
* @brief Checks whether the script is running as a client entity script.
|
|
*
|
|
* @return bool
|
|
*/
|
|
Q_INVOKABLE bool isEntityClientScript() const { return _context == ENTITY_CLIENT_SCRIPT; }
|
|
|
|
|
|
/**
|
|
* @brief Checks whether the script is running as a server entity script.
|
|
*
|
|
* @return bool
|
|
*/
|
|
Q_INVOKABLE bool isEntityServerScript() const { return _context == ENTITY_SERVER_SCRIPT; }
|
|
|
|
/**
|
|
* @brief Checks whether the script is running as an assignment client script.
|
|
*
|
|
* @return bool
|
|
*/
|
|
Q_INVOKABLE bool isAgentScript() const { return _context == AGENT_SCRIPT; }
|
|
|
|
/**
|
|
* @brief Registers a global object by name.
|
|
*
|
|
* @param valueName
|
|
* @param value
|
|
*/
|
|
Q_INVOKABLE void registerValue(const QString& valueName, ScriptValue value);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// NOTE - these are intended to be public interfaces available to scripts
|
|
|
|
/**
|
|
* @brief Format an exception and return it as a string
|
|
*
|
|
* @param exception Exception object, containing the exception information.
|
|
* @param includeExtendedDetails Include additional troubleshooting information from the "detail" property, if there's one
|
|
* @return QString A multi-line string containing the formatted exception
|
|
*/
|
|
Q_INVOKABLE QString formatException(const ScriptValue& exception, bool includeExtendedDetails);
|
|
|
|
|
|
|
|
/**
|
|
* @brief Adds a function to the list of functions called when a particular event occurs on a particular entity.
|
|
*
|
|
* @param entityID Entity ID
|
|
* @param eventName Name of the event
|
|
* @param handler Event handler
|
|
*
|
|
* @note The same handler can be added multiple times.
|
|
*/
|
|
Q_INVOKABLE void addEventHandler(const EntityItemID& entityID, const QString& eventName, const ScriptValue& handler);
|
|
|
|
/**
|
|
* @brief Removes a function from the list of functions called when an entity event occurs on a particular entity.
|
|
*
|
|
* @param entityID Entity ID
|
|
* @param eventName Name if the event
|
|
* @param handler Event handler
|
|
*/
|
|
Q_INVOKABLE void removeEventHandler(const EntityItemID& entityID, const QString& eventName, const ScriptValue& handler);
|
|
|
|
|
|
/**
|
|
* @brief Starts running another script in Interface, if it isn't already running. The script is not automatically loaded next
|
|
* time Interface starts.
|
|
*
|
|
* The script is loaded as a stand-alone script.
|
|
*
|
|
* @param loadfile File to load
|
|
* @warning In practice this seems equivalent to calling loadScript or reloadScript. It reacts to _isReloading in an odd-looking manner.
|
|
* Is this function superfluous?
|
|
*/
|
|
Q_INVOKABLE void load(const QString& loadfile);
|
|
|
|
/**
|
|
* @brief Includes JavaScript from other files in the current script.
|
|
*
|
|
* If a callback is specified, the included files will be loaded asynchronously and the callback will be called
|
|
* when all of the files have finished loading.
|
|
* If no callback is specified, the included files will be loaded synchronously and will block execution until
|
|
* all of the files have finished loading.
|
|
*
|
|
* @param includeFiles List of files to include
|
|
* @param callback Callback to call when the files have finished loading.
|
|
*/
|
|
Q_INVOKABLE void include(const QStringList& includeFiles, const ScriptValue& callback = ScriptValue());
|
|
|
|
/**
|
|
* @brief Includes JavaScript from another file in the current script.
|
|
*
|
|
* If a callback is specified, the included files will be loaded asynchronously and the callback will be called
|
|
* when all of the files have finished loading.
|
|
* If no callback is specified, the included files will be loaded synchronously and will block execution until
|
|
* all of the files have finished loading.
|
|
*
|
|
* @param includeFile
|
|
* @param callback
|
|
*/
|
|
Q_INVOKABLE void include(const QString& includeFile, const ScriptValue& callback = ScriptValue());
|
|
|
|
///@}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/**
|
|
* @name Module support methods
|
|
*
|
|
*/
|
|
///@{
|
|
|
|
/**
|
|
* @brief Provides access to methods or objects provided in an external JavaScript or JSON file.
|
|
*
|
|
* Implements CommonJS/Node.js like require/module support
|
|
*
|
|
* @param moduleId Module to load
|
|
* @return ScriptValue
|
|
*/
|
|
Q_INVOKABLE ScriptValue require(const QString& moduleId);
|
|
|
|
/**
|
|
* @brief Resets the module cache
|
|
*
|
|
* @deprecated
|
|
* @param deleteScriptCache
|
|
*/
|
|
Q_INVOKABLE void resetModuleCache(bool deleteScriptCache = false);
|
|
|
|
/**
|
|
* @brief The current parent module from the running JS script
|
|
*
|
|
*
|
|
* @return ScriptValue Module. May be null or empty.
|
|
*/
|
|
ScriptValue currentModule();
|
|
|
|
/**
|
|
* @brief Replaces or adds "module" to "parent.children[]" array
|
|
*
|
|
* This is an internal use function used as a part of the 'require' implementation.
|
|
*
|
|
* @param module Module to register
|
|
* @param parent Parent
|
|
* @return true Registration successful
|
|
* @return false Registration failed, if the parent isn't a valid module
|
|
*/
|
|
bool registerModuleWithParent(const ScriptValue& module, const ScriptValue& parent);
|
|
|
|
/**
|
|
* @brief creates a new JS "module" Object with default metadata properties
|
|
*
|
|
* This imitates what is provided by https://nodejs.org/api/modules.html
|
|
*
|
|
* @param modulePath File path to the module
|
|
* @param parent Parent module
|
|
* @return ScriptValue Created module object
|
|
*/
|
|
ScriptValue newModule(const QString& modulePath, const ScriptValue& parent = ScriptValue());
|
|
|
|
/**
|
|
* @brief Synchronously fetch a module's source code
|
|
*
|
|
* The return value is a map containing the following fields:
|
|
*
|
|
* * "status" -- A string indicating the status of the operation
|
|
* * "success" -- A true or false value indicating success or failure
|
|
* * "url" -- The URL of the source. May not be present.
|
|
* * "contents" -- The contents of the source. May not be present.
|
|
*
|
|
* @param modulePath Path to the module's source code
|
|
* @param forceDownload Force a redownload even if the source is already in the cache
|
|
* @return QVariantMap The result of the operation
|
|
*/
|
|
QVariantMap fetchModuleSource(const QString& modulePath, const bool forceDownload = false);
|
|
|
|
/**
|
|
* @brief Evaluate a pending module object using the fetched source code
|
|
*
|
|
* @param module Module object
|
|
* @param sourceCode Source code to evaluate
|
|
* @return ScriptValue The result of evaluating the source code
|
|
*/
|
|
ScriptValue instantiateModule(const ScriptValue& module, const QString& sourceCode);
|
|
|
|
/**
|
|
* @brief Evaluate a program in the underlying scripting engine
|
|
*
|
|
* This simply calls to ScriptEngine::evaluate()
|
|
*
|
|
* @param program Source of the program
|
|
* @param fileName Filename it was obtained from
|
|
* @return ScriptValue Result of the evaluation
|
|
*/
|
|
ScriptValue evaluate(const QString& program, const QString& fileName = QString());
|
|
|
|
/**
|
|
* @brief Calls a function repeatedly, at a set interval.
|
|
*
|
|
* @note This is a JS API service.
|
|
*
|
|
* @param function Function to call
|
|
* @param intervalMS Interval at which to call the function, in ms
|
|
* @return QTimer* A pointer to the timer
|
|
*/
|
|
Q_INVOKABLE QTimer* setInterval(const ScriptValue& function, int intervalMS);
|
|
|
|
|
|
/**
|
|
* @brief Calls a function once, after a delay.
|
|
*
|
|
* @note This is a JS API service.
|
|
*
|
|
* @param function Function to call
|
|
* @param timeoutMS How long to wait before calling the function, in ms
|
|
* @return QTimer* A pointer to the timer
|
|
*/
|
|
Q_INVOKABLE QTimer* setTimeout(const ScriptValue& function, int timeoutMS);
|
|
|
|
/**
|
|
* @brief Stops an interval timer
|
|
*
|
|
* @param timer Timer to stop
|
|
*/
|
|
Q_INVOKABLE void clearInterval(QTimer* timer) { stopTimer(timer); }
|
|
|
|
/**
|
|
* @brief Stops an interval timer
|
|
*
|
|
* Overloaded version is needed in case the timer has expired
|
|
*
|
|
* @param timer Timer to stop
|
|
*/
|
|
Q_INVOKABLE void clearInterval(QVariantMap timer) { ; }
|
|
|
|
/**
|
|
* @brief Stops a timeout timer
|
|
*
|
|
* @param timer Timer to stop
|
|
*/
|
|
Q_INVOKABLE void clearTimeout(QTimer* timer) { stopTimer(timer); }
|
|
|
|
/**
|
|
* @brief Stops a timeout timer
|
|
* Overloaded version is needed in case the timer has expired
|
|
*
|
|
* @param timer Timer to stop
|
|
*/
|
|
|
|
Q_INVOKABLE void clearTimeout(QVariantMap timer) { ; }
|
|
|
|
|
|
/**
|
|
* @brief Prints a message to the program log
|
|
*
|
|
* @param message
|
|
*/
|
|
Q_INVOKABLE void print(const QString& message);
|
|
|
|
/**
|
|
* @brief Resolves a relative path to an absolute path. The relative path is relative to the script's location.
|
|
*
|
|
* @param path
|
|
* @return QUrl
|
|
*/
|
|
Q_INVOKABLE QUrl resolvePath(const QString& path) const;
|
|
|
|
/**
|
|
* @brief Gets the path to the resources directory for QML files.
|
|
*
|
|
* @return QUrl
|
|
*/
|
|
Q_INVOKABLE QUrl resourcesPath() const;
|
|
|
|
/**
|
|
* @brief Starts timing a section of code in order to send usage data about it to Overte. Shouldn't be used outside of the
|
|
* standard scripts.
|
|
* @param label
|
|
*/
|
|
Q_INVOKABLE void beginProfileRange(const QString& label) const;
|
|
|
|
/**
|
|
* @brief Finishes timing a section of code in order to send usage data about it to Overte. Shouldn't be used outside of
|
|
* the standard scripts
|
|
* @param label
|
|
*/
|
|
Q_INVOKABLE void endProfileRange(const QString& label) const;
|
|
|
|
|
|
///@}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Entity Script Related methods
|
|
|
|
|
|
/**
|
|
* @name Entity Script methods
|
|
*
|
|
*/
|
|
|
|
///@{
|
|
|
|
/**
|
|
* @brief Checks whether an entity has an entity script running.
|
|
*
|
|
* @param entityID
|
|
* @return bool
|
|
*/
|
|
Q_INVOKABLE bool isEntityScriptRunning(const EntityItemID& entityID) {
|
|
QReadLocker locker { &_entityScriptsLock };
|
|
auto it = _entityScripts.constFind(entityID);
|
|
return it != _entityScripts.constEnd() && it->status == EntityScriptStatus::RUNNING;
|
|
}
|
|
|
|
/**
|
|
* @brief Clone the details of an entity script
|
|
*
|
|
* @param entityID Entity ID
|
|
* @return QVariant Copy of the details
|
|
*/
|
|
QVariant cloneEntityScriptDetails(const EntityItemID& entityID);
|
|
|
|
|
|
/**
|
|
* @brief Get the details of a local entity script
|
|
*
|
|
* Same as cloneEntityScriptDetails, only as a QFuture.
|
|
*
|
|
* @param entityID Entity ID
|
|
* @return QFuture<QVariant>
|
|
*/
|
|
QFuture<QVariant> getLocalEntityScriptDetails(const EntityItemID& entityID) override;
|
|
|
|
|
|
/**
|
|
* @brief Manually runs the JavaScript garbage collector which reclaims memory by disposing of objects that are no longer reachable
|
|
*
|
|
*/
|
|
Q_INVOKABLE void requestGarbageCollection();
|
|
|
|
/**
|
|
* @brief Prints out current backtrace to the log.
|
|
*
|
|
* @param title
|
|
*/
|
|
Q_INVOKABLE void logBacktrace(const QString &title);
|
|
|
|
/**
|
|
* @brief Load an entity script
|
|
*
|
|
* @param entityID
|
|
* @param entityScript
|
|
* @param forceRedownload
|
|
*/
|
|
Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload);
|
|
|
|
/**
|
|
* @brief Unload an entity script
|
|
*
|
|
* @param entityID
|
|
* @param shouldRemoveFromMap
|
|
*/
|
|
Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap = false); // will call unload method
|
|
|
|
|
|
/**
|
|
* @brief Unload all entity scripts
|
|
*
|
|
* @param blockingCall
|
|
*/
|
|
Q_INVOKABLE void unloadAllEntityScripts(bool blockingCall = false);
|
|
|
|
/**
|
|
* @brief Call a method on an entity script
|
|
*
|
|
* @param entityID
|
|
* @param methodName
|
|
* @param params
|
|
* @param remoteCallerID
|
|
*/
|
|
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName,
|
|
const QStringList& params = QStringList(),
|
|
const QUuid& remoteCallerID = QUuid()) override;
|
|
|
|
/**
|
|
* @brief Call a method on an entity script
|
|
*
|
|
* @param entityID
|
|
* @param methodName
|
|
* @param event
|
|
*/
|
|
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event);
|
|
|
|
|
|
/**
|
|
* @brief Call a method on an entity script
|
|
*
|
|
* @param entityID
|
|
* @param methodName
|
|
* @param otherID
|
|
* @param collision
|
|
*/
|
|
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision);
|
|
|
|
|
|
/**
|
|
* @brief Set the script type
|
|
*
|
|
* @param type Type of this script
|
|
*/
|
|
void setType(Type type) { _type = type; };
|
|
|
|
/**
|
|
* @brief Returns the script type
|
|
*
|
|
* @return Type of this script
|
|
*/
|
|
Type getType() { return _type; };
|
|
|
|
/**
|
|
* @brief Returns the type of the script as a string
|
|
*
|
|
* @return QString A string describing the script's type
|
|
*/
|
|
QString getTypeAsString() const;
|
|
|
|
/**
|
|
* @brief Whether the script has finished running
|
|
*
|
|
* The finished status is set by stop()
|
|
* @return true The script has finished
|
|
* @return false The script is running
|
|
*/
|
|
bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget
|
|
|
|
/**
|
|
* @brief Whether the script is running
|
|
*
|
|
* @return true The script is running
|
|
* @return false The script is not running
|
|
*/
|
|
bool isRunning() const { return _isRunning; } // used by ScriptWidget
|
|
|
|
// this is used by code in ScriptEngines.cpp during the "reload all" operation
|
|
/**
|
|
* @brief Whether this ScriptManager is stopping. Once this is true, it stays true.
|
|
*
|
|
* @return true We are stopping
|
|
* @return false We are not stopping
|
|
*/
|
|
bool isStopping() const { return _isStopping; }
|
|
|
|
/**
|
|
* @brief Disconnect all signals, except essential ones
|
|
*
|
|
* This disconnects all signals, except the destroyed() and finished() handlers that
|
|
* are needed for cleanup.
|
|
*/
|
|
void disconnectNonEssentialSignals();
|
|
|
|
|
|
///@}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* @brief Set whether this script was user-loaded
|
|
*
|
|
* This is used by Application to track if a script is user loaded or not.
|
|
* @note Consider finding a solution inside of Application so that the ScriptManager class is not polluted by this notion
|
|
*
|
|
* @param isUserLoaded Script is user-loaded.
|
|
*/
|
|
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
|
|
|
|
/**
|
|
* @brief Whether the script was user-loaded.
|
|
*
|
|
* This is used by Application to track if a script is user loaded or not.
|
|
* @note Consider finding a solution inside of Application so that the ScriptManager class is not polluted by this notion
|
|
*
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool isUserLoaded() const { return _isUserLoaded; }
|
|
|
|
/**
|
|
* @brief Set whether to quit when finished
|
|
*
|
|
* This is used by ScriptEngines
|
|
*
|
|
* @param quitWhenFinished
|
|
*/
|
|
void setQuitWhenFinished(const bool quitWhenFinished) { _quitWhenFinished = quitWhenFinished; }
|
|
|
|
/**
|
|
* @brief Whether to quit when finished
|
|
*
|
|
* This is read by ScriptEngines
|
|
*
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool isQuitWhenFinished() const { return _quitWhenFinished; }
|
|
|
|
/**
|
|
* @brief Set a function that determines whether to emit update events
|
|
*
|
|
* Function func will be called from run() to determine whether update() will be issued.
|
|
* The update() event will be called if func() returns true.
|
|
*
|
|
* @param func Function that determines whether update() events will be issued.
|
|
*/
|
|
void setEmitScriptUpdatesFunction(std::function<bool()> func) { _emitScriptUpdates = func; }
|
|
|
|
/**
|
|
* @brief Logs a script error message and emits an errorMessage event
|
|
*
|
|
* Emits errorMessage()
|
|
*
|
|
* @param message Message to send to the log
|
|
*/
|
|
void scriptErrorMessage(const QString& message);
|
|
|
|
/**
|
|
* @brief Logs a script warning message and emits an warningMessage event
|
|
*
|
|
* Emits warningMessage()
|
|
*
|
|
* @param message Message to send to the log
|
|
*/
|
|
void scriptWarningMessage(const QString& message);
|
|
|
|
/**
|
|
* @brief Logs a script info message and emits an infoMessage event
|
|
*
|
|
* Emits infoMessage()
|
|
*
|
|
* @param message Message to send to the log
|
|
*/
|
|
void scriptInfoMessage(const QString& message);
|
|
|
|
/**
|
|
* @brief Logs a script printed message and emits an printedMessage event
|
|
*
|
|
* These are messages scripts provide by calling the print function.
|
|
* Emits printedMessage()
|
|
*
|
|
* @param message Message to send to the log
|
|
*/
|
|
|
|
void scriptPrintedMessage(const QString& message);
|
|
|
|
/**
|
|
* @brief Clears the debug log window
|
|
*
|
|
* This only emits clearDebugWindow()
|
|
*
|
|
*/
|
|
void clearDebugLogWindow();
|
|
|
|
/**
|
|
* @brief Get the number of running entity scripts
|
|
*
|
|
* @return int Number of scripts with the status EntityScriptStatus::RUNNING
|
|
*/
|
|
int getNumRunningEntityScripts() const;
|
|
|
|
/**
|
|
* @brief Retrieves the details about an entity script
|
|
*
|
|
* @param entityID Entity ID
|
|
* @param details Returned details
|
|
* @return true If the entity ID was found
|
|
* @return false If the entity ID wasn't found. details will be unmodified.
|
|
*/
|
|
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;
|
|
|
|
/**
|
|
* @brief Whether there are script details for a given entity ID
|
|
*
|
|
* @param entityID Entity ID
|
|
* @return true There is an entity script for this entity
|
|
* @return false There's no entity script
|
|
*/
|
|
bool hasEntityScriptDetails(const EntityItemID& entityID) const;
|
|
|
|
/**
|
|
* @brief Set a shared pointer to the ScriptEngines class
|
|
*
|
|
* This is used to ask ScriptEngines whether the system is being stopped.
|
|
* Setting this is optional.
|
|
*
|
|
* isStopped() is implemented by asking ScriptEngines.
|
|
*
|
|
* @param scriptEngines ScriptEngines class
|
|
*/
|
|
void setScriptEngines(QSharedPointer<ScriptEngines>& scriptEngines) { _scriptEngines = scriptEngines; }
|
|
|
|
|
|
/**
|
|
* @brief Call all the registered event handlers on an entity for the specified name.
|
|
*
|
|
* Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
|
|
*
|
|
* @param entityID
|
|
* @param eventName
|
|
* @param eventHanderArgs
|
|
*/
|
|
void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, const ScriptValueList& eventHanderArgs);
|
|
|
|
/**
|
|
* @brief Remove all event handlers for the specified entityID (i.e. the entity is being removed)
|
|
*
|
|
* @param entityID Entity ID
|
|
*/
|
|
void removeAllEventHandlers(const EntityItemID& entityID);
|
|
|
|
|
|
/**
|
|
* @brief Return value of the script that finished running
|
|
*
|
|
* This should only be used after the script terminates.
|
|
*
|
|
* @return ScriptValue
|
|
*/
|
|
ScriptValue getReturnValue() const { return _returnValue; }
|
|
|
|
|
|
/**
|
|
* @brief Gets the URL for an asset in an external resource bucket.
|
|
*
|
|
* @param bucket
|
|
* @param path
|
|
* @return Q_INVOKABLE
|
|
*/
|
|
Q_INVOKABLE QString getExternalPath(ExternalResource::Bucket bucket, const QString& path);
|
|
|
|
|
|
/**
|
|
* @brief Get the uncaught exception from the underlying script engine
|
|
*
|
|
* @return std::shared_ptr<ScriptException> Exception
|
|
*/
|
|
std::shared_ptr<ScriptException> getUncaughtException() const;
|
|
|
|
/**
|
|
* @brief Whether this engine will abort on an uncaught exception
|
|
*
|
|
* @warning This probably should be refactored into a more comprehensive per-script flags system
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool getAbortOnUncaughtException() const { return _abortOnUncaughtException; }
|
|
|
|
/**
|
|
* @brief Whether to abort on an uncaught exception
|
|
*
|
|
* @warning This probably should be refactored into a more comprehensive per-script flags system
|
|
* @param value
|
|
*/
|
|
void setAbortOnUncaughtException(bool value) { _abortOnUncaughtException = value; }
|
|
|
|
public slots:
|
|
|
|
/**
|
|
* @brief Script.updateMemoryCost
|
|
*
|
|
* Sends a memory cost update to the underlying scripting engine
|
|
*
|
|
* @param deltaSize Difference in memory usage
|
|
* @deprecated Deprecated
|
|
*/
|
|
void updateMemoryCost(const qint64 &deltaSize);
|
|
|
|
signals:
|
|
|
|
|
|
/**
|
|
* @name Scripting events
|
|
*
|
|
*/
|
|
///@{
|
|
|
|
/**
|
|
* @brief Script.scriptLoaded
|
|
* @deprecated
|
|
* @param scriptFilename
|
|
*/
|
|
void scriptLoaded(const QString& scriptFilename);
|
|
|
|
|
|
/**
|
|
* @brief Script.errorLoadingScript
|
|
* @deprecated
|
|
* @param scriptFilename
|
|
*/
|
|
void errorLoadingScript(const QString& scriptFilename);
|
|
|
|
|
|
/**
|
|
* @brief Triggered frequently at a system-determined interval.
|
|
*
|
|
* @param deltaTime
|
|
*/
|
|
void update(float deltaTime);
|
|
|
|
|
|
|
|
/**
|
|
* @brief Triggered when the script is stopping.
|
|
*
|
|
*/
|
|
void scriptEnding();
|
|
|
|
|
|
|
|
/**
|
|
* @brief Script.finished
|
|
*
|
|
* @param fileNameString
|
|
*/
|
|
void finished(const QString& fileNameString, ScriptManagerPointer);
|
|
|
|
/**
|
|
* @brief Triggered when the script prints a message to the program log
|
|
*
|
|
* @param message
|
|
* @param scriptName
|
|
*/
|
|
void printedMessage(const QString& message, const QString& scriptName);
|
|
|
|
|
|
/**
|
|
* @brief Triggered when the script generates an error
|
|
*
|
|
* @param message
|
|
* @param scriptName
|
|
*/
|
|
void errorMessage(const QString& message, const QString& scriptName);
|
|
|
|
|
|
|
|
/**
|
|
* @brief Triggered when the script generates a warning
|
|
*
|
|
* @param message
|
|
* @param scriptName
|
|
*/
|
|
void warningMessage(const QString& message, const QString& scriptName);
|
|
|
|
|
|
/**
|
|
* @brief Triggered when the script generates an information message
|
|
*
|
|
* @param message
|
|
* @param scriptName
|
|
*/
|
|
void infoMessage(const QString& message, const QString& scriptName);
|
|
|
|
|
|
/**
|
|
* @brief Triggered when the running state of the script changes, e.g., from running to stopping.
|
|
*
|
|
*/
|
|
void runningStateChanged();
|
|
|
|
|
|
/**
|
|
* @brief Script.clearDebugWindow
|
|
* @deprecated
|
|
*/
|
|
void clearDebugWindow();
|
|
|
|
|
|
/**
|
|
* @brief Script.loadScript
|
|
* @deprecated
|
|
* @param scriptName
|
|
* @param isUserLoaded
|
|
*/
|
|
void loadScript(const QString& scriptName, bool isUserLoaded);
|
|
|
|
|
|
/**
|
|
* @brief Script.reloadScript
|
|
*
|
|
* @param scriptName
|
|
* @param isUserLoaded
|
|
*/
|
|
void reloadScript(const QString& scriptName, bool isUserLoaded);
|
|
|
|
/**
|
|
* @brief Triggered when the script has stopped.
|
|
*
|
|
*/
|
|
void doneRunning();
|
|
|
|
/**
|
|
* @brief Emitted when an entity script is added or removed, or when the status of an entity
|
|
* script is updated (goes from RUNNING to ERROR_RUNNING_SCRIPT, for example)
|
|
*
|
|
*/
|
|
void entityScriptDetailsUpdated();
|
|
|
|
/**
|
|
* @brief Emitted when an entity script has finished running preload
|
|
*
|
|
* @param entityID
|
|
*/
|
|
void entityScriptPreloadFinished(const EntityItemID& entityID);
|
|
|
|
|
|
/**
|
|
* @brief Triggered when a script generates an unhandled exception.
|
|
*
|
|
* @param exception
|
|
*/
|
|
void unhandledException(std::shared_ptr<ScriptException> exception);
|
|
|
|
///@}
|
|
|
|
/**
|
|
* @brief Triggered once before the first call to Script.addEventHandler happens on this ScriptManager
|
|
* connections assumed to use Qt::DirectConnection
|
|
*
|
|
* @note not for use by scripts
|
|
*/
|
|
void attachDefaultEventHandlers();
|
|
|
|
/**
|
|
* @brief Triggered repeatedly in the scripting loop to ensure entity edit messages get processed properly
|
|
* connections assumed to use Qt::DirectConnection
|
|
*
|
|
* @note Not for use by scripts
|
|
* @param wait
|
|
*/
|
|
void releaseEntityPacketSenderMessages(bool wait);
|
|
|
|
protected:
|
|
// Is called by the constructor, bceause all types need to be registered before method discovery with ScriptObjectV8Proxy::investigate()
|
|
void initMetaTypes();
|
|
|
|
/**
|
|
* @brief Initializes the underlying scripting engine
|
|
*
|
|
* This sets up the scripting engine with the default APIs
|
|
*/
|
|
void init();
|
|
|
|
/**
|
|
* @brief executeOnScriptThread
|
|
*
|
|
* @deprecated
|
|
* @param function
|
|
* @param type
|
|
*/
|
|
Q_INVOKABLE void executeOnScriptThread(std::function<void()> function, const Qt::ConnectionType& type = Qt::QueuedConnection );
|
|
|
|
/**
|
|
* @brief Script._requireResolve
|
|
*
|
|
* @note this is not meant to be called directly, but just to have QMetaObject take care of wiring it up in general;
|
|
* then inside of init() we just have to do "Script.require.resolve = Script._requireResolve;"
|
|
*
|
|
* @deprecated
|
|
* @param moduleId
|
|
* @param relativeTo
|
|
* @return QString
|
|
*/
|
|
Q_INVOKABLE QString _requireResolve(const QString& moduleId, const QString& relativeTo = QString());
|
|
|
|
/**
|
|
* @brief Log an exception
|
|
*
|
|
* This both sends an exception to the log as an error message, and returns the formatted
|
|
* text as a string.
|
|
*
|
|
* @param exception Exception
|
|
* @return QString Exception formatted as a string
|
|
*/
|
|
QString logException(const ScriptValue& exception);
|
|
void timerFired();
|
|
void stopAllTimers();
|
|
void stopAllTimersForEntityScript(const EntityItemID& entityID);
|
|
void refreshFileScript(const EntityItemID& entityID);
|
|
|
|
/**
|
|
* @brief Updates the status of an entity script
|
|
*
|
|
* Emits entityScriptDetailsUpdated()
|
|
*
|
|
* @param entityID Entity ID
|
|
* @param status Status
|
|
* @param errorInfo Description of the error, if any
|
|
*/
|
|
void updateEntityScriptStatus(const EntityItemID& entityID, const EntityScriptStatus& status, const QString& errorInfo = QString());
|
|
|
|
|
|
/**
|
|
* @brief Set the details for an entity script
|
|
*
|
|
* @param entityID Entity ID
|
|
* @param details Details
|
|
*/
|
|
void setEntityScriptDetails(const EntityItemID& entityID, const EntityScriptDetails& details);
|
|
|
|
/**
|
|
* @brief Set the parent URL, used to resolve relative paths
|
|
*
|
|
* Relative paths are resolved respect of this URL
|
|
*
|
|
* @param parentURL Parent URL
|
|
*/
|
|
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
|
|
|
|
/**
|
|
* @brief Creates a timer with the specified interval
|
|
*
|
|
* @param function Function to call when the interval elapses
|
|
* @param intervalMS Interval in milliseconds
|
|
* @param isSingleShot Whether the timer happens continuously or a single time
|
|
* @return QTimer*
|
|
*/
|
|
QTimer* setupTimerWithInterval(const ScriptValue& function, int intervalMS, bool isSingleShot);
|
|
|
|
/**
|
|
* @brief Stops a timer
|
|
*
|
|
* @param timer Timer to stop
|
|
*/
|
|
void stopTimer(QTimer* timer);
|
|
|
|
QHash<EntityItemID, RegisteredEventHandlers> _registeredHandlers;
|
|
|
|
|
|
/**
|
|
* @brief Script.entityScriptContentAvailable
|
|
*
|
|
* @param entityID
|
|
* @param scriptOrURL
|
|
* @param contents
|
|
* @param isURL
|
|
* @param success
|
|
* @param status
|
|
*/
|
|
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success, const QString& status);
|
|
|
|
EntityItemID currentEntityIdentifier; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution.
|
|
QUrl currentSandboxURL; // The toplevel url string for the entity script that loaded the code being executed, else empty.
|
|
|
|
/**
|
|
* @brief Execute operation in the appropriate context for (the possibly empty) entityID.
|
|
* Even if entityID is supplied as currentEntityIdentifier, this still documents the source
|
|
* of the code being executed (e.g., if we ever sandbox different entity scripts, or provide different
|
|
* global values for different entity scripts).
|
|
*
|
|
* @param entityID Entity ID, may be null
|
|
* @param sandboxURL Sandbox URL
|
|
* @param operation Operation to call
|
|
*/
|
|
void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation);
|
|
|
|
/**
|
|
* @brief Execute operation in the appropriate context for (the possibly empty) entityID.
|
|
*
|
|
* This is equivalent to doWithEnvironment(), only with separate arguments for the function, this object and arguments.
|
|
*
|
|
* This is a convenience function, which performs:
|
|
*
|
|
* @code {.cpp}
|
|
* auto operation = [&]() {
|
|
* function.call(thisObject, args);
|
|
* };
|
|
* doWithEnvironment(entityID, sandboxURL, operation);
|
|
* @endcode
|
|
*
|
|
* @param entityID Entity ID, may be null
|
|
* @param sandboxURL Sandbox URL
|
|
* @param function Function to call
|
|
* @param thisObject "this" object to use for the call
|
|
* @param args Arguments
|
|
*/
|
|
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, const ScriptValue& function, const ScriptValue& thisObject, const ScriptValueList& args);
|
|
|
|
Context _context;
|
|
Type _type;
|
|
ScriptEnginePointer _engine;
|
|
QString _scriptContents;
|
|
QString _parentURL;
|
|
std::atomic<bool> _isFinished { false };
|
|
std::atomic<bool> _isRunning { false };
|
|
std::atomic<bool> _isStopping { false };
|
|
bool _areMetaTypesInitialized { false };
|
|
bool _isInitialized { false };
|
|
QHash<QTimer*, CallbackData> _timerFunctionMap;
|
|
QSet<QUrl> _includedURLs;
|
|
mutable QReadWriteLock _entityScriptsLock { QReadWriteLock::Recursive };
|
|
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
|
EntityScriptContentAvailableMap _contentAvailableQueue;
|
|
ScriptValue _returnValue;
|
|
|
|
bool _isThreaded { false };
|
|
qint64 _lastUpdate;
|
|
|
|
QString _fileNameString;
|
|
Quat _quatLibrary;
|
|
Vec3 _vec3Library;
|
|
Mat4 _mat4Library;
|
|
ScriptUUID _uuidLibrary;
|
|
ConsoleScriptingInterface _consoleScriptingInterface;
|
|
std::atomic<bool> _isUserLoaded { false };
|
|
bool _isReloading { false };
|
|
|
|
std::atomic<bool> _quitWhenFinished;
|
|
|
|
AssetScriptingInterface* _assetScriptingInterface;
|
|
|
|
std::function<bool()> _emitScriptUpdates{ []() { return true; } };
|
|
|
|
std::recursive_mutex _lock;
|
|
|
|
std::chrono::microseconds _totalTimerExecution { 0 };
|
|
|
|
static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS;
|
|
|
|
Setting::Handle<bool> _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true };
|
|
|
|
QWeakPointer<ScriptEngines> _scriptEngines;
|
|
|
|
|
|
|
|
// For debugging performance issues
|
|
int _timerCallCounter{ 0 };
|
|
double _totalTimeInTimerEvents_s{ 0.0 };
|
|
|
|
ScriptManagerScriptingInterfacePointer _scriptingInterface;
|
|
|
|
bool _abortOnUncaughtException{ false };
|
|
|
|
friend ScriptManagerPointer newScriptManager(Context context, const QString& scriptContents, const QString& fileNameString);
|
|
friend class ScriptManagerScriptingInterface;
|
|
|
|
};
|
|
|
|
/**
|
|
* @brief Creates a new ScriptManager
|
|
*
|
|
* @param context Context in which scripts will run
|
|
* @param scriptContents Contents of the script to run
|
|
* @param fileNameString Filename for the script
|
|
* @return ScriptManagerPointer
|
|
*/
|
|
ScriptManagerPointer newScriptManager(ScriptManager::Context context,
|
|
const QString& scriptContents,
|
|
const QString& fileNameString);
|
|
|
|
/**
|
|
* @brief Creates a new ScriptManager and adds it to ScriptEngines
|
|
*
|
|
* Same as newScriptManager, but it additionally registers the new
|
|
* ScriptManager with ScriptEngines.
|
|
*
|
|
* @param context Context in which scripts will run
|
|
* @param scriptContents Contents of the script
|
|
* @param fileNameString Filename of the script
|
|
* @return ScriptManagerPointer
|
|
*/
|
|
ScriptManagerPointer scriptManagerFactory(ScriptManager::Context context,
|
|
const QString& scriptContents,
|
|
const QString& fileNameString);
|
|
|
|
#endif // hifi_ScriptManager_h
|