centralize path creation from position and orientation to AddressManager

This commit is contained in:
Stephen Birarda 2014-09-11 17:17:47 -07:00
parent 23a0ef2de1
commit b56ea5c936
10 changed files with 188 additions and 224 deletions

View file

@ -84,7 +84,6 @@
#include "scripting/MenuScriptingInterface.h"
#include "scripting/SettingsScriptingInterface.h"
#include "scripting/WindowScriptingInterface.h"
#include "scripting/LocationScriptingInterface.h"
#include "ui/InfoView.h"
#include "ui/OAuthWebViewHandler.h"
@ -408,7 +407,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_runningScriptsWidget, &RunningScriptsWidget::setBoundary);
// connect to the domainChangeRequired signal on AddressManager
connect(&AddressManager::getInstance(), &AddressManager::domainChangeRequired, this, &Application::changeDomainHostname);
connect(&AddressManager::getInstance(), &AddressManager::possibleDomainChangeRequired,
this, &Application::changeDomainHostname);
//When -url in command line, teleport to location
urlGoTo(argc, constArgv);
@ -811,7 +811,7 @@ bool Application::event(QEvent* event) {
// handle custom URL
if (event->type() == QEvent::FileOpen) {
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
bool isHifiSchemeURL = !fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME);
bool isHifiSchemeURL = !fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(HIFI_URL_SCHEME);
if (isHifiSchemeURL) {
// Menu::getInstance()->goToURL(fileEvent->url().toLocalFile());
}
@ -3376,31 +3376,44 @@ void Application::updateWindowTitle(){
void Application::updateLocationInServer() {
AccountManager& accountManager = AccountManager::getInstance();
if (accountManager.isLoggedIn()) {
const QUuid& domainUUID = NodeList::getInstance()->getDomainHandler().getUUID();
if (accountManager.isLoggedIn() && !domainUUID.isNull()) {
// construct a QJsonObject given the user's current address information
QJsonObject updatedLocationObject;
QJsonObject rootObject;
QJsonObject addressObject;
addressObject.insert("position", QString(createByteArray(_myAvatar->getPosition())));
addressObject.insert("orientation", QString(createByteArray(glm::degrees(safeEulerAngles(_myAvatar->getOrientation())))));
addressObject.insert("domain", NodeList::getInstance()->getDomainHandler().getHostname());
QJsonObject locationObject;
QString pathString = AddressManager::pathForPositionAndOrientation(_myAvatar->getPosition(),
true,
_myAvatar->getOrientation());
const QString LOCATION_KEY_IN_ROOT = "location";
const QString PATH_KEY_IN_LOCATION = "path";
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION, domainUUID.toString());
updatedLocationObject.insert("address", addressObject);
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation,
JSONCallbackParameters(), QJsonDocument(updatedLocationObject).toJson());
accountManager.authenticatedRequest("/api/v1/users/location", QNetworkAccessManager::PutOperation,
JSONCallbackParameters(), QJsonDocument(rootObject).toJson());
}
}
void Application::changeDomainHostname(const QString &newDomainHostname) {
// tell the MyAvatar object to send a kill packet so that it dissapears from its old avatar mixer immediately
_myAvatar->sendKillAvatar();
NodeList* nodeList = NodeList::getInstance();
// call the domain hostname change as a queued connection on the nodelist
QMetaObject::invokeMethod(&NodeList::getInstance()->getDomainHandler(), "setHostname",
Q_ARG(const QString&, newDomainHostname));
if (!nodeList->getDomainHandler().isCurrentHostname(newDomainHostname)) {
// tell the MyAvatar object to send a kill packet so that it dissapears from its old avatar mixer immediately
_myAvatar->sendKillAvatar();
// call the domain hostname change as a queued connection on the nodelist
QMetaObject::invokeMethod(&NodeList::getInstance()->getDomainHandler(), "setHostname",
Q_ARG(const QString&, newDomainHostname));
}
}
void Application::domainChanged(const QString& domainHostname) {
@ -3785,12 +3798,12 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
scriptEngine->registerGlobalObject("Overlays", &_overlays);
QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", WindowScriptingInterface::getInstance());
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
LocationScriptingInterface::locationSetter, windowValue);
// register `location` on the global object.
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
LocationScriptingInterface::locationSetter);
// scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
// LocationScriptingInterface::locationSetter, windowValue);
//
// // register `location` on the global object.
// scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
// LocationScriptingInterface::locationSetter);
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
@ -4111,8 +4124,8 @@ void Application::takeSnapshot() {
void Application::urlGoTo(int argc, const char * constArgv[]) {
//Gets the url (hifi://domain/destination/orientation)
QString customUrl = getCmdOption(argc, constArgv, "-url");
if(customUrl.startsWith(CUSTOM_URL_SCHEME + "//")) {
QStringList urlParts = customUrl.remove(0, CUSTOM_URL_SCHEME.length() + 2).split('/', QString::SkipEmptyParts);
if(customUrl.startsWith(HIFI_URL_SCHEME + "//")) {
QStringList urlParts = customUrl.remove(0, HIFI_URL_SCHEME.length() + 2).split('/', QString::SkipEmptyParts);
if (urlParts.count() == 1) {
// location coordinates or place name
QString domain = urlParts[0];

View file

@ -114,7 +114,6 @@ static const float NODE_KILLED_GREEN = 0.0f;
static const float NODE_KILLED_BLUE = 0.0f;
static const QString SNAPSHOT_EXTENSION = ".jpg";
static const QString CUSTOM_URL_SCHEME = "hifi:";
static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees
static const float BILLBOARD_DISTANCE = 5.0f; // meters

View file

@ -15,8 +15,7 @@
#include "LocationManager.h"
#include <UserActivityLogger.h>
const QString GET_USER_ADDRESS = "/api/v1/users/%1/address";
const QString GET_PLACE_ADDRESS = "/api/v1/metaverse/search/%1";
const QString POST_PLACE_CREATE = "/api/v1/places/";
LocationManager& LocationManager::getInstance() {
@ -36,10 +35,6 @@ void LocationManager::namedLocationDataReceived(const QJsonObject& data) {
}
}
void LocationManager::errorDataReceived(QNetworkReply::NetworkError error, const QString& message) {
emit creationCompleted(LocationManager::SystemError);
}
void LocationManager::createNamedLocation(NamedLocation* namedLocation) {
AccountManager& accountManager = AccountManager::getInstance();
if (accountManager.isLoggedIn()) {
@ -54,78 +49,6 @@ void LocationManager::createNamedLocation(NamedLocation* namedLocation) {
}
}
void LocationManager::goTo(QString destination) {
const QString USER_DESTINATION_TYPE = "user";
const QString PLACE_DESTINATION_TYPE = "place";
const QString COORDINATE_DESTINATION_TYPE = "coordinate";
if (destination.startsWith("@")) {
// remove '@' and go to user
QString destinationUser = destination.remove(0, 1);
UserActivityLogger::getInstance().wentTo(USER_DESTINATION_TYPE, destinationUser);
goToUser(destinationUser);
return;
}
// go to coordinate destination or to Username
if (!goToDestination(destination)) {
destination = QString(QUrl::toPercentEncoding(destination));
UserActivityLogger::getInstance().wentTo(PLACE_DESTINATION_TYPE, destination);
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "goToPlaceFromResponse";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "handleAddressLookupError";
AccountManager::getInstance().authenticatedRequest(GET_PLACE_ADDRESS.arg(destination),
QNetworkAccessManager::GetOperation,
callbackParams);
} else {
UserActivityLogger::getInstance().wentTo(COORDINATE_DESTINATION_TYPE, destination);
}
}
void LocationManager::goToPlaceFromResponse(const QJsonObject& responseData) {
QJsonValue status = responseData["status"];
QJsonObject data = responseData["data"].toObject();
QJsonObject domainObject = data["domain"].toObject();
const QString DOMAIN_NETWORK_ADDRESS_KEY = "network_address";
// get the network address for the domain we need to switch to
NodeList::getInstance()->getDomainHandler().setHostname(domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString());
// check if there is a path inside the domain we need to jump to
}
void LocationManager::goToUser(QString userName) {
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = Application::getInstance()->getAvatar();
callbackParams.jsonCallbackMethod = "goToLocationFromResponse";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "handleAddressLookupError";
userName = QString(QUrl::toPercentEncoding(userName));
AccountManager::getInstance().authenticatedRequest(GET_USER_ADDRESS.arg(userName),
QNetworkAccessManager::GetOperation,
callbackParams);
}
void LocationManager::goToPlace(QString placeName) {
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = Application::getInstance()->getAvatar();
callbackParams.jsonCallbackMethod = "goToLocationFromResponse";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "handleAddressLookupError";
placeName = QString(QUrl::toPercentEncoding(placeName));
AccountManager::getInstance().authenticatedRequest(GET_PLACE_ADDRESS.arg(placeName),
QNetworkAccessManager::GetOperation,
callbackParams);
}
void LocationManager::goToUrl(const QUrl& url) {
// if (location.startsWith(CUSTOM_URL_SCHEME + "/")) {
// QStringList urlParts = location.remove(0, CUSTOM_URL_SCHEME.length()).split('/', QString::SkipEmptyParts);

View file

@ -49,8 +49,6 @@ signals:
private slots:
void namedLocationDataReceived(const QJsonObject& data);
void errorDataReceived(QNetworkReply::NetworkError error, const QString& message);
void goToPlaceFromResponse(const QJsonObject& jsonObject);
};

View file

@ -1,53 +0,0 @@
//
// LocationScriptingInterface.cpp
// interface/src/scripting
//
// Created by Ryan Huffman on 4/29/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/glm.hpp>
#include "NodeList.h"
#include "LocationScriptingInterface.h"
LocationScriptingInterface* LocationScriptingInterface::getInstance() {
static LocationScriptingInterface sharedInstance;
return &sharedInstance;
}
bool LocationScriptingInterface::isConnected() {
return NodeList::getInstance()->getDomainHandler().isConnected();
}
QString LocationScriptingInterface::getHref() {
return getProtocol() + "//" + getHostname() + getPathname();
}
QString LocationScriptingInterface::getPathname() {
const glm::vec3& position = Application::getInstance()->getAvatar()->getPosition();
QString path;
path.sprintf("/%.4f,%.4f,%.4f", position.x, position.y, position.z);
return path;
}
QString LocationScriptingInterface::getHostname() {
return NodeList::getInstance()->getDomainHandler().getHostname();
}
void LocationScriptingInterface::assign(const QString& url) {
QMetaObject::invokeMethod(Menu::getInstance(), "goToURL", Q_ARG(const QString&, url));
}
QScriptValue LocationScriptingInterface::locationGetter(QScriptContext* context, QScriptEngine* engine) {
return engine->newQObject(getInstance());
}
QScriptValue LocationScriptingInterface::locationSetter(QScriptContext* context, QScriptEngine* engine) {
LocationScriptingInterface::getInstance()->assign(context->argument(0).toString());
return QScriptValue::UndefinedValue;
}

View file

@ -1,48 +0,0 @@
//
// LocationScriptingInterface.h
// interface/src/scripting
//
// Created by Ryan Huffman on 4/29/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_LocationScriptingInterface_h
#define hifi_LocationScriptingInterface_h
#include <QObject>
#include <QScriptContext>
#include <QScriptEngine>
#include <QScriptValue>
#include <QString>
#include "Application.h"
class LocationScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(bool isConnected READ isConnected)
Q_PROPERTY(QString href READ getHref)
Q_PROPERTY(QString protocol READ getProtocol)
Q_PROPERTY(QString hostname READ getHostname)
Q_PROPERTY(QString pathname READ getPathname)
LocationScriptingInterface() { };
public:
static LocationScriptingInterface* getInstance();
bool isConnected();
QString getHref();
QString getProtocol() { return CUSTOM_URL_SCHEME; };
QString getPathname();
QString getHostname();
static QScriptValue locationGetter(QScriptContext* context, QScriptEngine* engine);
static QScriptValue locationSetter(QScriptContext* context, QScriptEngine* engine);
public slots:
void assign(const QString& url);
};
#endif // hifi_LocationScriptingInterface_h

View file

@ -13,6 +13,8 @@
#include <qregexp.h>
#include <qstringlist.h>
#include <GLMHelpers.h>
#include "AddressManager.h"
AddressManager& AddressManager::getInstance() {
@ -20,6 +22,33 @@ AddressManager& AddressManager::getInstance() {
return sharedInstance;
}
QString AddressManager::pathForPositionAndOrientation(const glm::vec3& position, bool hasOrientation,
const glm::quat& orientation) {
QString pathString = "/" + createByteArray(position);
if (hasOrientation) {
QString orientationString = createByteArray(glm::degrees(safeEulerAngles(orientation)));
pathString += "/" + orientationString;
}
return pathString;
}
const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
static bool hasSetupParameters = false;
static JSONCallbackParameters callbackParams;
if (!hasSetupParameters) {
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "handleAPIResponse";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "handleAPIError";
}
return callbackParams;
}
void AddressManager::handleLookupString(const QString& lookupString) {
// there are 4 possible lookup strings
@ -28,15 +57,77 @@ void AddressManager::handleLookupString(const QString& lookupString) {
// 3. location string (posX,posY,posZ/eulerX,eulerY,eulerZ)
// 4. domain network address (IP or dns resolvable hostname)
QString sanitizedLookupString = lookupString.trimmed().remove(HIFI_URL_SCHEME + "//");
// use our regex'ed helpers to figure out what we're supposed to do with this
if (!lookupHandledAsUsername(lookupString) &&
!lookupHandledAsNetworkAddress(lookupString) &&
!lookupHandledAsLocationString(lookupString)) {
if (!isLookupHandledAsUsername(sanitizedLookupString) &&
!isLookupHandledAsNetworkAddress(sanitizedLookupString) &&
!isLookupHandledAsViewpoint(sanitizedLookupString)) {
attemptPlaceNameLookup(sanitizedLookupString);
}
}
bool AddressManager::lookupHandledAsNetworkAddress(const QString& lookupString) {
void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) {
QJsonObject dataObject = jsonObject["data"].toObject();
const QString ADDRESS_API_DOMAIN_KEY = "domain";
const QString ADDRESS_API_ONLINE_KEY = "online";
if (!dataObject.contains(ADDRESS_API_ONLINE_KEY)
|| dataObject[ADDRESS_API_ONLINE_KEY].toBool()) {
if (dataObject.contains(ADDRESS_API_DOMAIN_KEY)) {
QJsonObject domainObject = dataObject[ADDRESS_API_DOMAIN_KEY].toObject();
const QString DOMAIN_NETWORK_ADDRESS_KEY = "network_address";
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
emit possibleDomainChangeRequired(domainHostname);
// take the path that came back
const QString LOCATION_KEY = "location";
const QString LOCATION_PATH_KEY = "path";
QString returnedPath;
if (domainObject.contains(LOCATION_PATH_KEY)) {
returnedPath = domainObject[LOCATION_PATH_KEY].toString();
} else if (domainObject.contains(LOCATION_KEY)) {
returnedPath = domainObject[LOCATION_KEY].toObject()[LOCATION_PATH_KEY].toString();
}
if (!returnedPath.isEmpty()) {
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
if (!isLookupHandledAsViewpoint(returnedPath)) {
qDebug() << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
}
}
} else {
qDebug() << "Received an address manager API response with no domain key. Cannot parse.";
qDebug() << jsonObject;
}
} else {
// we've been told that this result exists but is offline, emit our signal so the application can handle
emit lookupResultIsOffline();
}
}
void AddressManager::handleAPIError(QNetworkReply::NetworkError error, const QString& message) {
qDebug() << "AddressManager API error -" << error << "-" << message;
}
const QString GET_PLACE = "/api/v1/places/%1";
void AddressManager::attemptPlaceNameLookup(const QString& lookupString) {
// assume this is a place name and see if we can get any info on it
QString placeName = QUrl::toPercentEncoding(lookupString);
AccountManager::getInstance().authenticatedRequest(GET_PLACE.arg(placeName),
QNetworkAccessManager::GetOperation,
apiCallbackParameters());
}
bool AddressManager::isLookupHandledAsNetworkAddress(const QString& lookupString) {
const QString IP_ADDRESS_REGEX_STRING = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}"
"([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(:\\d{1,5})?$";
@ -46,23 +137,23 @@ bool AddressManager::lookupHandledAsNetworkAddress(const QString& lookupString)
QRegExp hostnameRegex(HOSTNAME_REGEX_STRING, Qt::CaseInsensitive);
if (hostnameRegex.indexIn(lookupString) != -1) {
emit domainChangeRequired(hostnameRegex.cap(0));
emit possibleDomainChangeRequired(hostnameRegex.cap(0));
return true;
}
QRegExp ipAddressRegex(IP_ADDRESS_REGEX_STRING);
if (ipAddressRegex.indexIn(lookupString) != -1) {
emit domainChangeRequired(ipAddressRegex.cap(0));
emit possibleDomainChangeRequired(ipAddressRegex.cap(0));
return true;
}
return false;
}
bool AddressManager::lookupHandledAsLocationString(const QString& lookupString) {
bool AddressManager::isLookupHandledAsViewpoint(const QString& lookupString) {
const QString FLOAT_REGEX_STRING = "([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
const QString TRIPLE_FLOAT_REGEX_STRING = FLOAT_REGEX_STRING + "\\s*,\\s*" +
const QString TRIPLE_FLOAT_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + "\\s*,\\s*" +
FLOAT_REGEX_STRING + "\\s*,\\s*" + FLOAT_REGEX_STRING + "\\s*(?:$|\\/)";
QRegExp tripleFloatRegex(TRIPLE_FLOAT_REGEX_STRING);
@ -103,13 +194,19 @@ bool AddressManager::lookupHandledAsLocationString(const QString& lookupString)
return false;
}
bool AddressManager::lookupHandledAsUsername(const QString& lookupString) {
const QString GET_USER_LOCATION = "/api/v1/users/%1/location";
bool AddressManager::isLookupHandledAsUsername(const QString& lookupString) {
const QString USERNAME_REGEX_STRING = "^@(\\S+)$";
QRegExp usernameRegex(USERNAME_REGEX_STRING);
if (usernameRegex.indexIn(lookupString) != -1) {
// this is a username - pull the captured name and lookup that user's address
QString username = QUrl::toPercentEncoding(usernameRegex.cap(1));
// this is a username - pull the captured name and lookup that user's location
AccountManager::getInstance().authenticatedRequest(GET_USER_LOCATION.arg(username),
QNetworkAccessManager::GetOperation,
apiCallbackParameters());
return true;
}

View file

@ -15,20 +15,37 @@
#include <qobject.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include "AccountManager.h"
static const QString HIFI_URL_SCHEME = "hifi:";
const glm::quat EMPTY_QUAT = glm::quat();
class AddressManager : public QObject {
Q_OBJECT
public:
static AddressManager& getInstance();
static QString pathForPositionAndOrientation(const glm::vec3& position, bool hasOrientation = false,
const glm::quat& orientation = EMPTY_QUAT);
void handleLookupString(const QString& lookupString);
void attemptPlaceNameLookup(const QString& lookupString);
public slots:
void handleAPIResponse(const QJsonObject& jsonObject);
void handleAPIError(QNetworkReply::NetworkError error, const QString& message);
signals:
void domainChangeRequired(const QString& newHostname);
void lookupResultIsOffline();
void possibleDomainChangeRequired(const QString& newHostname);
void locationChangeRequired(const glm::vec3& newPosition, bool hasOrientationChange, const glm::vec3& newOrientation);
private:
bool lookupHandledAsNetworkAddress(const QString& lookupString);
bool lookupHandledAsLocationString(const QString& lookupString);
bool lookupHandledAsUsername(const QString& lookupString);
const JSONCallbackParameters& apiCallbackParameters();
bool isLookupHandledAsNetworkAddress(const QString& lookupString);
bool isLookupHandledAsViewpoint(const QString& lookupString);
bool isLookupHandledAsUsername(const QString& lookupString);
};
#endif // hifi_AddressManager_h

View file

@ -73,6 +73,22 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos
_hostname = hostname;
}
void DomainHandler::setUUID(const QUuid& uuid) {
if (uuid != _uuid) {
_uuid = uuid;
qDebug() << "Domain uuid changed to" << uuidStringWithoutCurlyBraces(_uuid);
}
}
QString DomainHandler::hostnameWithoutPort(const QString& hostname) {
int colonIndex = hostname.indexOf(':');
return colonIndex > 0 ? hostname.left(colonIndex) : hostname;
}
bool DomainHandler::isCurrentHostname(const QString& hostname) {
return hostnameWithoutPort(hostname) == _hostname;
}
void DomainHandler::setHostname(const QString& hostname) {
if (hostname != _hostname) {

View file

@ -37,8 +37,10 @@ public:
void clearSettings();
const QUuid& getUUID() const { return _uuid; }
void setUUID(const QUuid& uuid) { _uuid = uuid; }
void setUUID(const QUuid& uuid);
static QString hostnameWithoutPort(const QString& hostname);
bool isCurrentHostname(const QString& hostname);
const QString& getHostname() const { return _hostname; }
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }