Overte C++ Documentation
AddressManager.h
1 //
2 // AddressManager.h
3 // libraries/networking/src
4 //
5 // Created by Stephen Birarda on 2014-09-10.
6 // Copyright 2014 High Fidelity, Inc.
7 // Copyright 2021 Vircadia contributors.
8 // Copyright 2023 Overte e.V.
9 //
10 // Distributed under the Apache License, Version 2.0.
11 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
12 // SPDX-License-Identifier: Apache-2.0
13 //
14 
15 #ifndef hifi_AddressManager_h
16 #define hifi_AddressManager_h
17 
18 #include <QtCore/QObject>
19 #include <QtCore/QStack>
20 
21 #include <glm/glm.hpp>
22 #include <glm/gtc/quaternion.hpp>
23 
24 #include <DependencyManager.h>
25 
26 #include "AccountManager.h"
27 
28 extern const QString REDIRECT_HIFI_ADDRESS;
29 extern const QString DEFAULT_OVERTE_ADDRESS;
30 extern const QString DEFAULT_HOME_ADDRESS;
31 
32 const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost";
33 const QString INDEX_PATH = "/";
34 
35 const QString GET_PLACE = "/api/v1/places/%1";
36 
37 /*@jsdoc
38  * The <code>location</code> API provides facilities related to your current location in the directory services.
39  *
40  * <h3>Getter/Setter</h3>
41  * <p>You can get and set your current directory services address by directly reading a string value from and writing a string value to
42  * the <code>location</code> object. This is an alternative to using the <code>location.href</code> property or other object
43  * functions.</p>
44  *
45  * @namespace location
46  *
47  * @hifi-interface
48  * @hifi-client-entity
49  * @hifi-avatar
50  * @hifi-assignment-client
51  *
52  * @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid(0)|Uuid.NULL} if you're not
53  * connected to the domain or are in a serverless domain.
54  * <em>Read-only.</em>
55  * @property {string} hostname - The name of the domain for your current directory services address (e.g., <code>"DomainName"</code>,
56  * <code>localhost</code>, or an IP address). Is blank if you're in a serverless domain.
57  * <em>Read-only.</em>
58  * @property {string} href - Your current directory services address (e.g., <code>"hifi://domainname/15,-10,26/0,0,0,1"</code>)
59  * regardless of whether or not you're connected to the domain. Starts with <code>"file:///"</code> if you're in a
60  * serverless domain.
61  * <em>Read-only.</em>
62  * @property {boolean} isConnected - <code>true</code> if you're connected to the domain in your current <code>href</code>
63  * directory services address, otherwise <code>false</code>.
64  * @property {string} pathname - The location and orientation in your current <code>href</code> directory services address
65  * (e.g., <code>"/15,-10,26/0,0,0,1"</code>).
66  * <em>Read-only.</em>
67  * @property {string} placename - The place name in your current <code>href</code> directory services address
68  * (e.g., <code>"DomainName"</code>). Is blank if your <code>hostname</code> is an IP address.
69  * <em>Read-only.</em>
70  * @property {string} protocol - The protocol of your current <code>href</code> directory services address (e.g., <code>"hifi"</code>).
71  * <em>Read-only.</em>
72  */
73 
74 /*@jsdoc
75  * The <code>AddressManager</code> API provides facilities related to your current location in the directory services.
76  *
77  * @namespace AddressManager
78  *
79  * @hifi-interface
80  * @hifi-client-entity
81  * @hifi-avatar
82  *
83  * @deprecated This API is deprecated and will be removed. Use the {@link location} or {@link Window|Window.location} APIs
84  * instead.
85  *
86  * @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid(0)|Uuid.NULL} if you're not
87  * connected to the domain or are in a serverless domain.
88  * <em>Read-only.</em>
89  * @property {string} hostname - The name of the domain for your current directory services address (e.g., <code>"DomainName"</code>,
90  * <code>localhost</code>, or an IP address). Is blank if you're in a serverless domain.
91  * <em>Read-only.</em>
92  * @property {string} href - Your current directory services address (e.g., <code>"hifi://domainname/15,-10,26/0,0,0,1"</code>)
93  * regardless of whether or not you're connected to the domain. Starts with <code>"file:///"</code> if you're in a
94  * serverless domain.
95  * <em>Read-only.</em>
96  * @property {boolean} isConnected - <code>true</code> if you're connected to the domain in your current <code>href</code>
97  * directory services address, otherwise <code>false</code>.
98  * @property {string} pathname - The location and orientation in your current <code>href</code> directory services address
99  * (e.g., <code>"/15,-10,26/0,0,0,1"</code>).
100  * <em>Read-only.</em>
101  * @property {string} placename - The place name in your current <code>href</code> directory services address
102  * (e.g., <code>"DomainName"</code>). Is blank if your <code>hostname</code> is an IP address.
103  * <em>Read-only.</em>
104  * @property {string} protocol - The protocol of your current <code>href</code> directory services address (e.g., <code>"hifi"</code>).
105  * <em>Read-only.</em>
106  *
107  * @borrows location.handleLookupString as handleLookupString
108  * @borrows location.goToViewpointForPath as goToViewpointForPath
109  * @borrows location.goBack as goBack
110  * @borrows location.goForward as goForward
111  * @borrows location.goToLocalSandbox as goToLocalSandbox
112  * @borrows location.goToEntry as goToEntry
113  * @borrows location.goToUser as goToUser
114  * @borrows location.goToLastAddress as goToLastAddress
115  * @borrows location.canGoBack as canGoBack
116  * @borrows location.refreshPreviousLookup as refreshPreviousLookup
117  * @borrows location.storeCurrentAddress as storeCurrentAddress
118  * @borrows location.copyAddress as copyAddress
119  * @borrows location.copyPath as copyPath
120  * @borrows location.lookupShareableNameForDomainID as lookupShareableNameForDomainID
121  *
122  * @borrows location.lookupResultsFinished as lookupResultsFinished
123  * @borrows location.lookupResultIsOffline as lookupResultIsOffline
124  * @borrows location.lookupResultIsNotFound as lookupResultIsNotFound
125  * @borrows location.possibleDomainChangeRequired as possibleDomainChangeRequired
126  * @borrows location.locationChangeRequired as locationChangeRequired
127  * @borrows location.possibleDomainChangeRequiredViaICEForID as possibleDomainChangeRequiredViaICEForID
128  * @borrows location.pathChangeRequired as pathChangeRequired
129  * @borrows location.hostChanged as hostChanged
130  * @borrows location.goBackPossible as goBackPossible
131  * @borrows location.goForwardPossible as goForwardPossible
132  */
133 
134 class AddressManager : public QObject, public Dependency {
135  Q_OBJECT
136  SINGLETON_DEPENDENCY
137  Q_PROPERTY(bool isConnected READ isConnected)
138  Q_PROPERTY(QUrl href READ currentShareableAddress)
139  Q_PROPERTY(QString protocol READ getProtocol)
140  Q_PROPERTY(QString hostname READ getHost)
141  Q_PROPERTY(QString pathname READ currentPath)
142  Q_PROPERTY(QString placename READ getPlaceName)
143  Q_PROPERTY(QString domainID READ getDomainID)
144 public:
145  using PositionGetter = std::function<glm::vec3()>;
146  using OrientationGetter = std::function<glm::quat()>;
147 
148  /*@jsdoc
149  * <p>The reasons for an address lookup via the Directory Services API are defined by numeric values:</p>
150  * <table>
151  * <thead>
152  * <tr>
153  * <th>Name</th>
154  * <th>Value</th>
155  * <th>Description</th>
156  * </tr>
157  * </thead>
158  * <tbody>
159  * <tr>
160  * <td><strong>UserInput</strong></td>
161  * <td><code>0</code></td>
162  * <td>User-typed input.</td>
163  * </tr>
164  * <tr>
165  * <td><strong>Back</strong></td>
166  * <td><code>1</code></td>
167  * <td>Address from a {@link location.goBack|goBack} call.</td>
168  * </tr>
169  * <tr>
170  * <td><strong>Forward</strong></td>
171  * <td><code>2</code></td>
172  * <td>Address from a {@link location.goForward|goForward} call.</td>
173  * </tr>
174  * <tr>
175  * <td><strong>StartupFromSettings</strong></td>
176  * <td><code>3</code></td>
177  * <td>Initial location at Interface start-up from settings.</td>
178  * </tr>
179  * <tr>
180  * <td><strong>DomainPathResponse</strong></td>
181  * <td><code>4</code></td>
182  * <td>A named path in the domain.</td>
183  * </tr>
184  * <tr>
185  * <td><strong>Internal</strong></td>
186  * <td><code>5</code></td>
187  * <td>An internal attempt to resolve an alternative path.</td>
188  * </tr>
189  * <tr>
190  * <td><strong>AttemptedRefresh</strong></td>
191  * <td><code>6</code></td>
192  * <td>A refresh after connecting to a domain.</td>
193  * </tr>
194  * <tr>
195  * <td><strong>Suggestions</strong></td>
196  * <td><code>7</code></td>
197  * <td>Address from the Goto dialog.</td>
198  * </tr>
199  * <tr>
200  * <td><strong>VisitUserFromPAL</strong></td>
201  * <td><code>8</code></td>
202  * <td>User from the People dialog.</td>
203  * </tr>
204  * </tbody>
205  * </table>
206  * @typedef {number} location.LookupTrigger
207  */
208  enum LookupTrigger {
209  UserInput,
210  Back,
211  Forward,
212  StartupFromSettings,
213  DomainPathResponse,
214  Internal,
215  AttemptedRefresh,
216  Suggestions,
217  VisitUserFromPAL
218  };
219  Q_ENUM(LookupTrigger)
220 
221  bool isConnected();
222  QString getProtocol() const;
223 
224  QUrl currentAddress(bool domainOnly = false) const;
225  QUrl currentFacingAddress() const;
226  QUrl currentShareableAddress(bool domainOnly = false) const;
227  QUrl currentPublicAddress(bool domainOnly = false) const;
228  QUrl currentFacingShareableAddress() const;
229  QUrl currentFacingPublicAddress() const;
230  QString currentPath(bool withOrientation = true) const;
231  QString currentFacingPath() const;
232 
233  QUrl lastAddress() const;
234 
235  const QUuid& getRootPlaceID() const { return _rootPlaceID; }
236  QString getPlaceName() const;
237  QString getDomainID() const;
238 
239  QString getHost() const { return _domainURL.host(); }
240 
241  void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
242  void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
243 
244  void loadSettings(const QString& lookupString = QString());
245 
246  const QStack<QUrl>& getBackStack() const { return _backStack; }
247  const QStack<QUrl>& getForwardStack() const { return _forwardStack; }
248 
249  QUrl getDomainURL() { return _domainURL; }
250 
251 public slots:
252  /*@jsdoc
253  * Takes you to a specified directory services address.
254  * @function location.handleLookupString
255  * @param {string} address - The address to go to: a <code>"hifi://"</code> address, an IP address (e.g.,
256  * <code>"127.0.0.1"</code> or <code>"localhost"</code>), a <code>file:///</code> address, a domain name, a named path
257  * on a domain (starts with <code>"/"</code>), a position or position and orientation, or a user (starts with
258  * <code>"@"</code>).
259  * @param {boolean} [fromSuggestions=false] - Set to <code>true</code> if the address is obtained from the "Explore" app.
260  * Helps ensure that the user's location history is correctly maintained.
261  */
262  void handleLookupString(const QString& lookupString, bool fromSuggestions = false);
263 
264  /*@jsdoc
265  * Takes you to a position and orientation resulting from a lookup for a named path in the domain (set in the domain
266  * server's settings).
267  * @function location.goToViewpointForPath
268  * @param {string} path - The position and orientation corresponding to the named path.
269  * @param {string} namedPath - The named path that was looked up on the server.
270  * @deprecated This function is deprecated and will be removed.
271  */
272  // This function is marked as deprecated in anticipation that it will not be included in the JavaScript API if and when the
273  // functions and signals that should be exposed are moved to a scripting interface class.
274  //
275  // we currently expect this to be called from NodeList once handleLookupString has been called with a path
276  bool goToViewpointForPath(const QString& viewpointString, const QString& pathString)
277  { return handleViewpoint(viewpointString, false, DomainPathResponse, false, pathString); }
278 
279  /*@jsdoc
280  * Takes you back to the previous location in your navigation history, if there is one.
281  * @function location.goBack
282  */
283  void goBack();
284 
285  /*@jsdoc
286  * Takes you forward to the next location in your navigation history, if there is one.
287  * @function location.goForward
288  */
289  void goForward();
290 
291  /*@jsdoc
292  * Takes you to the local Sandbox server that's running on the same PC as Interface.
293  * @function location.goToLocalSandbox
294  * @param {string} [path=""] - The position and orientation to go to (e.g., <code>"/0,0,0"</code>).
295  * @param {location.LookupTrigger} [trigger=StartupFromSettings] - The reason for the function call. Helps ensure that user's
296  * location history is correctly maintained.
297  */
298  void goToLocalSandbox(QString path = "", LookupTrigger trigger = LookupTrigger::StartupFromSettings) {
299  handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger);
300  }
301 
302  /*@jsdoc
303  * Takes you to the default "welcome" directory services address.
304  * @function location.goToEntry
305  * @param {location.LookupTrigger} trigger=StartupFromSettings - The reason for the function call. Helps ensure that user's
306  * location history is correctly maintained.
307  */
308  void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings);
309 
310  /*@jsdoc
311  * Takes you to the specified user's location.
312  * @function location.goToUser
313  * @param {string} username - The user's username.
314  * @param {boolean} [matchOrientation=true] - If <code>true</code> then go to a location just in front of the user and turn
315  * to face them, otherwise go to the user's exact location and orientation.
316  */
317  void goToUser(const QString& username, bool shouldMatchOrientation = true);
318 
319  /*@jsdoc
320  * Takes you to the last address tried. This will be the last URL tried from <code>location.handleLookupString</code>.
321  * @function location.goToLastAddress
322  */
323  void goToLastAddress();
324 
325  /*@jsdoc
326  * Checks if going back to the previous location is possible.
327  * @function location.canGoBack
328  * @returns {boolean} <code>true</code> if going back is possible, <code>false</code> if it isn't.
329  */
330  bool canGoBack() const;
331 
332  /*@jsdoc
333  * Refreshes the current address, e.g., after connecting to a domain in order to position the user to the desired location.
334  * @function location.refreshPreviousLookup
335  * @deprecated This function is deprecated and will be removed.
336  */
337  // This function is marked as deprecated in anticipation that it will not be included in the JavaScript API if and when the
338  // functions and signals that should be exposed are moved to a scripting interface class.
339  void refreshPreviousLookup();
340 
341  /*@jsdoc
342  * Updates your current directory services location in Interface's {@link Settings} file as your last-known address. This can be used
343  * to ensure that you start up at that address if you exit Interface without a later address automatically being saved.
344  * @function location.storeCurrentAddress
345  */
346  void storeCurrentAddress();
347 
348  /*@jsdoc
349  * Copies your current directory services address (i.e., <code>location.href</code> property value) to the OS clipboard.
350  * @function location.copyAddress
351  */
352  void copyAddress();
353 
354  /*@jsdoc
355  * Copies your current directory services location and orientation (i.e., <code>location.pathname</code> property value) to the OS
356  * clipboard.
357  * @function location.copyPath
358  */
359  void copyPath();
360 
361  /*@jsdoc
362  * Retrieves and remembers the place name for the given domain ID if the place name is not already known.
363  * @function location.lookupShareableNameForDomainID
364  * @param {Uuid} domainID - The UUID of the domain.
365  * @deprecated This function is deprecated and will be removed.
366  */
367  // This function is marked as deprecated in anticipation that it will not be included in the JavaScript API if and when the
368  // functions and signals that should be exposed are moved to a scripting interface class.
369  void lookupShareableNameForDomainID(const QUuid& domainID);
370 
371 signals:
372  /*@jsdoc
373  * Triggered when looking up the details of a directory services user or location to go to has completed (successfully or
374  * unsuccessfully).
375  * @function location.lookupResultsFinished
376  * @returns {Signal}
377  */
378  void lookupResultsFinished();
379 
380  /*@jsdoc
381  * Triggered when looking up the details of a directory services user or location to go to has completed and the domain or user is
382  * offline.
383  * @function location.lookupResultIsOffline
384  * @returns {Signal}
385  */
386  void lookupResultIsOffline();
387 
388  /*@jsdoc
389  * Triggered when looking up the details of a directory services user or location to go to has completed and the domain or user could
390  * not be found.
391  * @function location.lookupResultIsNotFound
392  * @returns {Signal}
393  */
394  void lookupResultIsNotFound();
395 
396  /*@jsdoc
397  * Triggered when a request is made to go to a URL or IP address.
398  * @function location.possibleDomainChangeRequired
399  * @param {string} domainURL - The URL of the domain.
400  * @param {Uuid} domainID - The UUID of the domain to go to. May be {@link Uuid|Uuid.NULL} if not yet known.
401  * @returns {Signal}
402  */
403  // No example because this function isn't typically used in scripts.
404  void possibleDomainChangeRequired(QUrl domainURL, QUuid domainID);
405 
406  /*@jsdoc
407  * Triggered when a request is made to go to a named domain or user.
408  * @function location.possibleDomainChangeRequiredViaICEForID
409  * @param {string} iceServerHostName - IP address of the ICE server.
410  * @param {Uuid} domainID - The UUID of the domain to go to.
411  * @returns {Signal}
412  */
413  // No example because this function isn't typically used in scripts.
414  void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID);
415 
416  /*@jsdoc
417  * Triggered when an attempt is made to send your avatar to a specified position on the current domain. For example, when
418  * you change domains or enter a position to go to in the "Goto" dialog.
419  * @function location.locationChangeRequired
420  * @param {Vec3} position - The position to go to.
421  * @param {boolean} hasOrientationChange - If <code>true</code> then a new <code>orientation</code> has been requested.
422  * @param {Quat} orientation - The orientation to change to. Is {@link Quat(0)|Quat.IDENTITY} if
423  * <code>hasOrientationChange</code> is <code>false</code>.
424  * @param {boolean} shouldFaceLocation - If <code>true</code> then the request is to go to a position near that specified
425  * and orient your avatar to face it. For example when you visit someone from the "People" dialog.
426  * @returns {Signal}
427  * @example <caption>Report location change requests.</caption>
428  * function onLocationChangeRequired(newPosition, hasOrientationChange, newOrientation, shouldFaceLocation) {
429  * print("Location change required:");
430  * print("- New position = " + JSON.stringify(newPosition));
431  * print("- Has orientation change = " + hasOrientationChange);
432  * print("- New orientation = " + JSON.stringify(newOrientation));
433  * print("- Should face location = " + shouldFaceLocation);
434  * }
435  *
436  * location.locationChangeRequired.connect(onLocationChangeRequired);
437  */
438  void locationChangeRequired(const glm::vec3& newPosition,
439  bool hasOrientationChange, const glm::quat& newOrientation,
440  bool shouldFaceLocation);
441 
442  /*@jsdoc
443  * Triggered when an attempt is made to send your avatar to a new named path on the domain (set in the domain server's
444  * settings). For example, when you enter a "/" followed by the path's name in the "GOTO" dialog.
445  * @function location.pathChangeRequired
446  * @param {string} path - The name of the path to go to.
447  * @returns {Signal}
448  * @example <caption>Report path change requests.</caption>
449  * function onPathChangeRequired(newPath) {
450  * print("onPathChangeRequired: newPath = " + newPath);
451  * }
452  *
453  * location.pathChangeRequired.connect(onPathChangeRequired);
454  */
455  void pathChangeRequired(const QString& newPath);
456 
457  /*@jsdoc
458  * Triggered when you navigate to a new domain.
459  * @function location.hostChanged
460  * @param {string} hostname - The new domain's host name.
461  * @returns {Signal}
462  * @example <caption>Report when you navigate to a new domain.</caption>
463  * function onHostChanged(host) {
464  * print("Host changed to: " + host);
465  * }
466  *
467  * location.hostChanged.connect(onHostChanged);
468  */
469  void hostChanged(const QString& newHost);
470 
471  /*@jsdoc
472  * Triggered when there's a change in whether or not there's a previous location that can be navigated to using
473  * {@link location.goBack|goBack}. (Reflects changes in the state of the "Goto" dialog's back arrow.)
474  * @function location.goBackPossible
475  * @param {boolean} isPossible - <code>true</code> if there's a previous location to navigate to, otherwise
476  * <code>false</code>.
477  * @returns {Signal}
478  * @example <caption>Report when ability to navigate back changes.</caption>
479  * function onGoBackPossible(isPossible) {
480  * print("Go back possible: " + isPossible);
481  * }
482  *
483  * location.goBackPossible.connect(onGoBackPossible);
484  */
485  void goBackPossible(bool isPossible);
486 
487  /*@jsdoc
488  * Triggered when there's a change in whether or not there's a forward location that can be navigated to using
489  * {@link location.goForward|goForward}. (Reflects changes in the state of the "Goto" dialog's forward arrow.)
490  * @function location.goForwardPossible
491  * @param {boolean} isPossible - <code>true</code> if there's a forward location to navigate to, otherwise
492  * <code>false</code>.
493  * @returns {Signal}
494  * @example <caption>Report when ability to navigate forward changes.</caption>
495  * function onGoForwardPossible(isPossible) {
496  * print("Go forward possible: " + isPossible);
497  * }
498  *
499  * location.goForwardPossible.connect(onGoForwardPossible);
500  */
501  void goForwardPossible(bool isPossible);
502 
503 private slots:
504  void handleAPIResponse(QNetworkReply* requestReply);
505  void handleAPIError(QNetworkReply* errorReply);
506 
507  void handleShareableNameAPIResponse(QNetworkReply* requestReply);
508 
509 private:
510  void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply* reply);
511 
512  // Set host and port, and return `true` if it was changed.
513  bool setHost(const QString& host, LookupTrigger trigger, quint16 port = 0);
514  bool setDomainInfo(const QUrl& domainURL, LookupTrigger trigger);
515 
516  const JSONCallbackParameters& apiCallbackParameters();
517 
518  bool handleUrl(const QUrl& lookupUrl, LookupTrigger trigger = UserInput, const QString& lookupUrlInString = "");
519 
520  bool handleNetworkAddress(const QString& lookupString, LookupTrigger trigger, bool& hostChanged);
521  void handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly = false);
522  bool handleViewpoint(const QString& viewpointString, bool shouldFace, LookupTrigger trigger,
523  bool definitelyPathOnly = false, const QString& pathString = QString());
524  bool handleUsername(const QString& lookupString);
525  bool handleDomainID(const QString& host);
526 
527  void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger);
528  void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger);
529 
530  void addCurrentAddressToHistory(LookupTrigger trigger);
531 
532  void resetConfirmConnectWithoutAvatarEntities();
533 
534  QUrl _domainURL;
535  QUrl _lastVisitedURL;
536 
537  QUuid _rootPlaceID;
538  PositionGetter _positionGetter;
539  OrientationGetter _orientationGetter;
540 
541  QString _shareablePlaceName;
542 
543  QStack<QUrl> _backStack;
544  QStack<QUrl> _forwardStack;
545  quint64 _lastBackPush = 0;
546 
547  QString _newHostLookupPath;
548 
549  QUrl _previousAPILookup;
550 };
551 
552 Q_DECLARE_METATYPE(AddressManager::LookupTrigger)
553 
554 #endif // hifi_AddressManager_h