Overte C++ Documentation
ScriptEngines.h
1 //
2 // Created by Bradley Austin Davis on 2016/01/08
3 // Copyright 2013-2016 High Fidelity, Inc.
4 // Copyright 2023 Overte e.V.
5 //
6 // Distributed under the Apache License, Version 2.0.
7 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8 // SPDX-License-Identifier: Apache-2.0
9 //
10 
13 
14 #ifndef hifi_ScriptEngines_h
15 #define hifi_ScriptEngines_h
16 
17 #include <functional>
18 #include <atomic>
19 #include <memory>
20 
21 #include <QtCore/QObject>
22 #include <QtCore/QMutex>
23 #include <QtCore/QReadWriteLock>
24 #include <QtCore/QUrl>
25 
26 #include <SettingHandle.h>
27 #include <DependencyManager.h>
28 #include <shared/ScriptInitializerMixin.h>
29 #include "ScriptManager.h"
30 
31 #include "ScriptsModel.h"
32 #include "ScriptsModelFilter.h"
33 #include "ScriptGatekeeper.h"
34 
35 class ScriptEngine;
36 
37 /*@jsdoc
38  * The <code>ScriptDiscoveryService</code> API provides facilities to work with Interface scripts.
39  *
40  * @namespace ScriptDiscoveryService
41  *
42  * @hifi-interface
43  * @hifi-avatar
44  * @hifi-client-entity
45  *
46  * @property {string} debugScriptUrl="" - The path and name of a script to debug using the "API Debugger" developer tool
47  * (currentAPI.js). If set, the API Debugger dialog displays the objects and values exposed by the script using
48  * {@link Script.registerValue} and similar.
49  * @property {string} defaultScriptsPath - The path where the default scripts are located in the Interface installation.
50  * <em>Read-only.</em>
51  * @property {ScriptsModel} scriptsModel - Information on the scripts that are in the default scripts directory of the
52  * Interface installation.
53  * <em>Read-only.</em>
54  * @property {ScriptsModelFilter} scriptsModelFilter - Sorted and filtered information on the scripts that are in the default
55  * scripts directory of the Interface installation.
56  * <em>Read-only.</em>
57  */
59 class ScriptEngines : public QObject, public Dependency, public ScriptInitializerMixin<ScriptManagerPointer> {
60  Q_OBJECT
61 
62  Q_PROPERTY(ScriptsModel* scriptsModel READ scriptsModel CONSTANT)
63  Q_PROPERTY(ScriptsModelFilter* scriptsModelFilter READ scriptsModelFilter CONSTANT)
64  Q_PROPERTY(QString debugScriptUrl READ getDebugScriptUrl WRITE setDebugScriptUrl)
65 
66 public:
67  ScriptEngines(ScriptManager::Context context, const QUrl& defaultScriptsOverride = QUrl());
68  int runScriptInitializers(ScriptManagerPointer manager) override;
69 
70  void loadScripts();
71  void saveScripts();
72 
73  QString getDebugScriptUrl() { return _debugScriptUrl; };
74  void setDebugScriptUrl(const QString& url) { _debugScriptUrl = url; };
75 
76  void loadDefaultScripts();
77  void reloadLocalFiles();
78 
79  QStringList getRunningScripts();
80  ScriptManagerPointer getScriptEngine(const QUrl& scriptHash);
81 
82  ScriptsModel* scriptsModel() { return &_scriptsModel; };
83  ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; };
84 
85  QString getDefaultScriptsLocation() const;
86 
87  /*@jsdoc
88  * Starts running an Interface script, if it isn't already running. The script is automatically loaded next time Interface
89  * starts.
90  * <p>This is a synonym for calling {@link ScriptDiscoveryService.loadScript|loadScript} with just the script URL.</p>
91  * <p class="availableIn"><strong>Supported Script Types:</strong> Interface Scripts &bull; Avatar Scripts</p>
92  * <p>See also, {@link Script.load}.</p>
93  * @function ScriptDiscoveryService.loadOneScript
94  * @param {string} url - The path and name of the script. If a local file, including the <code>"file:///"</code> scheme is
95  * optional.
96  */
97  Q_INVOKABLE void loadOneScript(const QString& scriptFilename);
98 
99  /*@jsdoc
100  * Starts running an Interface script, if it isn't already running.
101  * <p class="availableIn"><strong>Supported Script Types:</strong> Interface Scripts &bull; Avatar Scripts</p>
102  * <p>See also, {@link Script.load}.</p>
103  * @function ScriptDiscoveryService.loadScript
104  * @param {string} [url=""] - The path and name of the script. If a local file, including the <code>"file:///"</code>
105  * scheme is optional.
106  * @param {boolean} [isUserLoaded=true] - <code>true</code> if the user specifically loaded it, <code>false</code> if not
107  * (e.g., a script loaded it). If <code>false</code>, the script is not automatically loaded next time Interface starts.
108  * @param {boolean} [loadScriptFromEditor=false] - <em>Not used.</em>
109  * @param {boolean} [activateMainWindow=false] - <em>Not used.</em>
110  * @param {boolean} [reload=false] - <code>true</code> to redownload the script, <code>false</code> to use the copy from
111  * the cache if available.
112  * @param {boolean} [quitWhenFinished=false] - <code>true</code> to close Interface when the script finishes,
113  * <code>false</code> to not close Interface.
114  * @returns {object} An empty object, <code>{}</code>.
115  */
116  Q_INVOKABLE ScriptManagerPointer loadScript(const QUrl& scriptFilename = QString(),
117  bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false, bool quitWhenFinished = false);
118 
119  /*@jsdoc
120  * Stops or restarts an Interface script.
121  * @function ScriptDiscoveryService.stopScript
122  * @param {string} url - The path and name of the script. If a local file, including the <code>"file:///"</code> scheme is
123  * optional.
124  * @param {boolean} [restart=false] - <code>true</code> to redownload and restart the script, <code>false</code> to stop
125  * it.
126  * @returns {boolean} <code>true</code> if the script was successfully stopped or restarted, <code>false</code> if it
127  * wasn't (e.g., the script couldn't be found).
128  */
129  Q_INVOKABLE bool stopScript(const QString& scriptHash, bool restart = false);
130 
131 
132  /*@jsdoc
133  * Restarts all Interface, avatar, and client entity scripts after clearing the scripts cache.
134  * @function ScriptDiscoveryService.reloadAllScripts
135  */
136  Q_INVOKABLE void reloadAllScripts();
137 
138  /*@jsdoc
139  * Stops or restarts all Interface scripts. The scripts cache is not cleared. If restarting, avatar and client entity
140  * scripts are also restarted.
141  * @function ScriptDiscoveryService.stopAllScripts
142  * @param {boolean} [restart=false] - <code>true</code> to restart the scripts, <code>false</code> to stop them.
143  */
144  Q_INVOKABLE void stopAllScripts(bool restart = false);
145 
146 
147  /*@jsdoc
148  * Gets a list of all Interface scripts that are currently running.
149  * @function ScriptDiscoveryService.getRunning
150  * @returns {ScriptDiscoveryService.RunningScript[]} All Interface scripts that are currently running.
151  * @example <caption>Report all running scripts.</caption>
152  * var runningScripts = ScriptDiscoveryService.getRunning();
153  * print("Running scripts:");
154  * for (var i = 0; i < runningScripts.length; i++) {
155  * print(JSON.stringify(runningScripts[i]));
156  * }
157  */
158  Q_INVOKABLE QVariantList getRunning();
159 
160  /*@jsdoc
161  * Gets a list of all script files that are in the default scripts directory of the Interface installation.
162  * @function ScriptDiscoveryService.getPublic
163  * @returns {ScriptDiscoveryService.PublicScript[]} All scripts in the "scripts" directory of the Interface
164  * installation.
165  */
166  Q_INVOKABLE QVariantList getPublic();
167 
168  /*@jsdoc
169  * @function ScriptDiscoveryService.getLocal
170  * @returns {ScriptDiscoveryService.LocalScript[]} Local scripts.
171  * @deprecated This function is deprecated and will be removed.
172  */
173  // Deprecated because there is no longer a notion of a "local" scripts folder where you would put your personal scripts.
174  Q_INVOKABLE QVariantList getLocal();
175 
176  // FIXME: Move to other Q_PROPERTY declarations.
177  Q_PROPERTY(QString defaultScriptsPath READ getDefaultScriptsLocation)
178 
179  void defaultScriptsLocationOverridden(bool overridden) { _defaultScriptsLocationOverridden = overridden; };
180 
181  // Called at shutdown time
182  void shutdownScripting();
183  bool isStopped() const { return _isStopped; }
184 
185  void addScriptEngine(ScriptManagerPointer);
186 
187  void removeScriptEngine(ScriptManagerPointer);
188 
189  // Called by ScriptManagerScriptingInterface
190  void requestServerEntityScriptMessages(ScriptManager *manager);
191  void requestServerEntityScriptMessages(ScriptManager *manager, const QUuid& entityID);
192 
193  void removeServerEntityScriptMessagesRequest(ScriptManager *manager);
194  void removeServerEntityScriptMessagesRequest(ScriptManager *manager, const QUuid& entityID);
195 
196  ScriptGatekeeper scriptGatekeeper;
197 
198 signals:
199 
200  /*@jsdoc
201  * Triggered when the number of Interface scripts running changes.
202  * @function ScriptDiscoveryService.scriptCountChanged
203  * @returns {Signal}
204  * @example <caption>Report when the number of running scripts changes.</caption>
205  * ScriptDiscoveryService.scriptCountChanged.connect(function () {
206  * print("Scripts count changed: " + ScriptDiscoveryService.getRunning().length);
207  * });
208  */
209  void scriptCountChanged();
210 
211  /*@jsdoc
212  * Triggered when Interface, avatar, and client entity scripts are restarting as a result of
213  * {@link ScriptDiscoveryService.reloadAllScripts|reloadAllScripts} or
214  * {@link ScriptDiscoveryService.stopAllScripts|stopAllScripts}.
215  * @function ScriptDiscoveryService.scriptsReloading
216  * @returns {Signal}
217  */
218  void scriptsReloading();
219 
220  /*@jsdoc
221  * Triggered when a script could not be loaded.
222  * @function ScriptDiscoveryService.scriptLoadError
223  * @param {string} url - The path and name of the script that could not be loaded.
224  * @param {string} error - <code>""</code> always.
225  * @returns {Signal}
226  */
227  void scriptLoadError(const QString& filename, const QString& error);
228 
229  /*@jsdoc
230  * Triggered when any script prints a message to the program log via {@link print}, {@link Script.print},
231  * {@link console.log}, or {@link console.debug}.
232  * @function ScriptDiscoveryService.printedMessage
233  * @param {string} message - The message.
234  * @param {string} scriptName - The name of the script that generated the message.
235  * @returns {Signal}
236  */
237  void printedMessage(const QString& message, const QString& engineName);
238 
239  /*@jsdoc
240  * Triggered when any script generates an error or {@link console.error} is called.
241  * @function ScriptDiscoveryService.errorMessage
242  * @param {string} message - The error message.
243  * @param {string} scriptName - The name of the script that generated the error message.
244  * @returns {Signal}
245  */
246  void errorMessage(const QString& message, const QString& engineName);
247 
248  /*@jsdoc
249  * Triggered when any script generates a warning or {@link console.warn} is called.
250  * @function ScriptDiscoveryService.warningMessage
251  * @param {string} message - The warning message.
252  * @param {string} scriptName - The name of the script that generated the warning message.
253  * @returns {Signal}
254  */
255  void warningMessage(const QString& message, const QString& engineName);
256 
257  /*@jsdoc
258  * Triggered when any script generates an information message or {@link console.info} is called.
259  * @function ScriptDiscoveryService.infoMessage
260  * @param {string} message - The information message.
261  * @param {string} scriptName - The name of the script that generated the information message.
262  * @returns {Signal}
263  */
264  void infoMessage(const QString& message, const QString& engineName);
265 
266  /*@jsdoc
267  * Triggered when a client side entity script prints a message to the program log via {@link print}, {@link Script.print},
268  * {@link console.log}, {@link console.debug}, {@link console.group}, {@link console.groupEnd}, {@link console.time}, or
269  * {@link console.timeEnd}.
270  * @function Script.printedMessage
271  * @param {string} message - The message.
272  * @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
273  * @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
274  * @param {Uuid} entityID - Entity ID.
275  * @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side.
276  * @returns {Signal}
277  */
278  void printedEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript);
279 
280  /*@jsdoc
281  * Triggered when a client side entity script generates an error, {@link console.error} or {@link console.exception} is called, or
282  * {@link console.assert} is called and fails.
283  * @function Script.errorMessage
284  * @param {string} message - The error message.
285  * @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
286  * @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
287  * @param {Uuid} entityID - Entity ID.
288  * @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side.
289  * @returns {Signal}
290  */
291  void errorEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript);
292 
293  /*@jsdoc
294  * Triggered when a client side entity script generates a warning or {@link console.warn} is called.
295  * @function Script.warningMessage
296  * @param {string} message - The warning message.
297  * @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
298  * @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
299  * @param {Uuid} entityID - Entity ID.
300  * @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side.
301  * @returns {Signal}
302  */
303  void warningEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript);
304 
305  /*@jsdoc
306  * Triggered when a client side entity script generates an information message or {@link console.info} is called.
307  * @function Script.infoMessage
308  * @param {string} message - The information message.
309  * @param {string} fileName - Name of the file in which message was generated. Empty string when no file name is available.
310  * @param {number} lineNumber - Number of the line on which message was generated. -1 if there line number is not available.
311  * @param {Uuid} entityID - Entity ID.
312  * @param {boolean} isServerScript - true if entity script is server-side, false if it is client-side.
313  * @returns {Signal}
314  */
315  void infoEntityMessage(const QString& message, const QString& fileName, int lineNumber, const EntityItemID& entityID, bool isServerScript);
316 
317  /*@jsdoc
318  * @function ScriptDiscoveryService.errorLoadingScript
319  * @param {string} url - URL.
320  * @returns {Signal}
321  * @deprecated This signal is deprecated and will be removed.
322  */
323  // Deprecated because never emitted.
324  void errorLoadingScript(const QString& url);
325 
326  /*@jsdoc
327  * Triggered when the Debug Window is cleared.
328  * @function ScriptDiscoveryService.clearDebugWindow
329  * @returns {Signal}
330  */
331  void clearDebugWindow();
332 
337  void requestingEntityScriptServerLog(bool areMessagesRequested);
338 
339 public slots:
340 
341  /*@jsdoc
342  * @function ScriptDiscoveryService.onPrintedMessage
343  * @param {string} message - Message.
344  * @param {string} scriptName - Script name.
345  * @deprecated This function is deprecated and will be removed.
346  */
347  // Deprecated because only use is to emit a signal.
348  void onPrintedMessage(const QString& message, const QString& scriptName);
349 
350  /*@jsdoc
351  * @function ScriptDiscoveryService.onErrorMessage
352  * @param {string} message - Message.
353  * @param {string} scriptName - Script name.
354  * @deprecated This function is deprecated and will be removed.
355  */
356  // Deprecated because only use is to emit a signal.
357  void onErrorMessage(const QString& message, const QString& scriptName);
358 
359  /*@jsdoc
360  * @function ScriptDiscoveryService.onWarningMessage
361  * @param {string} message - Message.
362  * @param {string} scriptName - Script name.
363  * @deprecated This function is deprecated and will be removed.
364  */
365  // Deprecated because only use is to emit a signal.
366  void onWarningMessage(const QString& message, const QString& scriptName);
367 
368  /*@jsdoc
369  * @function ScriptDiscoveryService.onInfoMessage
370  * @param {string} message - Message.
371  * @param {string} scriptName - Script name.
372  * @deprecated This function is deprecated and will be removed.
373  */
374  // Deprecated because only use is to emit a signal.
375  void onInfoMessage(const QString& message, const QString& scriptName);
376 
377  /*@jsdoc
378  * @function ScriptDiscoveryService.onErrorLoadingScript
379  * @param {string} url - URL.
380  * @deprecated This function is deprecated and will be removed.
381  */
382  // Deprecated because only use is to emit a signal. And it isn't used.
383  void onErrorLoadingScript(const QString& url);
384 
385  /*@jsdoc
386  * @function ScriptDiscoveryService.onClearDebugWindow
387  * @deprecated This function is deprecated and will be removed.
388  */
389  // Deprecated because only use is to emit a signal.
390  void onClearDebugWindow();
391 
392 protected slots:
393 
394  /*@jsdoc
395  * @function ScriptDiscoveryService.onScriptFinished
396  * @param {string} scriptName - Script name.
397  * @param {object} manager - Script manager.
398  * @deprecated This function is deprecated and will be removed.
399  */
400  // Deprecated because it wasn't intended to be in the API.
401  void onScriptFinished(const QString& fileNameString, ScriptManagerPointer manager);
402 
403 protected:
404  ScriptManagerPointer reloadScript(const QString& scriptName, bool isUserLoaded = true) { return loadScript(scriptName, isUserLoaded, false, false, true); }
405  void onScriptEngineLoaded(const QString& scriptFilename);
406  void quitWhenFinished();
407  void onScriptEngineError(const QString& scriptFilename);
408  void launchScriptEngine(ScriptManagerPointer);
409 
410  ScriptManager::Context _context;
411  QReadWriteLock _scriptManagersHashLock;
412  QMultiHash<QUrl, ScriptManagerPointer> _scriptManagersHash;
413  QSet<ScriptManagerPointer> _allKnownScriptManagers;
414  QMutex _allScriptsMutex;
415  ScriptsModel _scriptsModel;
416  ScriptsModelFilter _scriptsModelFilter;
417  std::atomic<bool> _isStopped { false };
418  std::atomic<bool> _isReloading { false };
419  bool _defaultScriptsLocationOverridden { false };
420  QString _debugScriptUrl;
421 
422  // For subscriptions to server entity script messages
423  std::mutex _subscriptionsToEntityScriptMessagesMutex;
424  QSet<ScriptManager*> _managersSubscribedToEntityScriptMessages;
425  // Since multiple entity scripts run in the same script engine, there's a need to track subscriptions per entity
426  QHash<ScriptManager*, QSet<QUuid>> _entitiesSubscribedToEntityScriptMessages;
427 
428  // If this is set, defaultScripts.js will not be run if it is in the settings,
429  // and this will be run instead. This script will not be persisted to settings.
430  const QUrl _defaultScriptsOverride { };
431  // If an override is set, this will be true if defaultScripts.js was previously running.
432  bool _defaultScriptsWasRunning { false };
433 };
434 
435 QUrl normalizeScriptURL(const QUrl& rawScriptURL);
436 QString expandScriptPath(const QString& rawPath);
437 QUrl expandScriptUrl(const QUrl& rawScriptURL);
438 
439 #endif // hifi_ScriptEngine_h
440 
Abstract ID for editing model items. Used in EntityItem JS API.
Definition: EntityItemID.h:28
Provides an engine-independent interface for a scripting engine.
Definition: ScriptEngine.h:93
Provides the ScriptDiscoveryService scripting interface.
Definition: ScriptEngines.h:59
void requestingEntityScriptServerLog(bool areMessagesRequested)
Fires when script engines need entity server script messages (areMessagesRequested == true) and when ...
Manages script whitelisting in a domain.
Definition: ScriptGatekeeper.h:21
Manages a single scripting engine.
Definition: ScriptManager.h:281
Context
Context of the script.
Definition: ScriptManager.h:294
Provides script file information available from the ScriptDiscoveryService scripting interface.
Definition: ScriptsModelFilter.h:78
Provides script file information available from the ScriptDiscoveryService scripting interface.
Definition: ScriptsModel.h:128