mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
441 lines
20 KiB
C++
441 lines
20 KiB
C++
//
|
|
// Created by Bradley Austin Davis on 2016/01/08
|
|
// Copyright 2013-2016 High Fidelity, Inc.
|
|
// Copyright 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
|
|
//
|
|
|
|
/// @addtogroup ScriptEngine
|
|
/// @{
|
|
|
|
#ifndef hifi_ScriptEngines_h
|
|
#define hifi_ScriptEngines_h
|
|
|
|
#include <functional>
|
|
#include <atomic>
|
|
#include <memory>
|
|
|
|
#include <QtCore/QObject>
|
|
#include <QtCore/QMutex>
|
|
#include <QtCore/QReadWriteLock>
|
|
#include <QtCore/QUrl>
|
|
|
|
#include <SettingHandle.h>
|
|
#include <DependencyManager.h>
|
|
#include <shared/ScriptInitializerMixin.h>
|
|
#include "ScriptManager.h"
|
|
|
|
#include "ScriptsModel.h"
|
|
#include "ScriptsModelFilter.h"
|
|
#include "ScriptGatekeeper.h"
|
|
|
|
class ScriptEngine;
|
|
|
|
/*@jsdoc
|
|
* The <code>ScriptDiscoveryService</code> API provides facilities to work with Interface scripts.
|
|
*
|
|
* @namespace ScriptDiscoveryService
|
|
*
|
|
* @hifi-interface
|
|
* @hifi-avatar
|
|
* @hifi-client-entity
|
|
*
|
|
* @property {string} debugScriptUrl="" - The path and name of a script to debug using the "API Debugger" developer tool
|
|
* (currentAPI.js). If set, the API Debugger dialog displays the objects and values exposed by the script using
|
|
* {@link Script.registerValue} and similar.
|
|
* @property {string} defaultScriptsPath - The path where the default scripts are located in the Interface installation.
|
|
* <em>Read-only.</em>
|
|
* @property {ScriptsModel} scriptsModel - Information on the scripts that are in the default scripts directory of the
|
|
* Interface installation.
|
|
* <em>Read-only.</em>
|
|
* @property {ScriptsModelFilter} scriptsModelFilter - Sorted and filtered information on the scripts that are in the default
|
|
* scripts directory of the Interface installation.
|
|
* <em>Read-only.</em>
|
|
*/
|
|
/// Provides the <code><a href="https://apidocs.overte.org/ScriptDiscoveryService.html">ScriptDiscoveryService</a></code> scripting interface
|
|
class ScriptEngines : public QObject, public Dependency, public ScriptInitializerMixin<ScriptManagerPointer> {
|
|
Q_OBJECT
|
|
|
|
Q_PROPERTY(ScriptsModel* scriptsModel READ scriptsModel CONSTANT)
|
|
Q_PROPERTY(ScriptsModelFilter* scriptsModelFilter READ scriptsModelFilter CONSTANT)
|
|
Q_PROPERTY(QString debugScriptUrl READ getDebugScriptUrl WRITE setDebugScriptUrl)
|
|
|
|
public:
|
|
ScriptEngines(ScriptManager::Context context, const QUrl& defaultScriptsOverride = QUrl());
|
|
int runScriptInitializers(ScriptManagerPointer manager) override;
|
|
|
|
void loadScripts();
|
|
void saveScripts();
|
|
|
|
QString getDebugScriptUrl() { return _debugScriptUrl; };
|
|
void setDebugScriptUrl(const QString& url) { _debugScriptUrl = url; };
|
|
|
|
void loadDefaultScripts();
|
|
void reloadLocalFiles();
|
|
|
|
QStringList getRunningScripts();
|
|
ScriptManagerPointer getScriptEngine(const QUrl& scriptHash);
|
|
|
|
ScriptsModel* scriptsModel() { return &_scriptsModel; };
|
|
ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; };
|
|
|
|
QString getDefaultScriptsLocation() const;
|
|
|
|
/*@jsdoc
|
|
* Starts running an Interface script, if it isn't already running. The script is automatically loaded next time Interface
|
|
* starts.
|
|
* <p>This is a synonym for calling {@link ScriptDiscoveryService.loadScript|loadScript} with just the script URL.</p>
|
|
* <p class="availableIn"><strong>Supported Script Types:</strong> Interface Scripts • Avatar Scripts</p>
|
|
* <p>See also, {@link Script.load}.</p>
|
|
* @function ScriptDiscoveryService.loadOneScript
|
|
* @param {string} url - The path and name of the script. If a local file, including the <code>"file:///"</code> scheme is
|
|
* optional.
|
|
*/
|
|
Q_INVOKABLE void loadOneScript(const QString& scriptFilename);
|
|
|
|
/*@jsdoc
|
|
* Starts running an Interface script, if it isn't already running.
|
|
* <p class="availableIn"><strong>Supported Script Types:</strong> Interface Scripts • Avatar Scripts</p>
|
|
* <p>See also, {@link Script.load}.</p>
|
|
* @function ScriptDiscoveryService.loadScript
|
|
* @param {string} [url=""] - The path and name of the script. If a local file, including the <code>"file:///"</code>
|
|
* scheme is optional.
|
|
* @param {boolean} [isUserLoaded=true] - <code>true</code> if the user specifically loaded it, <code>false</code> if not
|
|
* (e.g., a script loaded it). If <code>false</code>, the script is not automatically loaded next time Interface starts.
|
|
* @param {boolean} [loadScriptFromEditor=false] - <em>Not used.</em>
|
|
* @param {boolean} [activateMainWindow=false] - <em>Not used.</em>
|
|
* @param {boolean} [reload=false] - <code>true</code> to redownload the script, <code>false</code> to use the copy from
|
|
* the cache if available.
|
|
* @param {boolean} [quitWhenFinished=false] - <code>true</code> to close Interface when the script finishes,
|
|
* <code>false</code> to not close Interface.
|
|
* @returns {object} An empty object, <code>{}</code>.
|
|
*/
|
|
Q_INVOKABLE ScriptManagerPointer loadScript(const QUrl& scriptFilename = QString(),
|
|
bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false, bool quitWhenFinished = false);
|
|
|
|
/*@jsdoc
|
|
* Stops or restarts an Interface script.
|
|
* @function ScriptDiscoveryService.stopScript
|
|
* @param {string} url - The path and name of the script. If a local file, including the <code>"file:///"</code> scheme is
|
|
* optional.
|
|
* @param {boolean} [restart=false] - <code>true</code> to redownload and restart the script, <code>false</code> to stop
|
|
* it.
|
|
* @returns {boolean} <code>true</code> if the script was successfully stopped or restarted, <code>false</code> if it
|
|
* wasn't (e.g., the script couldn't be found).
|
|
*/
|
|
Q_INVOKABLE bool stopScript(const QString& scriptHash, bool restart = false);
|
|
|
|
|
|
/*@jsdoc
|
|
* Restarts all Interface, avatar, and client entity scripts after clearing the scripts cache.
|
|
* @function ScriptDiscoveryService.reloadAllScripts
|
|
*/
|
|
Q_INVOKABLE void reloadAllScripts();
|
|
|
|
/*@jsdoc
|
|
* Stops or restarts all Interface scripts. The scripts cache is not cleared. If restarting, avatar and client entity
|
|
* scripts are also restarted.
|
|
* @function ScriptDiscoveryService.stopAllScripts
|
|
* @param {boolean} [restart=false] - <code>true</code> to restart the scripts, <code>false</code> to stop them.
|
|
*/
|
|
Q_INVOKABLE void stopAllScripts(bool restart = false);
|
|
|
|
|
|
/*@jsdoc
|
|
* Gets a list of all Interface scripts that are currently running.
|
|
* @function ScriptDiscoveryService.getRunning
|
|
* @returns {ScriptDiscoveryService.RunningScript[]} All Interface scripts that are currently running.
|
|
* @example <caption>Report all running scripts.</caption>
|
|
* var runningScripts = ScriptDiscoveryService.getRunning();
|
|
* print("Running scripts:");
|
|
* for (var i = 0; i < runningScripts.length; i++) {
|
|
* print(JSON.stringify(runningScripts[i]));
|
|
* }
|
|
*/
|
|
Q_INVOKABLE QVariantList getRunning();
|
|
|
|
/*@jsdoc
|
|
* Gets a list of all script files that are in the default scripts directory of the Interface installation.
|
|
* @function ScriptDiscoveryService.getPublic
|
|
* @returns {ScriptDiscoveryService.PublicScript[]} All scripts in the "scripts" directory of the Interface
|
|
* installation.
|
|
*/
|
|
Q_INVOKABLE QVariantList getPublic();
|
|
|
|
/*@jsdoc
|
|
* @function ScriptDiscoveryService.getLocal
|
|
* @returns {ScriptDiscoveryService.LocalScript[]} Local scripts.
|
|
* @deprecated This function is deprecated and will be removed.
|
|
*/
|
|
// Deprecated because there is no longer a notion of a "local" scripts folder where you would put your personal scripts.
|
|
Q_INVOKABLE QVariantList getLocal();
|
|
|
|
// FIXME: Move to other Q_PROPERTY declarations.
|
|
Q_PROPERTY(QString defaultScriptsPath READ getDefaultScriptsLocation)
|
|
|
|
void defaultScriptsLocationOverridden(bool overridden) { _defaultScriptsLocationOverridden = overridden; };
|
|
|
|
// Called at shutdown time
|
|
void shutdownScripting();
|
|
bool isStopped() const { return _isStopped; }
|
|
|
|
void addScriptEngine(ScriptManagerPointer);
|
|
|
|
void removeScriptEngine(ScriptManagerPointer);
|
|
|
|
// Called by ScriptManagerScriptingInterface
|
|
void requestServerEntityScriptMessages(ScriptManager *manager);
|
|
void requestServerEntityScriptMessages(ScriptManager *manager, const QUuid& entityID);
|
|
|
|
void removeServerEntityScriptMessagesRequest(ScriptManager *manager);
|
|
void removeServerEntityScriptMessagesRequest(ScriptManager *manager, const QUuid& entityID);
|
|
|
|
ScriptGatekeeper scriptGatekeeper;
|
|
|
|
signals:
|
|
|
|
/*@jsdoc
|
|
* Triggered when the number of Interface scripts running changes.
|
|
* @function ScriptDiscoveryService.scriptCountChanged
|
|
* @returns {Signal}
|
|
* @example <caption>Report when the number of running scripts changes.</caption>
|
|
* ScriptDiscoveryService.scriptCountChanged.connect(function () {
|
|
* print("Scripts count changed: " + ScriptDiscoveryService.getRunning().length);
|
|
* });
|
|
*/
|
|
void scriptCountChanged();
|
|
|
|
/*@jsdoc
|
|
* Triggered when Interface, avatar, and client entity scripts are restarting as a result of
|
|
* {@link ScriptDiscoveryService.reloadAllScripts|reloadAllScripts} or
|
|
* {@link ScriptDiscoveryService.stopAllScripts|stopAllScripts}.
|
|
* @function ScriptDiscoveryService.scriptsReloading
|
|
* @returns {Signal}
|
|
*/
|
|
void scriptsReloading();
|
|
|
|
/*@jsdoc
|
|
* Triggered when a script could not be loaded.
|
|
* @function ScriptDiscoveryService.scriptLoadError
|
|
* @param {string} url - The path and name of the script that could not be loaded.
|
|
* @param {string} error - <code>""</code> always.
|
|
* @returns {Signal}
|
|
*/
|
|
void scriptLoadError(const QString& filename, const QString& error);
|
|
|
|
/*@jsdoc
|
|
* Triggered when any script prints a message to the program log via {@link print}, {@link Script.print},
|
|
* {@link console.log}, or {@link console.debug}.
|
|
* @function ScriptDiscoveryService.printedMessage
|
|
* @param {string} message - The message.
|
|
* @param {string} scriptName - The name of the script that generated the message.
|
|
* @returns {Signal}
|
|
*/
|
|
void printedMessage(const QString& message, const QString& engineName);
|
|
|
|
/*@jsdoc
|
|
* Triggered when any script generates an error or {@link console.error} is called.
|
|
* @function ScriptDiscoveryService.errorMessage
|
|
* @param {string} message - The error message.
|
|
* @param {string} scriptName - The name of the script that generated the error message.
|
|
* @returns {Signal}
|
|
*/
|
|
void errorMessage(const QString& message, const QString& engineName);
|
|
|
|
/*@jsdoc
|
|
* Triggered when any script generates a warning or {@link console.warn} is called.
|
|
* @function ScriptDiscoveryService.warningMessage
|
|
* @param {string} message - The warning message.
|
|
* @param {string} scriptName - The name of the script that generated the warning message.
|
|
* @returns {Signal}
|
|
*/
|
|
void warningMessage(const QString& message, const QString& engineName);
|
|
|
|
/*@jsdoc
|
|
* Triggered when any script generates an information message or {@link console.info} is called.
|
|
* @function ScriptDiscoveryService.infoMessage
|
|
* @param {string} message - The information message.
|
|
* @param {string} scriptName - The name of the script that generated the information message.
|
|
* @returns {Signal}
|
|
*/
|
|
void infoMessage(const QString& message, const QString& engineName);
|
|
|
|
/*@jsdoc
|
|
* Triggered when a client side entity script prints a message to the program log via {@link print}, {@link Script.print},
|
|
* {@link console.log}, {@link console.debug}, {@link console.group}, {@link console.groupEnd}, {@link console.time}, or
|
|
* {@link console.timeEnd}.
|
|
* @function Script.printedEntityMessage
|
|
* @param {string} message - The message.
|
|
* @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
|
|
* @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
|
|
* @param {Uuid} entityID - Entity ID.
|
|
* @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side.
|
|
* @returns {Signal}
|
|
*/
|
|
void printedEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript);
|
|
|
|
/*@jsdoc
|
|
* Triggered when a client side entity script generates an error, {@link console.error} or {@link console.exception} is called, or
|
|
* {@link console.assert} is called and fails.
|
|
* @function Script.errorEntityMessage
|
|
* @param {string} message - The error message.
|
|
* @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
|
|
* @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
|
|
* @param {Uuid} entityID - Entity ID.
|
|
* @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side.
|
|
* @returns {Signal}
|
|
*/
|
|
void errorEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript);
|
|
|
|
/*@jsdoc
|
|
* Triggered when a client side entity script generates a warning or {@link console.warn} is called.
|
|
* @function Script.warningEntityMessage
|
|
* @param {string} message - The warning message.
|
|
* @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
|
|
* @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
|
|
* @param {Uuid} entityID - Entity ID.
|
|
* @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side.
|
|
* @returns {Signal}
|
|
*/
|
|
void warningEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript);
|
|
|
|
/*@jsdoc
|
|
* Triggered when a client side entity script generates an information message or {@link console.info} is called.
|
|
* @function Script.infoEntityMessage
|
|
* @param {string} message - The information message.
|
|
* @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
|
|
* @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
|
|
* @param {Uuid} entityID - Entity ID.
|
|
* @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side.
|
|
* @returns {Signal}
|
|
*/
|
|
void infoEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript);
|
|
|
|
/*@jsdoc
|
|
* @function ScriptDiscoveryService.errorLoadingScript
|
|
* @param {string} url - URL.
|
|
* @returns {Signal}
|
|
* @deprecated This signal is deprecated and will be removed.
|
|
*/
|
|
// Deprecated because never emitted.
|
|
void errorLoadingScript(const QString& url);
|
|
|
|
/*@jsdoc
|
|
* Triggered when the Debug Window is cleared.
|
|
* @function ScriptDiscoveryService.clearDebugWindow
|
|
* @returns {Signal}
|
|
*/
|
|
void clearDebugWindow();
|
|
|
|
/**
|
|
* @brief Fires when script engines need entity server script messages (areMessagesRequested == true)
|
|
* and when messages are not needed anymore (areMessagesRequested == false).
|
|
*/
|
|
void requestingEntityScriptServerLog(bool areMessagesRequested);
|
|
|
|
public slots:
|
|
|
|
/*@jsdoc
|
|
* @function ScriptDiscoveryService.onPrintedMessage
|
|
* @param {string} message - Message.
|
|
* @param {string} scriptName - Script name.
|
|
* @deprecated This function is deprecated and will be removed.
|
|
*/
|
|
// Deprecated because only use is to emit a signal.
|
|
void onPrintedMessage(const QString& message, const QString& scriptName);
|
|
|
|
/*@jsdoc
|
|
* @function ScriptDiscoveryService.onErrorMessage
|
|
* @param {string} message - Message.
|
|
* @param {string} scriptName - Script name.
|
|
* @deprecated This function is deprecated and will be removed.
|
|
*/
|
|
// Deprecated because only use is to emit a signal.
|
|
void onErrorMessage(const QString& message, const QString& scriptName);
|
|
|
|
/*@jsdoc
|
|
* @function ScriptDiscoveryService.onWarningMessage
|
|
* @param {string} message - Message.
|
|
* @param {string} scriptName - Script name.
|
|
* @deprecated This function is deprecated and will be removed.
|
|
*/
|
|
// Deprecated because only use is to emit a signal.
|
|
void onWarningMessage(const QString& message, const QString& scriptName);
|
|
|
|
/*@jsdoc
|
|
* @function ScriptDiscoveryService.onInfoMessage
|
|
* @param {string} message - Message.
|
|
* @param {string} scriptName - Script name.
|
|
* @deprecated This function is deprecated and will be removed.
|
|
*/
|
|
// Deprecated because only use is to emit a signal.
|
|
void onInfoMessage(const QString& message, const QString& scriptName);
|
|
|
|
/*@jsdoc
|
|
* @function ScriptDiscoveryService.onErrorLoadingScript
|
|
* @param {string} url - URL.
|
|
* @deprecated This function is deprecated and will be removed.
|
|
*/
|
|
// Deprecated because only use is to emit a signal. And it isn't used.
|
|
void onErrorLoadingScript(const QString& url);
|
|
|
|
/*@jsdoc
|
|
* @function ScriptDiscoveryService.onClearDebugWindow
|
|
* @deprecated This function is deprecated and will be removed.
|
|
*/
|
|
// Deprecated because only use is to emit a signal.
|
|
void onClearDebugWindow();
|
|
|
|
protected slots:
|
|
|
|
/*@jsdoc
|
|
* @function ScriptDiscoveryService.onScriptFinished
|
|
* @param {string} scriptName - Script name.
|
|
* @param {object} manager - Script manager.
|
|
* @deprecated This function is deprecated and will be removed.
|
|
*/
|
|
// Deprecated because it wasn't intended to be in the API.
|
|
void onScriptFinished(const QString& fileNameString, ScriptManagerPointer manager);
|
|
|
|
protected:
|
|
ScriptManagerPointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); }
|
|
void onScriptEngineLoaded(const QString& scriptFilename);
|
|
void quitWhenFinished();
|
|
void onScriptEngineError(const QString& scriptFilename);
|
|
void launchScriptEngine(ScriptManagerPointer);
|
|
|
|
ScriptManager::Context _context;
|
|
QReadWriteLock _scriptManagersHashLock;
|
|
QMultiHash<QUrl, ScriptManagerPointer> _scriptManagersHash;
|
|
QSet<ScriptManagerPointer> _allKnownScriptManagers;
|
|
QMutex _allScriptsMutex;
|
|
ScriptsModel _scriptsModel;
|
|
ScriptsModelFilter _scriptsModelFilter;
|
|
std::atomic<bool> _isStopped { false };
|
|
std::atomic<bool> _isReloading { false };
|
|
bool _defaultScriptsLocationOverridden { false };
|
|
QString _debugScriptUrl;
|
|
|
|
// For subscriptions to server entity script messages
|
|
std::mutex _subscriptionsToEntityScriptMessagesMutex;
|
|
QSet<ScriptManager*> _managersSubscribedToEntityScriptMessages;
|
|
// Since multiple entity scripts run in the same script engine, there's a need to track subscriptions per entity
|
|
QHash<ScriptManager*, QSet<QUuid>> _entitiesSubscribedToEntityScriptMessages;
|
|
|
|
// If this is set, defaultScripts.js will not be run if it is in the settings,
|
|
// and this will be run instead. This script will not be persisted to settings.
|
|
const QUrl _defaultScriptsOverride { };
|
|
// If an override is set, this will be true if defaultScripts.js was previously running.
|
|
bool _defaultScriptsWasRunning { false };
|
|
};
|
|
|
|
QUrl normalizeScriptURL(const QUrl& rawScriptURL);
|
|
QString expandScriptPath(const QString& rawPath);
|
|
QUrl expandScriptUrl(const QUrl& rawScriptURL);
|
|
|
|
#endif // hifi_ScriptEngine_h
|
|
|
|
/// @}
|