mirror of
https://github.com/overte-org/overte.git
synced 2025-04-25 13:33:30 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into modelserver
This commit is contained in:
commit
3bb72c04a5
48 changed files with 1384 additions and 497 deletions
|
@ -39,7 +39,7 @@ you to run the full stack of the virtual world.
|
||||||
In order to set up your own virtual world, you need to set up and run your own
|
In order to set up your own virtual world, you need to set up and run your own
|
||||||
local "domain".
|
local "domain".
|
||||||
|
|
||||||
The domain-server gives a number different types of assignments to the assignment-client for different features: audio, avatars, voxels, particles, and meta-voxels.
|
The domain-server gives a number different types of assignments to the assignment-client for different features: audio, avatars, voxels, particles, meta-voxels and models.
|
||||||
|
|
||||||
Follow the instructions in the [build guide](BUILD.md) to build the various components.
|
Follow the instructions in the [build guide](BUILD.md) to build the various components.
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ Any target can be terminated with Ctrl-C (SIGINT) in the associated Terminal win
|
||||||
|
|
||||||
This assignment-client will grab one assignment from the domain-server. You can tell the assignment-client what type you want it to be with the `-t` option. You can also run an assignment-client that forks off *n* assignment-clients with the `-n` option.
|
This assignment-client will grab one assignment from the domain-server. You can tell the assignment-client what type you want it to be with the `-t` option. You can also run an assignment-client that forks off *n* assignment-clients with the `-n` option.
|
||||||
|
|
||||||
./assignment-client -n 5
|
./assignment-client -n 6
|
||||||
|
|
||||||
To test things out you'll want to run the Interface client.
|
To test things out you'll want to run the Interface client.
|
||||||
|
|
||||||
|
|
|
@ -191,9 +191,10 @@ void Agent::run() {
|
||||||
// figure out the URL for the script for this agent assignment
|
// figure out the URL for the script for this agent assignment
|
||||||
QUrl scriptURL;
|
QUrl scriptURL;
|
||||||
if (_payload.isEmpty()) {
|
if (_payload.isEmpty()) {
|
||||||
scriptURL = QUrl(QString("http://%1:8080/assignment/%2")
|
scriptURL = QUrl(QString("http://%1:%2/assignment/%3")
|
||||||
.arg(NodeList::getInstance()->getDomainHandler().getIP().toString(),
|
.arg(NodeList::getInstance()->getDomainHandler().getIP().toString())
|
||||||
uuidStringWithoutCurlyBraces(_uuid)));
|
.arg(DOMAIN_SERVER_HTTP_PORT)
|
||||||
|
.arg(uuidStringWithoutCurlyBraces(_uuid)));
|
||||||
} else {
|
} else {
|
||||||
scriptURL = QUrl(_payload);
|
scriptURL = QUrl(_payload);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <QtCore/QProcess>
|
#include <QtCore/QProcess>
|
||||||
#include <QtCore/QStandardPaths>
|
#include <QtCore/QStandardPaths>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtCore/QUrlQuery>
|
||||||
|
|
||||||
#include <gnutls/dtls.h>
|
#include <gnutls/dtls.h>
|
||||||
|
|
||||||
|
@ -31,18 +32,22 @@
|
||||||
|
|
||||||
#include "DomainServer.h"
|
#include "DomainServer.h"
|
||||||
|
|
||||||
const quint16 DOMAIN_SERVER_HTTP_PORT = 8080;
|
|
||||||
|
|
||||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
QCoreApplication(argc, argv),
|
QCoreApplication(argc, argv),
|
||||||
_HTTPManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||||
_staticAssignmentHash(),
|
_httpsManager(NULL),
|
||||||
_assignmentQueue(),
|
_allAssignments(),
|
||||||
|
_unfulfilledAssignments(),
|
||||||
_isUsingDTLS(false),
|
_isUsingDTLS(false),
|
||||||
_x509Credentials(NULL),
|
_x509Credentials(NULL),
|
||||||
_dhParams(NULL),
|
_dhParams(NULL),
|
||||||
_priorityCache(NULL),
|
_priorityCache(NULL),
|
||||||
_dtlsSessions()
|
_dtlsSessions(),
|
||||||
|
_oauthProviderURL(),
|
||||||
|
_oauthClientID(),
|
||||||
|
_hostname(),
|
||||||
|
_networkReplyUUIDMap(),
|
||||||
|
_sessionAuthenticationHash()
|
||||||
{
|
{
|
||||||
gnutls_global_init();
|
gnutls_global_init();
|
||||||
|
|
||||||
|
@ -53,22 +58,20 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
|
|
||||||
_argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments());
|
_argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments());
|
||||||
|
|
||||||
if (optionallySetupDTLS()) {
|
if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth()) {
|
||||||
// we either read a certificate and private key or were not passed one, good to load assignments
|
// we either read a certificate and private key or were not passed one, good to load assignments
|
||||||
// and set up the node list
|
// and set up the node list
|
||||||
qDebug() << "Setting up LimitedNodeList and assignments.";
|
qDebug() << "Setting up LimitedNodeList and assignments.";
|
||||||
setupNodeListAndAssignments();
|
setupNodeListAndAssignments();
|
||||||
|
|
||||||
if (_isUsingDTLS) {
|
if (_isUsingDTLS) {
|
||||||
// we're using DTLS and our NodeList socket is good to go, so make the required DTLS changes
|
|
||||||
// DTLS requires that IP_DONTFRAG be set
|
|
||||||
// This is not accessible on some platforms (OS X) so we need to make sure DTLS still works without it
|
|
||||||
|
|
||||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||||
|
|
||||||
// connect our socket to read datagrams received on the DTLS socket
|
// connect our socket to read datagrams received on the DTLS socket
|
||||||
connect(&nodeList->getDTLSSocket(), &QUdpSocket::readyRead, this, &DomainServer::readAvailableDTLSDatagrams);
|
connect(&nodeList->getDTLSSocket(), &QUdpSocket::readyRead, this, &DomainServer::readAvailableDTLSDatagrams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_networkAccessManager = new QNetworkAccessManager(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,44 +89,7 @@ DomainServer::~DomainServer() {
|
||||||
gnutls_global_deinit();
|
gnutls_global_deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DomainServer::optionallySetupDTLS() {
|
bool DomainServer::optionallyReadX509KeyAndCertificate() {
|
||||||
if (readX509KeyAndCertificate()) {
|
|
||||||
if (_x509Credentials) {
|
|
||||||
qDebug() << "Generating Diffie-Hellman parameters.";
|
|
||||||
|
|
||||||
// generate Diffie-Hellman parameters
|
|
||||||
// When short bit length is used, it might be wise to regenerate parameters often.
|
|
||||||
int dhBits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY);
|
|
||||||
|
|
||||||
_dhParams = new gnutls_dh_params_t;
|
|
||||||
gnutls_dh_params_init(_dhParams);
|
|
||||||
gnutls_dh_params_generate2(*_dhParams, dhBits);
|
|
||||||
|
|
||||||
qDebug() << "Successfully generated Diffie-Hellman parameters.";
|
|
||||||
|
|
||||||
// set the D-H paramters on the X509 credentials
|
|
||||||
gnutls_certificate_set_dh_params(*_x509Credentials, *_dhParams);
|
|
||||||
|
|
||||||
// setup the key used for cookie verification
|
|
||||||
_cookieKey = new gnutls_datum_t;
|
|
||||||
gnutls_key_generate(_cookieKey, GNUTLS_COOKIE_KEY_SIZE);
|
|
||||||
|
|
||||||
_priorityCache = new gnutls_priority_t;
|
|
||||||
const char DTLS_PRIORITY_STRING[] = "PERFORMANCE:-VERS-TLS-ALL:+VERS-DTLS1.2:%SERVER_PRECEDENCE";
|
|
||||||
gnutls_priority_init(_priorityCache, DTLS_PRIORITY_STRING, NULL);
|
|
||||||
|
|
||||||
_isUsingDTLS = true;
|
|
||||||
|
|
||||||
qDebug() << "Initial DTLS setup complete.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DomainServer::readX509KeyAndCertificate() {
|
|
||||||
const QString X509_CERTIFICATE_OPTION = "cert";
|
const QString X509_CERTIFICATE_OPTION = "cert";
|
||||||
const QString X509_PRIVATE_KEY_OPTION = "key";
|
const QString X509_PRIVATE_KEY_OPTION = "key";
|
||||||
const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE";
|
const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE";
|
||||||
|
@ -157,6 +123,22 @@ bool DomainServer::readX509KeyAndCertificate() {
|
||||||
|
|
||||||
qDebug() << "Successfully read certificate and private key.";
|
qDebug() << "Successfully read certificate and private key.";
|
||||||
|
|
||||||
|
// we need to also pass this certificate and private key to the HTTPS manager
|
||||||
|
// this is used for Oauth callbacks when authorizing users against a data server
|
||||||
|
|
||||||
|
QFile certFile(certPath);
|
||||||
|
certFile.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
QFile keyFile(keyPath);
|
||||||
|
keyFile.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
QSslCertificate sslCertificate(&certFile);
|
||||||
|
QSslKey privateKey(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, keyPassphraseString.toUtf8());
|
||||||
|
|
||||||
|
_httpsManager = new HTTPSManager(DOMAIN_SERVER_HTTPS_PORT, sslCertificate, privateKey, QString(), this, this);
|
||||||
|
|
||||||
|
qDebug() << "TCP server listening for HTTPS connections on" << DOMAIN_SERVER_HTTPS_PORT;
|
||||||
|
|
||||||
} else if (!certPath.isEmpty() || !keyPath.isEmpty()) {
|
} else if (!certPath.isEmpty() || !keyPath.isEmpty()) {
|
||||||
qDebug() << "Missing certificate or private key. domain-server will now quit.";
|
qDebug() << "Missing certificate or private key. domain-server will now quit.";
|
||||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||||
|
@ -166,6 +148,67 @@ bool DomainServer::readX509KeyAndCertificate() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DomainServer::optionallySetupOAuth() {
|
||||||
|
const QString OAUTH_PROVIDER_URL_OPTION = "oauth-provider";
|
||||||
|
const QString OAUTH_CLIENT_ID_OPTION = "oauth-client-id";
|
||||||
|
const QString OAUTH_CLIENT_SECRET_ENV = "DOMAIN_SERVER_CLIENT_SECRET";
|
||||||
|
const QString REDIRECT_HOSTNAME_OPTION = "hostname";
|
||||||
|
|
||||||
|
_oauthProviderURL = QUrl(_argumentVariantMap.value(OAUTH_PROVIDER_URL_OPTION).toString());
|
||||||
|
_oauthClientID = _argumentVariantMap.value(OAUTH_CLIENT_ID_OPTION).toString();
|
||||||
|
_oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV);
|
||||||
|
_hostname = _argumentVariantMap.value(REDIRECT_HOSTNAME_OPTION).toString();
|
||||||
|
|
||||||
|
if (!_oauthProviderURL.isEmpty() || !_hostname.isEmpty() || !_oauthClientID.isEmpty()) {
|
||||||
|
if (_oauthProviderURL.isEmpty()
|
||||||
|
|| _hostname.isEmpty()
|
||||||
|
|| _oauthClientID.isEmpty()
|
||||||
|
|| _oauthClientSecret.isEmpty()) {
|
||||||
|
qDebug() << "Missing OAuth provider URL, hostname, client ID, or client secret. domain-server will now quit.";
|
||||||
|
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
qDebug() << "OAuth will be used to identify clients using provider at" << _oauthProviderURL.toString();
|
||||||
|
qDebug() << "OAuth Client ID is" << _oauthClientID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DomainServer::optionallySetupDTLS() {
|
||||||
|
if (_x509Credentials) {
|
||||||
|
qDebug() << "Generating Diffie-Hellman parameters.";
|
||||||
|
|
||||||
|
// generate Diffie-Hellman parameters
|
||||||
|
// When short bit length is used, it might be wise to regenerate parameters often.
|
||||||
|
int dhBits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY);
|
||||||
|
|
||||||
|
_dhParams = new gnutls_dh_params_t;
|
||||||
|
gnutls_dh_params_init(_dhParams);
|
||||||
|
gnutls_dh_params_generate2(*_dhParams, dhBits);
|
||||||
|
|
||||||
|
qDebug() << "Successfully generated Diffie-Hellman parameters.";
|
||||||
|
|
||||||
|
// set the D-H paramters on the X509 credentials
|
||||||
|
gnutls_certificate_set_dh_params(*_x509Credentials, *_dhParams);
|
||||||
|
|
||||||
|
// setup the key used for cookie verification
|
||||||
|
_cookieKey = new gnutls_datum_t;
|
||||||
|
gnutls_key_generate(_cookieKey, GNUTLS_COOKIE_KEY_SIZE);
|
||||||
|
|
||||||
|
_priorityCache = new gnutls_priority_t;
|
||||||
|
const char DTLS_PRIORITY_STRING[] = "PERFORMANCE:-VERS-TLS-ALL:+VERS-DTLS1.2:%SERVER_PRECEDENCE";
|
||||||
|
gnutls_priority_init(_priorityCache, DTLS_PRIORITY_STRING, NULL);
|
||||||
|
|
||||||
|
_isUsingDTLS = true;
|
||||||
|
|
||||||
|
qDebug() << "Initial DTLS setup complete.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
||||||
|
|
||||||
const QString CUSTOM_PORT_OPTION = "port";
|
const QString CUSTOM_PORT_OPTION = "port";
|
||||||
|
@ -246,7 +289,8 @@ void DomainServer::parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes)
|
||||||
|
|
||||||
void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment) {
|
void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment) {
|
||||||
qDebug() << "Inserting assignment" << *newAssignment << "to static assignment hash.";
|
qDebug() << "Inserting assignment" << *newAssignment << "to static assignment hash.";
|
||||||
_staticAssignmentHash.insert(newAssignment->getUUID(), SharedAssignmentPointer(newAssignment));
|
newAssignment->setIsStatic(true);
|
||||||
|
_allAssignments.insert(newAssignment->getUUID(), SharedAssignmentPointer(newAssignment));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configArray) {
|
void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configArray) {
|
||||||
|
@ -279,7 +323,9 @@ void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configAr
|
||||||
qDebug() << "URL for script is" << assignmentURL;
|
qDebug() << "URL for script is" << assignmentURL;
|
||||||
|
|
||||||
// scripts passed on CL or via JSON are static - so they are added back to the queue if the node dies
|
// scripts passed on CL or via JSON are static - so they are added back to the queue if the node dies
|
||||||
_assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment));
|
SharedAssignmentPointer sharedScriptAssignment(scriptAssignment);
|
||||||
|
_unfulfilledAssignments.enqueue(sharedScriptAssignment);
|
||||||
|
_allAssignments.insert(sharedScriptAssignment->getUUID(), sharedScriptAssignment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,47 +386,71 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString ALLOWED_ROLES_CONFIG_KEY = "allowed-roles";
|
||||||
|
|
||||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||||
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer << NodeType::ModelServer
|
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer << NodeType::ModelServer
|
||||||
<< NodeType::MetavoxelServer;
|
<< NodeType::MetavoxelServer;
|
||||||
|
|
||||||
void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
|
void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
|
||||||
|
|
||||||
NodeType_t nodeType;
|
NodeType_t nodeType;
|
||||||
HifiSockAddr publicSockAddr, localSockAddr;
|
HifiSockAddr publicSockAddr, localSockAddr;
|
||||||
|
|
||||||
int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr);
|
int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr);
|
||||||
|
|
||||||
QUuid assignmentUUID = uuidFromPacketHeader(packet);
|
QUuid packetUUID = uuidFromPacketHeader(packet);
|
||||||
bool isStaticAssignment = _staticAssignmentHash.contains(assignmentUUID);
|
|
||||||
SharedAssignmentPointer matchingAssignment = SharedAssignmentPointer();
|
// check if this connect request matches an assignment in the queue
|
||||||
|
bool isFulfilledOrUnfulfilledAssignment = _allAssignments.contains(packetUUID);
|
||||||
if (isStaticAssignment) {
|
SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer();
|
||||||
// this is a static assignment, make sure the UUID sent is for an assignment we're actually trying to give out
|
if (isFulfilledOrUnfulfilledAssignment) {
|
||||||
matchingAssignment = matchingQueuedAssignmentForCheckIn(assignmentUUID, nodeType);
|
matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(packetUUID, nodeType);
|
||||||
|
|
||||||
if (matchingAssignment) {
|
|
||||||
// remove the matching assignment from the assignment queue so we don't take the next check in
|
|
||||||
// (if it exists)
|
|
||||||
removeMatchingAssignmentFromQueue(matchingAssignment);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assignmentUUID = QUuid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure this was either not a static assignment or it was and we had a matching one in teh queue
|
if (!matchingQueuedAssignment && !_oauthProviderURL.isEmpty() && _argumentVariantMap.contains(ALLOWED_ROLES_CONFIG_KEY)) {
|
||||||
if ((!isStaticAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) || (isStaticAssignment && matchingAssignment)) {
|
// this is an Agent, and we require authentication so we can compare the user's roles to our list of allowed ones
|
||||||
|
if (_sessionAuthenticationHash.contains(packetUUID)) {
|
||||||
|
if (!_sessionAuthenticationHash.value(packetUUID)) {
|
||||||
|
// we've decided this is a user that isn't allowed in, return out
|
||||||
|
// TODO: provide information to the user so they know why they can't connect
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// we're letting this user in, don't return and remove their UUID from the hash
|
||||||
|
_sessionAuthenticationHash.remove(packetUUID);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we don't know anything about this client
|
||||||
|
// we have an OAuth provider, ask this interface client to auth against it
|
||||||
|
QByteArray oauthRequestByteArray = byteArrayWithPopulatedHeader(PacketTypeDomainOAuthRequest);
|
||||||
|
QDataStream oauthRequestStream(&oauthRequestByteArray, QIODevice::Append);
|
||||||
|
QUrl authorizationURL = packetUUID.isNull() ? oauthAuthorizationURL() : oauthAuthorizationURL(packetUUID);
|
||||||
|
oauthRequestStream << authorizationURL;
|
||||||
|
|
||||||
|
// send this oauth request datagram back to the client
|
||||||
|
LimitedNodeList::getInstance()->writeUnverifiedDatagram(oauthRequestByteArray, senderSockAddr);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!isFulfilledOrUnfulfilledAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType))
|
||||||
|
|| (isFulfilledOrUnfulfilledAssignment && matchingQueuedAssignment)) {
|
||||||
|
// this was either not a static assignment or it was and we had a matching one in the queue
|
||||||
|
|
||||||
// create a new session UUID for this node
|
// create a new session UUID for this node
|
||||||
QUuid nodeUUID = QUuid::createUuid();
|
QUuid nodeUUID = QUuid::createUuid();
|
||||||
|
|
||||||
SharedNodePointer newNode = LimitedNodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType,
|
SharedNodePointer newNode = LimitedNodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType,
|
||||||
publicSockAddr, localSockAddr);
|
publicSockAddr, localSockAddr);
|
||||||
|
|
||||||
// when the newNode is created the linked data is also created
|
// when the newNode is created the linked data is also created
|
||||||
// if this was a static assignment set the UUID, set the sendingSockAddr
|
// if this was a static assignment set the UUID, set the sendingSockAddr
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||||
|
|
||||||
nodeData->setStaticAssignmentUUID(assignmentUUID);
|
if (isFulfilledOrUnfulfilledAssignment) {
|
||||||
|
nodeData->setAssignmentUUID(packetUUID);
|
||||||
|
}
|
||||||
|
|
||||||
nodeData->setSendingSockAddr(senderSockAddr);
|
nodeData->setSendingSockAddr(senderSockAddr);
|
||||||
|
|
||||||
// reply back to the user with a PacketTypeDomainList
|
// reply back to the user with a PacketTypeDomainList
|
||||||
|
@ -388,6 +458,40 @@ void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUrl DomainServer::oauthRedirectURL() {
|
||||||
|
return QString("https://%1:%2/oauth").arg(_hostname).arg(_httpsManager->serverPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString OAUTH_CLIENT_ID_QUERY_KEY = "client_id";
|
||||||
|
const QString OAUTH_REDIRECT_URI_QUERY_KEY = "redirect_uri";
|
||||||
|
|
||||||
|
QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) {
|
||||||
|
// for now these are all interface clients that have a GUI
|
||||||
|
// so just send them back the full authorization URL
|
||||||
|
QUrl authorizationURL = _oauthProviderURL;
|
||||||
|
|
||||||
|
const QString OAUTH_AUTHORIZATION_PATH = "/oauth/authorize";
|
||||||
|
authorizationURL.setPath(OAUTH_AUTHORIZATION_PATH);
|
||||||
|
|
||||||
|
QUrlQuery authorizationQuery;
|
||||||
|
|
||||||
|
authorizationQuery.addQueryItem(OAUTH_CLIENT_ID_QUERY_KEY, _oauthClientID);
|
||||||
|
|
||||||
|
const QString OAUTH_RESPONSE_TYPE_QUERY_KEY = "response_type";
|
||||||
|
const QString OAUTH_REPSONSE_TYPE_QUERY_VALUE = "code";
|
||||||
|
authorizationQuery.addQueryItem(OAUTH_RESPONSE_TYPE_QUERY_KEY, OAUTH_REPSONSE_TYPE_QUERY_VALUE);
|
||||||
|
|
||||||
|
const QString OAUTH_STATE_QUERY_KEY = "state";
|
||||||
|
// create a new UUID that will be the state parameter for oauth authorization AND the new session UUID for that node
|
||||||
|
authorizationQuery.addQueryItem(OAUTH_STATE_QUERY_KEY, uuidStringWithoutCurlyBraces(stateUUID));
|
||||||
|
|
||||||
|
authorizationQuery.addQueryItem(OAUTH_REDIRECT_URI_QUERY_KEY, oauthRedirectURL().toString());
|
||||||
|
|
||||||
|
authorizationURL.setQuery(authorizationQuery);
|
||||||
|
|
||||||
|
return authorizationURL;
|
||||||
|
}
|
||||||
|
|
||||||
int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
||||||
HifiSockAddr& localSockAddr, const QByteArray& packet,
|
HifiSockAddr& localSockAddr, const QByteArray& packet,
|
||||||
const HifiSockAddr& senderSockAddr) {
|
const HifiSockAddr& senderSockAddr) {
|
||||||
|
@ -451,52 +555,54 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
||||||
DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
||||||
int dataMTU = dtlsSession ? (int)gnutls_dtls_get_data_mtu(*dtlsSession->getGnuTLSSession()) : MAX_PACKET_SIZE;
|
int dataMTU = dtlsSession ? (int)gnutls_dtls_get_data_mtu(*dtlsSession->getGnuTLSSession()) : MAX_PACKET_SIZE;
|
||||||
|
|
||||||
// if the node has any interest types, send back those nodes as well
|
if (nodeData->isAuthenticated()) {
|
||||||
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
|
// if this authenticated node has any interest types, send back those nodes as well
|
||||||
|
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
|
||||||
// reset our nodeByteArray and nodeDataStream
|
|
||||||
QByteArray nodeByteArray;
|
|
||||||
QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append);
|
|
||||||
|
|
||||||
if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) {
|
|
||||||
|
|
||||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
// reset our nodeByteArray and nodeDataStream
|
||||||
nodeDataStream << *otherNode.data();
|
QByteArray nodeByteArray;
|
||||||
|
QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append);
|
||||||
|
|
||||||
// pack the secret that these two nodes will use to communicate with each other
|
if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) {
|
||||||
QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID());
|
|
||||||
if (secretUUID.isNull()) {
|
|
||||||
// generate a new secret UUID these two nodes can use
|
|
||||||
secretUUID = QUuid::createUuid();
|
|
||||||
|
|
||||||
// set that on the current Node's sessionSecretHash
|
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||||
nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID);
|
nodeDataStream << *otherNode.data();
|
||||||
|
|
||||||
// set it on the other Node's sessionSecretHash
|
// pack the secret that these two nodes will use to communicate with each other
|
||||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())
|
QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID());
|
||||||
|
if (secretUUID.isNull()) {
|
||||||
|
// generate a new secret UUID these two nodes can use
|
||||||
|
secretUUID = QUuid::createUuid();
|
||||||
|
|
||||||
|
// set that on the current Node's sessionSecretHash
|
||||||
|
nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID);
|
||||||
|
|
||||||
|
// set it on the other Node's sessionSecretHash
|
||||||
|
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())
|
||||||
->getSessionSecretHash().insert(node->getUUID(), secretUUID);
|
->getSessionSecretHash().insert(node->getUUID(), secretUUID);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeDataStream << secretUUID;
|
|
||||||
|
|
||||||
if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) {
|
|
||||||
// we need to break here and start a new packet
|
|
||||||
// so send the current one
|
|
||||||
|
|
||||||
if (!dtlsSession) {
|
|
||||||
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
|
||||||
} else {
|
|
||||||
dtlsSession->writeDatagram(broadcastPacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset the broadcastPacket structure
|
nodeDataStream << secretUUID;
|
||||||
broadcastPacket.resize(numBroadcastPacketLeadBytes);
|
|
||||||
broadcastDataStream.device()->seek(numBroadcastPacketLeadBytes);
|
if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) {
|
||||||
|
// we need to break here and start a new packet
|
||||||
|
// so send the current one
|
||||||
|
|
||||||
|
if (!dtlsSession) {
|
||||||
|
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
||||||
|
} else {
|
||||||
|
dtlsSession->writeDatagram(broadcastPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the broadcastPacket structure
|
||||||
|
broadcastPacket.resize(numBroadcastPacketLeadBytes);
|
||||||
|
broadcastDataStream.device()->seek(numBroadcastPacketLeadBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append the nodeByteArray to the current state of broadcastDataStream
|
||||||
|
broadcastPacket.append(nodeByteArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
// append the nodeByteArray to the current state of broadcastDataStream
|
|
||||||
broadcastPacket.append(nodeByteArray);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,9 +776,7 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS
|
||||||
PacketType requestType = packetTypeForPacket(receivedPacket);
|
PacketType requestType = packetTypeForPacket(receivedPacket);
|
||||||
|
|
||||||
if (requestType == PacketTypeDomainConnectRequest) {
|
if (requestType == PacketTypeDomainConnectRequest) {
|
||||||
// add this node to our NodeList
|
handleConnectRequest(receivedPacket, senderSockAddr);
|
||||||
// and send back session UUID right away
|
|
||||||
addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr);
|
|
||||||
} else if (requestType == PacketTypeDomainListRequest) {
|
} else if (requestType == PacketTypeDomainListRequest) {
|
||||||
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
|
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
|
||||||
|
|
||||||
|
@ -738,7 +842,8 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
|
||||||
nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp());
|
nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp());
|
||||||
|
|
||||||
// if the node has pool information, add it
|
// if the node has pool information, add it
|
||||||
SharedAssignmentPointer matchingAssignment = _staticAssignmentHash.value(node->getUUID());
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID());
|
||||||
if (matchingAssignment) {
|
if (matchingAssignment) {
|
||||||
nodeJson[JSON_KEY_POOL] = matchingAssignment->getPool();
|
nodeJson[JSON_KEY_POOL] = matchingAssignment->getPool();
|
||||||
}
|
}
|
||||||
|
@ -774,9 +879,11 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
|
|
||||||
// enumerate the NodeList to find the assigned nodes
|
// enumerate the NodeList to find the assigned nodes
|
||||||
foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) {
|
foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) {
|
||||||
if (_staticAssignmentHash.value(node->getUUID())) {
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
|
||||||
|
if (!nodeData->getAssignmentUUID().isNull()) {
|
||||||
// add the node using the UUID as the key
|
// add the node using the UUID as the key
|
||||||
QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());
|
QString uuidString = uuidStringWithoutCurlyBraces(nodeData->getAssignmentUUID());
|
||||||
assignedNodesJSON[uuidString] = jsonObjectForNode(node);
|
assignedNodesJSON[uuidString] = jsonObjectForNode(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -786,7 +893,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
QJsonObject queuedAssignmentsJSON;
|
QJsonObject queuedAssignmentsJSON;
|
||||||
|
|
||||||
// add the queued but unfilled assignments to the json
|
// add the queued but unfilled assignments to the json
|
||||||
foreach(const SharedAssignmentPointer& assignment, _assignmentQueue) {
|
foreach(const SharedAssignmentPointer& assignment, _unfulfilledAssignments) {
|
||||||
QJsonObject queuedAssignmentJSON;
|
QJsonObject queuedAssignmentJSON;
|
||||||
|
|
||||||
QString uuidString = uuidStringWithoutCurlyBraces(assignment->getUUID());
|
QString uuidString = uuidStringWithoutCurlyBraces(assignment->getUUID());
|
||||||
|
@ -903,7 +1010,9 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
.arg(newPath).arg(assignmentPool == emptyPool ? "" : " - pool is " + assignmentPool));
|
.arg(newPath).arg(assignmentPool == emptyPool ? "" : " - pool is " + assignmentPool));
|
||||||
|
|
||||||
// add the script assigment to the assignment queue
|
// add the script assigment to the assignment queue
|
||||||
_assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment));
|
SharedAssignmentPointer sharedScriptedAssignment(scriptAssignment);
|
||||||
|
_unfulfilledAssignments.enqueue(sharedScriptedAssignment);
|
||||||
|
_allAssignments.insert(sharedScriptedAssignment->getUUID(), sharedScriptedAssignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
// respond with a 200 code for successful upload
|
// respond with a 200 code for successful upload
|
||||||
|
@ -950,6 +1059,114 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url) {
|
||||||
|
const QString URI_OAUTH = "/oauth";
|
||||||
|
if (url.path() == URI_OAUTH) {
|
||||||
|
|
||||||
|
QUrlQuery codeURLQuery(url);
|
||||||
|
|
||||||
|
const QString CODE_QUERY_KEY = "code";
|
||||||
|
QString authorizationCode = codeURLQuery.queryItemValue(CODE_QUERY_KEY);
|
||||||
|
|
||||||
|
const QString STATE_QUERY_KEY = "state";
|
||||||
|
QUuid stateUUID = QUuid(codeURLQuery.queryItemValue(STATE_QUERY_KEY));
|
||||||
|
|
||||||
|
|
||||||
|
if (!authorizationCode.isEmpty() && !stateUUID.isNull()) {
|
||||||
|
// fire off a request with this code and state to get an access token for the user
|
||||||
|
|
||||||
|
const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token";
|
||||||
|
QUrl tokenRequestUrl = _oauthProviderURL;
|
||||||
|
tokenRequestUrl.setPath(OAUTH_TOKEN_REQUEST_PATH);
|
||||||
|
|
||||||
|
const QString OAUTH_GRANT_TYPE_POST_STRING = "grant_type=authorization_code";
|
||||||
|
QString tokenPostBody = OAUTH_GRANT_TYPE_POST_STRING;
|
||||||
|
tokenPostBody += QString("&code=%1&redirect_uri=%2&client_id=%3&client_secret=%4")
|
||||||
|
.arg(authorizationCode, oauthRedirectURL().toString(), _oauthClientID, _oauthClientSecret);
|
||||||
|
|
||||||
|
QNetworkRequest tokenRequest(tokenRequestUrl);
|
||||||
|
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
|
QNetworkReply* tokenReply = _networkAccessManager->post(tokenRequest, tokenPostBody.toLocal8Bit());
|
||||||
|
|
||||||
|
qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID);
|
||||||
|
|
||||||
|
// insert this to our pending token replies so we can associate the returned access token with the right UUID
|
||||||
|
_networkReplyUUIDMap.insert(tokenReply, stateUUID);
|
||||||
|
|
||||||
|
connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleTokenRequestFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
// respond with a 200 code indicating that login is complete
|
||||||
|
connection->respond(HTTPConnection::StatusCode200);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token";
|
||||||
|
|
||||||
|
void DomainServer::handleTokenRequestFinished() {
|
||||||
|
QNetworkReply* networkReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||||
|
QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply);
|
||||||
|
|
||||||
|
if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) {
|
||||||
|
// pull the access token from the returned JSON and store it with the matching session UUID
|
||||||
|
QJsonDocument returnedJSON = QJsonDocument::fromJson(networkReply->readAll());
|
||||||
|
QString accessToken = returnedJSON.object()[OAUTH_JSON_ACCESS_TOKEN_KEY].toString();
|
||||||
|
|
||||||
|
qDebug() << "Received access token for user with UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID);
|
||||||
|
|
||||||
|
// fire off a request to get this user's identity so we can see if we will let them in
|
||||||
|
QUrl profileURL = _oauthProviderURL;
|
||||||
|
profileURL.setPath("/api/v1/users/profile");
|
||||||
|
profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken));
|
||||||
|
|
||||||
|
QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL));
|
||||||
|
|
||||||
|
qDebug() << "Requesting access token for user with session UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID);
|
||||||
|
|
||||||
|
connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished);
|
||||||
|
|
||||||
|
_networkReplyUUIDMap.insert(profileReply, matchingSessionUUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainServer::handleProfileRequestFinished() {
|
||||||
|
QNetworkReply* networkReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||||
|
QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply);
|
||||||
|
|
||||||
|
if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) {
|
||||||
|
QJsonDocument profileJSON = QJsonDocument::fromJson(networkReply->readAll());
|
||||||
|
|
||||||
|
if (profileJSON.object()["status"].toString() == "success") {
|
||||||
|
// pull the user roles from the response
|
||||||
|
QJsonArray userRolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray();
|
||||||
|
|
||||||
|
QJsonArray allowedRolesArray = _argumentVariantMap.value(ALLOWED_ROLES_CONFIG_KEY).toJsonValue().toArray();
|
||||||
|
|
||||||
|
bool shouldAllowUserToConnect = false;
|
||||||
|
|
||||||
|
foreach(const QJsonValue& roleValue, userRolesArray) {
|
||||||
|
if (allowedRolesArray.contains(roleValue)) {
|
||||||
|
// the user has a role that lets them in
|
||||||
|
// set the bool to true and break
|
||||||
|
shouldAllowUserToConnect = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Confirmed authentication state for user" << uuidStringWithoutCurlyBraces(matchingSessionUUID)
|
||||||
|
<< "-" << shouldAllowUserToConnect;
|
||||||
|
|
||||||
|
// insert this UUID and a flag that indicates if they are allowed to connect
|
||||||
|
_sessionAuthenticationHash.insert(matchingSessionUUID, shouldAllowUserToConnect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) {
|
void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) {
|
||||||
QUuid oldUUID = assignment->getUUID();
|
QUuid oldUUID = assignment->getUUID();
|
||||||
assignment->resetUUID();
|
assignment->resetUUID();
|
||||||
|
@ -963,13 +1180,8 @@ void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer&
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the static assignment back under the right UUID, and to the queue
|
// add the static assignment back under the right UUID, and to the queue
|
||||||
_staticAssignmentHash.insert(assignment->getUUID(), assignment);
|
_allAssignments.insert(assignment->getUUID(), assignment);
|
||||||
|
_unfulfilledAssignments.enqueue(assignment);
|
||||||
_assignmentQueue.enqueue(assignment);
|
|
||||||
|
|
||||||
// remove the old assignment from the _staticAssignmentHash
|
|
||||||
// this must be done last so copies are created before the assignment passed by reference is killed
|
|
||||||
_staticAssignmentHash.remove(oldUUID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::nodeAdded(SharedNodePointer node) {
|
void DomainServer::nodeAdded(SharedNodePointer node) {
|
||||||
|
@ -983,10 +1195,10 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||||
|
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
|
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
|
||||||
if (!nodeData->getStaticAssignmentUUID().isNull()) {
|
if (!nodeData->getAssignmentUUID().isNull()) {
|
||||||
SharedAssignmentPointer matchedAssignment = _staticAssignmentHash.value(nodeData->getStaticAssignmentUUID());
|
SharedAssignmentPointer matchedAssignment = _allAssignments.take(nodeData->getAssignmentUUID());
|
||||||
|
|
||||||
if (matchedAssignment) {
|
if (matchedAssignment && matchedAssignment->isStatic()) {
|
||||||
refreshStaticAssignmentAndAddToQueue(matchedAssignment);
|
refreshStaticAssignmentAndAddToQueue(matchedAssignment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1010,11 +1222,11 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) {
|
SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) {
|
||||||
QQueue<SharedAssignmentPointer>::iterator i = _assignmentQueue.begin();
|
QQueue<SharedAssignmentPointer>::iterator i = _unfulfilledAssignments.begin();
|
||||||
|
|
||||||
while (i != _assignmentQueue.end()) {
|
while (i != _unfulfilledAssignments.end()) {
|
||||||
if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) {
|
if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) {
|
||||||
return _assignmentQueue.takeAt(i - _assignmentQueue.begin());
|
return _unfulfilledAssignments.takeAt(i - _unfulfilledAssignments.begin());
|
||||||
} else {
|
} else {
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
@ -1026,9 +1238,9 @@ SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const Q
|
||||||
SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assignment& requestAssignment) {
|
SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assignment& requestAssignment) {
|
||||||
// this is an unassigned client talking to us directly for an assignment
|
// this is an unassigned client talking to us directly for an assignment
|
||||||
// go through our queue and see if there are any assignments to give out
|
// go through our queue and see if there are any assignments to give out
|
||||||
QQueue<SharedAssignmentPointer>::iterator sharedAssignment = _assignmentQueue.begin();
|
QQueue<SharedAssignmentPointer>::iterator sharedAssignment = _unfulfilledAssignments.begin();
|
||||||
|
|
||||||
while (sharedAssignment != _assignmentQueue.end()) {
|
while (sharedAssignment != _unfulfilledAssignments.end()) {
|
||||||
Assignment* assignment = sharedAssignment->data();
|
Assignment* assignment = sharedAssignment->data();
|
||||||
bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes;
|
bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes;
|
||||||
bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType();
|
bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType();
|
||||||
|
@ -1038,16 +1250,12 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig
|
||||||
if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) {
|
if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) {
|
||||||
|
|
||||||
// remove the assignment from the queue
|
// remove the assignment from the queue
|
||||||
SharedAssignmentPointer deployableAssignment = _assignmentQueue.takeAt(sharedAssignment
|
SharedAssignmentPointer deployableAssignment = _unfulfilledAssignments.takeAt(sharedAssignment
|
||||||
- _assignmentQueue.begin());
|
- _unfulfilledAssignments.begin());
|
||||||
|
|
||||||
if (deployableAssignment->getType() != Assignment::AgentType
|
// until we get a connection for this assignment
|
||||||
|| _staticAssignmentHash.contains(deployableAssignment->getUUID())) {
|
// put assignment back in queue but stick it at the back so the others have a chance to go out
|
||||||
// this is a static assignment
|
_unfulfilledAssignments.enqueue(deployableAssignment);
|
||||||
// until we get a check-in from that GUID
|
|
||||||
// put assignment back in queue but stick it at the back so the others have a chance to go out
|
|
||||||
_assignmentQueue.enqueue(deployableAssignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop looping, we've handed out an assignment
|
// stop looping, we've handed out an assignment
|
||||||
return deployableAssignment;
|
return deployableAssignment;
|
||||||
|
@ -1061,10 +1269,10 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment) {
|
void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment) {
|
||||||
QQueue<SharedAssignmentPointer>::iterator potentialMatchingAssignment = _assignmentQueue.begin();
|
QQueue<SharedAssignmentPointer>::iterator potentialMatchingAssignment = _unfulfilledAssignments.begin();
|
||||||
while (potentialMatchingAssignment != _assignmentQueue.end()) {
|
while (potentialMatchingAssignment != _unfulfilledAssignments.end()) {
|
||||||
if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) {
|
if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) {
|
||||||
_assignmentQueue.erase(potentialMatchingAssignment);
|
_unfulfilledAssignments.erase(potentialMatchingAssignment);
|
||||||
|
|
||||||
// we matched and removed an assignment, bail out
|
// we matched and removed an assignment, bail out
|
||||||
break;
|
break;
|
||||||
|
@ -1078,7 +1286,7 @@ void DomainServer::addStaticAssignmentsToQueue() {
|
||||||
|
|
||||||
// if the domain-server has just restarted,
|
// if the domain-server has just restarted,
|
||||||
// check if there are static assignments that we need to throw into the assignment queue
|
// check if there are static assignments that we need to throw into the assignment queue
|
||||||
QHash<QUuid, SharedAssignmentPointer> staticHashCopy = _staticAssignmentHash;
|
QHash<QUuid, SharedAssignmentPointer> staticHashCopy = _allAssignments;
|
||||||
QHash<QUuid, SharedAssignmentPointer>::iterator staticAssignment = staticHashCopy.begin();
|
QHash<QUuid, SharedAssignmentPointer>::iterator staticAssignment = staticHashCopy.begin();
|
||||||
while (staticAssignment != staticHashCopy.end()) {
|
while (staticAssignment != staticHashCopy.end()) {
|
||||||
// add any of the un-matched static assignments to the queue
|
// add any of the un-matched static assignments to the queue
|
||||||
|
|
|
@ -23,20 +23,21 @@
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
|
|
||||||
#include <Assignment.h>
|
#include <Assignment.h>
|
||||||
#include <HTTPManager.h>
|
#include <HTTPSConnection.h>
|
||||||
#include <LimitedNodeList.h>
|
#include <LimitedNodeList.h>
|
||||||
|
|
||||||
#include "DTLSServerSession.h"
|
#include "DTLSServerSession.h"
|
||||||
|
|
||||||
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
|
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
|
||||||
|
|
||||||
class DomainServer : public QCoreApplication, public HTTPRequestHandler {
|
class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DomainServer(int argc, char* argv[]);
|
DomainServer(int argc, char* argv[]);
|
||||||
~DomainServer();
|
~DomainServer();
|
||||||
|
|
||||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
|
bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url);
|
||||||
|
|
||||||
void exit(int retCode = 0);
|
void exit(int retCode = 0);
|
||||||
|
|
||||||
|
@ -52,12 +53,13 @@ private slots:
|
||||||
void readAvailableDTLSDatagrams();
|
void readAvailableDTLSDatagrams();
|
||||||
private:
|
private:
|
||||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||||
|
bool optionallySetupOAuth();
|
||||||
bool optionallySetupDTLS();
|
bool optionallySetupDTLS();
|
||||||
bool readX509KeyAndCertificate();
|
bool optionallyReadX509KeyAndCertificate();
|
||||||
|
|
||||||
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||||
|
|
||||||
void addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||||
int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
||||||
HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||||
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
|
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
|
||||||
|
@ -76,13 +78,20 @@ private:
|
||||||
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
|
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
|
||||||
void addStaticAssignmentsToQueue();
|
void addStaticAssignmentsToQueue();
|
||||||
|
|
||||||
|
QUrl oauthRedirectURL();
|
||||||
|
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());
|
||||||
|
|
||||||
|
void handleTokenRequestFinished();
|
||||||
|
void handleProfileRequestFinished();
|
||||||
|
|
||||||
QJsonObject jsonForSocket(const HifiSockAddr& socket);
|
QJsonObject jsonForSocket(const HifiSockAddr& socket);
|
||||||
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
|
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
|
||||||
|
|
||||||
HTTPManager _HTTPManager;
|
HTTPManager _httpManager;
|
||||||
|
HTTPSManager* _httpsManager;
|
||||||
|
|
||||||
QHash<QUuid, SharedAssignmentPointer> _staticAssignmentHash;
|
QHash<QUuid, SharedAssignmentPointer> _allAssignments;
|
||||||
QQueue<SharedAssignmentPointer> _assignmentQueue;
|
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
||||||
|
|
||||||
QVariantMap _argumentVariantMap;
|
QVariantMap _argumentVariantMap;
|
||||||
|
|
||||||
|
@ -93,6 +102,15 @@ private:
|
||||||
gnutls_priority_t* _priorityCache;
|
gnutls_priority_t* _priorityCache;
|
||||||
|
|
||||||
QHash<HifiSockAddr, DTLSServerSession*> _dtlsSessions;
|
QHash<HifiSockAddr, DTLSServerSession*> _dtlsSessions;
|
||||||
|
|
||||||
|
QNetworkAccessManager* _networkAccessManager;
|
||||||
|
|
||||||
|
QUrl _oauthProviderURL;
|
||||||
|
QString _oauthClientID;
|
||||||
|
QString _oauthClientSecret;
|
||||||
|
QString _hostname;
|
||||||
|
QMap<QNetworkReply*, QUuid> _networkReplyUUIDMap;
|
||||||
|
QHash<QUuid, bool> _sessionAuthenticationHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_DomainServer_h
|
#endif // hifi_DomainServer_h
|
||||||
|
|
|
@ -19,9 +19,10 @@
|
||||||
|
|
||||||
DomainServerNodeData::DomainServerNodeData() :
|
DomainServerNodeData::DomainServerNodeData() :
|
||||||
_sessionSecretHash(),
|
_sessionSecretHash(),
|
||||||
_staticAssignmentUUID(),
|
_assignmentUUID(),
|
||||||
_statsJSONObject(),
|
_statsJSONObject(),
|
||||||
_sendingSockAddr()
|
_sendingSockAddr(),
|
||||||
|
_isAuthenticated(true)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,20 +27,24 @@ public:
|
||||||
|
|
||||||
void parseJSONStatsPacket(const QByteArray& statsPacket);
|
void parseJSONStatsPacket(const QByteArray& statsPacket);
|
||||||
|
|
||||||
void setStaticAssignmentUUID(const QUuid& staticAssignmentUUID) { _staticAssignmentUUID = staticAssignmentUUID; }
|
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
||||||
const QUuid& getStaticAssignmentUUID() const { return _staticAssignmentUUID; }
|
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
||||||
|
|
||||||
void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; }
|
void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; }
|
||||||
const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; }
|
const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; }
|
||||||
|
|
||||||
|
void setIsAuthenticated(bool isAuthenticated) { _isAuthenticated = isAuthenticated; }
|
||||||
|
bool isAuthenticated() const { return _isAuthenticated; }
|
||||||
|
|
||||||
QHash<QUuid, QUuid>& getSessionSecretHash() { return _sessionSecretHash; }
|
QHash<QUuid, QUuid>& getSessionSecretHash() { return _sessionSecretHash; }
|
||||||
private:
|
private:
|
||||||
QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject);
|
QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject);
|
||||||
|
|
||||||
QHash<QUuid, QUuid> _sessionSecretHash;
|
QHash<QUuid, QUuid> _sessionSecretHash;
|
||||||
QUuid _staticAssignmentUUID;
|
QUuid _assignmentUUID;
|
||||||
QJsonObject _statsJSONObject;
|
QJsonObject _statsJSONObject;
|
||||||
HifiSockAddr _sendingSockAddr;
|
HifiSockAddr _sendingSockAddr;
|
||||||
|
bool _isAuthenticated;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_DomainServerNodeData_h
|
#endif // hifi_DomainServerNodeData_h
|
||||||
|
|
342
examples/editModels.js
Normal file
342
examples/editModels.js
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
//
|
||||||
|
// editModels.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Clément Brisset on 4/24/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
|
||||||
|
//
|
||||||
|
|
||||||
|
var LASER_WIDTH = 4;
|
||||||
|
var LASER_COLOR = { red: 255, green: 0, blue: 0 };
|
||||||
|
var LASER_LENGTH_FACTOR = 1.5;
|
||||||
|
|
||||||
|
var LEFT = 0;
|
||||||
|
var RIGHT = 1;
|
||||||
|
|
||||||
|
function controller(wichSide) {
|
||||||
|
this.side = wichSide;
|
||||||
|
this.palm = 2 * wichSide;
|
||||||
|
this.tip = 2 * wichSide + 1;
|
||||||
|
this.trigger = wichSide;
|
||||||
|
|
||||||
|
this.oldPalmPosition = Controller.getSpatialControlPosition(this.palm);
|
||||||
|
this.palmPosition = Controller.getSpatialControlPosition(this.palm);
|
||||||
|
|
||||||
|
this.oldTipPosition = Controller.getSpatialControlPosition(this.tip);
|
||||||
|
this.tipPosition = Controller.getSpatialControlPosition(this.tip);
|
||||||
|
|
||||||
|
this.oldUp = Controller.getSpatialControlNormal(this.palm);
|
||||||
|
this.up = this.oldUp;
|
||||||
|
|
||||||
|
this.oldFront = Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition));
|
||||||
|
this.front = this.oldFront;
|
||||||
|
|
||||||
|
this.oldRight = Vec3.cross(this.front, this.up);
|
||||||
|
this.right = this.oldRight;
|
||||||
|
|
||||||
|
this.oldRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
|
||||||
|
this.rotation = this.oldRotation;
|
||||||
|
|
||||||
|
this.triggerValue = Controller.getTriggerValue(this.trigger);
|
||||||
|
|
||||||
|
this.pressed = false; // is trigger pressed
|
||||||
|
this.pressing = false; // is trigger being pressed (is pressed now but wasn't previously)
|
||||||
|
|
||||||
|
this.grabbing = false;
|
||||||
|
this.modelID;
|
||||||
|
|
||||||
|
this.laser = Overlays.addOverlay("line3d", {
|
||||||
|
position: this.palmPosition,
|
||||||
|
end: this.tipPosition,
|
||||||
|
color: LASER_COLOR,
|
||||||
|
alpha: 1,
|
||||||
|
visible: false,
|
||||||
|
lineWidth: LASER_WIDTH
|
||||||
|
});
|
||||||
|
|
||||||
|
this.guideScale = 0.02;
|
||||||
|
this.ball = Overlays.addOverlay("sphere", {
|
||||||
|
position: this.palmPosition,
|
||||||
|
size: this.guideScale,
|
||||||
|
solid: true,
|
||||||
|
color: { red: 0, green: 255, blue: 0 },
|
||||||
|
alpha: 1,
|
||||||
|
visible: false,
|
||||||
|
});
|
||||||
|
this.leftRight = Overlays.addOverlay("line3d", {
|
||||||
|
position: this.palmPosition,
|
||||||
|
end: this.tipPosition,
|
||||||
|
color: { red: 0, green: 0, blue: 255 },
|
||||||
|
alpha: 1,
|
||||||
|
visible: false,
|
||||||
|
lineWidth: LASER_WIDTH
|
||||||
|
});
|
||||||
|
this.topDown = Overlays.addOverlay("line3d", {
|
||||||
|
position: this.palmPosition,
|
||||||
|
end: this.tipPosition,
|
||||||
|
color: { red: 0, green: 0, blue: 255 },
|
||||||
|
alpha: 1,
|
||||||
|
visible: false,
|
||||||
|
lineWidth: LASER_WIDTH
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.grab = function (modelID) {
|
||||||
|
if (!modelID.isKnownID) {
|
||||||
|
var identify = Models.identifyModel(modelID);
|
||||||
|
if (!identify.isKnownID) {
|
||||||
|
print("Unknown ID " + identify.id + "(grab)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modelID = identify;
|
||||||
|
}
|
||||||
|
print("Grabbing " + modelID.id);
|
||||||
|
this.grabbing = true;
|
||||||
|
this.modelID = modelID;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.release = function () {
|
||||||
|
this.grabbing = false;
|
||||||
|
this.modelID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkTrigger = function () {
|
||||||
|
if (this.triggerValue > 0.9) {
|
||||||
|
if (this.pressed) {
|
||||||
|
this.pressing = false;
|
||||||
|
} else {
|
||||||
|
this.pressing = true;
|
||||||
|
}
|
||||||
|
this.pressed = true;
|
||||||
|
} else {
|
||||||
|
this.pressing = false;
|
||||||
|
this.pressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.moveLaser = function () {
|
||||||
|
var endPosition = Vec3.sum(this.palmPosition, Vec3.multiply(this.front, LASER_LENGTH_FACTOR));
|
||||||
|
|
||||||
|
Overlays.editOverlay(this.laser, {
|
||||||
|
position: this.palmPosition,
|
||||||
|
end: endPosition,
|
||||||
|
visible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Overlays.editOverlay(this.ball, {
|
||||||
|
position: endPosition,
|
||||||
|
visible: true
|
||||||
|
});
|
||||||
|
Overlays.editOverlay(this.leftRight, {
|
||||||
|
position: Vec3.sum(endPosition, Vec3.multiply(this.right, 2 * this.guideScale)),
|
||||||
|
end: Vec3.sum(endPosition, Vec3.multiply(this.right, -2 * this.guideScale)),
|
||||||
|
visible: true
|
||||||
|
});
|
||||||
|
Overlays.editOverlay(this.topDown, {position: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)),
|
||||||
|
end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)),
|
||||||
|
visible: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkModel = function (modelID) {
|
||||||
|
if (!modelID.isKnownID) {
|
||||||
|
var identify = Models.identifyModel(modelID);
|
||||||
|
if (!identify.isKnownID) {
|
||||||
|
print("Unknown ID " + identify.id + "(checkModel)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modelID = identify;
|
||||||
|
}
|
||||||
|
// P P - Model
|
||||||
|
// /| A - Palm
|
||||||
|
// / | d B - unit vector toward tip
|
||||||
|
// / | X - base of the perpendicular line
|
||||||
|
// A---X----->B d - distance fom axis
|
||||||
|
// x x - distance from A
|
||||||
|
//
|
||||||
|
// |X-A| = (P-A).B
|
||||||
|
// X == A + ((P-A).B)B
|
||||||
|
// d = |P-X|
|
||||||
|
|
||||||
|
var A = this.palmPosition;
|
||||||
|
var B = this.front;
|
||||||
|
var P = Models.getModelProperties(modelID).position;
|
||||||
|
|
||||||
|
this.x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||||
|
this.y = Vec3.dot(Vec3.subtract(P, A), this.up);
|
||||||
|
this.z = Vec3.dot(Vec3.subtract(P, A), this.right);
|
||||||
|
var X = Vec3.sum(A, Vec3.multiply(B, this.x));
|
||||||
|
var d = Vec3.length(Vec3.subtract(P, X));
|
||||||
|
|
||||||
|
// Vec3.print("A: ", A);
|
||||||
|
// Vec3.print("B: ", B);
|
||||||
|
// Vec3.print("Particle pos: ", P);
|
||||||
|
// print("d: " + d + ", x: " + this.x);
|
||||||
|
if (d < Models.getModelProperties(modelID).radius && 0 < this.x && this.x < LASER_LENGTH_FACTOR) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.update = function () {
|
||||||
|
this.oldPalmPosition = this.palmPosition;
|
||||||
|
this.oldTipPosition = this.tipPosition;
|
||||||
|
this.palmPosition = Controller.getSpatialControlPosition(this.palm);
|
||||||
|
this.tipPosition = Controller.getSpatialControlPosition(this.tip);
|
||||||
|
|
||||||
|
this.oldUp = this.up;
|
||||||
|
this.up = Vec3.normalize(Controller.getSpatialControlNormal(this.palm));
|
||||||
|
|
||||||
|
this.oldFront = this.front;
|
||||||
|
this.front = Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition));
|
||||||
|
|
||||||
|
this.oldRight = this.right;
|
||||||
|
this.right = Vec3.normalize(Vec3.cross(this.front, this.up));
|
||||||
|
|
||||||
|
this.oldRotation = this.rotation;
|
||||||
|
this.rotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
|
||||||
|
|
||||||
|
this.triggerValue = Controller.getTriggerValue(this.trigger);
|
||||||
|
|
||||||
|
this.checkTrigger();
|
||||||
|
|
||||||
|
if (this.pressing) {
|
||||||
|
Vec3.print("Looking at: ", this.palmPosition);
|
||||||
|
var foundModels = Models.findModels(this.palmPosition, LASER_LENGTH_FACTOR);
|
||||||
|
for (var i = 0; i < foundModels.length; i++) {
|
||||||
|
print("Model found ID (" + foundModels[i].id + ")");
|
||||||
|
if (this.checkModel(foundModels[i])) {
|
||||||
|
if (this.grab(foundModels[i])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.pressed && this.grabbing) {
|
||||||
|
// release if trigger not pressed anymore.
|
||||||
|
this.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.moveLaser();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cleanup = function () {
|
||||||
|
Overlays.deleteOverlay(this.laser);
|
||||||
|
Overlays.deleteOverlay(this.ball);
|
||||||
|
Overlays.deleteOverlay(this.leftRight);
|
||||||
|
Overlays.deleteOverlay(this.topDown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var leftController = new controller(LEFT);
|
||||||
|
var rightController = new controller(RIGHT);
|
||||||
|
|
||||||
|
function moveModels() {
|
||||||
|
if (leftController.grabbing) {
|
||||||
|
if (rightController.grabbing) {
|
||||||
|
var properties = Models.getModelProperties(leftController.modelID);
|
||||||
|
|
||||||
|
var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x));
|
||||||
|
var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x));
|
||||||
|
|
||||||
|
var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5);
|
||||||
|
var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint));
|
||||||
|
|
||||||
|
|
||||||
|
var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x));
|
||||||
|
var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x));
|
||||||
|
|
||||||
|
var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5);
|
||||||
|
var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint));
|
||||||
|
|
||||||
|
var ratio = length / oldLength;
|
||||||
|
|
||||||
|
var newPosition = Vec3.sum(middle,
|
||||||
|
Vec3.multiply(Vec3.subtract(properties.position, oldMiddle), ratio));
|
||||||
|
Vec3.print("Ratio : " + ratio + " New position: ", newPosition);
|
||||||
|
var rotation = Quat.multiply(leftController.rotation,
|
||||||
|
Quat.inverse(leftController.oldRotation));
|
||||||
|
rotation = Quat.multiply(rotation, properties.modelRotation);
|
||||||
|
|
||||||
|
Models.editModel(leftController.modelID, {
|
||||||
|
position: newPosition,
|
||||||
|
//modelRotation: rotation,
|
||||||
|
radius: properties.radius * ratio
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
var newPosition = Vec3.sum(leftController.palmPosition,
|
||||||
|
Vec3.multiply(leftController.front, leftController.x));
|
||||||
|
newPosition = Vec3.sum(newPosition,
|
||||||
|
Vec3.multiply(leftController.up, leftController.y));
|
||||||
|
newPosition = Vec3.sum(newPosition,
|
||||||
|
Vec3.multiply(leftController.right, leftController.z));
|
||||||
|
|
||||||
|
var rotation = Quat.multiply(leftController.rotation,
|
||||||
|
Quat.inverse(leftController.oldRotation));
|
||||||
|
rotation = Quat.multiply(rotation,
|
||||||
|
Models.getModelProperties(leftController.modelID).modelRotation);
|
||||||
|
|
||||||
|
Models.editModel(leftController.modelID, {
|
||||||
|
position: newPosition,
|
||||||
|
modelRotation: rotation
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (rightController.grabbing) {
|
||||||
|
var newPosition = Vec3.sum(rightController.palmPosition,
|
||||||
|
Vec3.multiply(rightController.front, rightController.x));
|
||||||
|
newPosition = Vec3.sum(newPosition,
|
||||||
|
Vec3.multiply(rightController.up, rightController.y));
|
||||||
|
newPosition = Vec3.sum(newPosition,
|
||||||
|
Vec3.multiply(rightController.right, rightController.z));
|
||||||
|
|
||||||
|
var rotation = Quat.multiply(rightController.rotation,
|
||||||
|
Quat.inverse(rightController.oldRotation));
|
||||||
|
rotation = Quat.multiply(rotation,
|
||||||
|
Models.getModelProperties(rightController.modelID).modelRotation);
|
||||||
|
|
||||||
|
Models.editModel(rightController.modelID, {
|
||||||
|
position: newPosition,
|
||||||
|
modelRotation: rotation
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkController(deltaTime) {
|
||||||
|
var numberOfButtons = Controller.getNumberOfButtons();
|
||||||
|
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||||
|
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||||
|
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||||
|
|
||||||
|
// this is expected for hydras
|
||||||
|
if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
|
||||||
|
//print("no hydra connected?");
|
||||||
|
return; // bail if no hydra
|
||||||
|
}
|
||||||
|
|
||||||
|
leftController.update();
|
||||||
|
rightController.update();
|
||||||
|
moveModels();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scriptEnding() {
|
||||||
|
leftController.cleanup();
|
||||||
|
rightController.cleanup();
|
||||||
|
}
|
||||||
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
|
||||||
|
// register the call back so it fires before each data send
|
||||||
|
Script.update.connect(checkController);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -81,9 +81,10 @@
|
||||||
#include "scripting/LocationScriptingInterface.h"
|
#include "scripting/LocationScriptingInterface.h"
|
||||||
|
|
||||||
#include "ui/InfoView.h"
|
#include "ui/InfoView.h"
|
||||||
|
#include "ui/OAuthWebViewHandler.h"
|
||||||
#include "ui/Snapshot.h"
|
#include "ui/Snapshot.h"
|
||||||
#include "ui/TextRenderer.h"
|
|
||||||
#include "ui/Stats.h"
|
#include "ui/Stats.h"
|
||||||
|
#include "ui/TextRenderer.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -361,12 +362,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
QMutexLocker locker(&_settingsMutex);
|
QMutexLocker locker(&_settingsMutex);
|
||||||
_previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString();
|
_previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(_window, &MainWindow::windowGeometryChanged,
|
connect(_window, &MainWindow::windowGeometryChanged,
|
||||||
_runningScriptsWidget, &RunningScriptsWidget::setBoundary);
|
_runningScriptsWidget, &RunningScriptsWidget::setBoundary);
|
||||||
|
|
||||||
//When -url in command line, teleport to location
|
//When -url in command line, teleport to location
|
||||||
urlGoTo(argc, constArgv);
|
urlGoTo(argc, constArgv);
|
||||||
|
|
||||||
|
// call the OAuthWebviewHandler static getter so that its instance lives in our thread
|
||||||
|
OAuthWebViewHandler::getInstance();
|
||||||
|
// make sure the High Fidelity root CA is in our list of trusted certs
|
||||||
|
OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
|
@ -555,41 +561,6 @@ void Application::paintGL() {
|
||||||
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
|
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
|
||||||
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
|
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
|
||||||
|
|
||||||
glm::vec3 planeNormal = _myCamera.getTargetRotation() * IDENTITY_FRONT;
|
|
||||||
const float BASE_PUSHBACK_RADIUS = 0.25f;
|
|
||||||
float pushbackRadius = _myCamera.getNearClip() + _myAvatar->getScale() * BASE_PUSHBACK_RADIUS;
|
|
||||||
glm::vec4 plane(planeNormal, -glm::dot(planeNormal, _myCamera.getTargetPosition()) - pushbackRadius);
|
|
||||||
|
|
||||||
// push camera out of any intersecting avatars
|
|
||||||
foreach (const AvatarSharedPointer& avatarData, _avatarManager.getAvatarHash()) {
|
|
||||||
Avatar* avatar = static_cast<Avatar*>(avatarData.data());
|
|
||||||
if (avatar->isMyAvatar()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (glm::distance(avatar->getPosition(), _myCamera.getTargetPosition()) >
|
|
||||||
avatar->getBoundingRadius() + pushbackRadius) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
float angle = angleBetween(avatar->getPosition() - _myCamera.getTargetPosition(), planeNormal);
|
|
||||||
if (angle > PI_OVER_TWO) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
float scale = 1.0f - angle / PI_OVER_TWO;
|
|
||||||
scale = qMin(1.0f, scale * 2.5f);
|
|
||||||
static CollisionList collisions(64);
|
|
||||||
collisions.clear();
|
|
||||||
if (!avatar->findPlaneCollisions(plane, collisions)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < collisions.size(); i++) {
|
|
||||||
pushback = qMax(pushback, glm::length(collisions.getCollision(i)->_penetration) * scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const float MAX_PUSHBACK = 0.35f;
|
|
||||||
pushback = qMin(pushback, MAX_PUSHBACK * _myAvatar->getScale());
|
|
||||||
const float BASE_PUSHBACK_FOCAL_LENGTH = 0.5f;
|
|
||||||
pushbackFocalLength = BASE_PUSHBACK_FOCAL_LENGTH * _myAvatar->getScale();
|
|
||||||
|
|
||||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||||
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
|
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
|
||||||
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
|
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
|
||||||
|
@ -3124,9 +3095,9 @@ void Application::updateWindowTitle(){
|
||||||
QString buildVersion = " (build " + applicationVersion() + ")";
|
QString buildVersion = " (build " + applicationVersion() + ")";
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
QString username = AccountManager::getInstance().getUsername();
|
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
|
||||||
QString title = QString() + (!username.isEmpty() ? username + " " : QString()) + nodeList->getSessionUUID().toString()
|
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
||||||
+ " @ " + nodeList->getDomainHandler().getHostname() + buildVersion;
|
+ nodeList->getDomainHandler().getHostname() + buildVersion;
|
||||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||||
_window->setWindowTitle(title);
|
_window->setWindowTitle(title);
|
||||||
}
|
}
|
||||||
|
@ -3150,6 +3121,9 @@ void Application::domainChanged(const QString& domainHostname) {
|
||||||
|
|
||||||
// reset the voxels renderer
|
// reset the voxels renderer
|
||||||
_voxels.killLocalVoxels();
|
_voxels.killLocalVoxels();
|
||||||
|
|
||||||
|
// reset the auth URL for OAuth web view handler
|
||||||
|
OAuthWebViewHandler::getInstance().clearLastAuthorizationURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::connectedToDomain(const QString& hostname) {
|
void Application::connectedToDomain(const QString& hostname) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
#include "ui/OAuthWebViewHandler.h"
|
||||||
|
|
||||||
#include "DatagramProcessor.h"
|
#include "DatagramProcessor.h"
|
||||||
|
|
||||||
|
@ -56,13 +57,11 @@ void DatagramProcessor::processDatagrams() {
|
||||||
Particle::handleAddParticleResponse(incomingPacket);
|
Particle::handleAddParticleResponse(incomingPacket);
|
||||||
application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket);
|
application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PacketTypeModelAddResponse:
|
case PacketTypeModelAddResponse:
|
||||||
// this will keep creatorTokenIDs to IDs mapped correctly
|
// this will keep creatorTokenIDs to IDs mapped correctly
|
||||||
ModelItem::handleAddModelResponse(incomingPacket);
|
ModelItem::handleAddModelResponse(incomingPacket);
|
||||||
application->getModels()->getTree()->handleAddModelResponse(incomingPacket);
|
application->getModels()->getTree()->handleAddModelResponse(incomingPacket);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PacketTypeParticleData:
|
case PacketTypeParticleData:
|
||||||
case PacketTypeParticleErase:
|
case PacketTypeParticleErase:
|
||||||
case PacketTypeModelData:
|
case PacketTypeModelData:
|
||||||
|
@ -120,6 +119,18 @@ void DatagramProcessor::processDatagrams() {
|
||||||
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size());
|
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PacketTypeDomainOAuthRequest: {
|
||||||
|
QDataStream readStream(incomingPacket);
|
||||||
|
readStream.skipRawData(numBytesForPacketHeader(incomingPacket));
|
||||||
|
|
||||||
|
QUrl authorizationURL;
|
||||||
|
readStream >> authorizationURL;
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(&OAuthWebViewHandler::getInstance(), "displayWebviewForAuthorizationURL",
|
||||||
|
Q_ARG(const QUrl&, authorizationURL));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
nodeList->processNodeData(senderSockAddr, incomingPacket);
|
nodeList->processNodeData(senderSockAddr, incomingPacket);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -336,7 +336,6 @@ Menu::Menu() :
|
||||||
SLOT(setFilter(bool)));
|
SLOT(setFilter(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false);
|
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, true);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, true);
|
||||||
|
@ -1100,7 +1099,7 @@ void Menu::toggleLoginMenuItem() {
|
||||||
|
|
||||||
if (accountManager.isLoggedIn()) {
|
if (accountManager.isLoggedIn()) {
|
||||||
// change the menu item to logout
|
// change the menu item to logout
|
||||||
_loginAction->setText("Logout " + accountManager.getUsername());
|
_loginAction->setText("Logout " + accountManager.getAccountInfo().getUsername());
|
||||||
connect(_loginAction, &QAction::triggered, &accountManager, &AccountManager::logout);
|
connect(_loginAction, &QAction::triggered, &accountManager, &AccountManager::logout);
|
||||||
} else {
|
} else {
|
||||||
// change the menu item to login
|
// change the menu item to login
|
||||||
|
|
|
@ -340,7 +340,6 @@ namespace MenuOption {
|
||||||
const QString Particles = "Particles";
|
const QString Particles = "Particles";
|
||||||
const QString PasteToVoxel = "Paste to Voxel...";
|
const QString PasteToVoxel = "Paste to Voxel...";
|
||||||
const QString PipelineWarnings = "Show Render Pipeline Warnings";
|
const QString PipelineWarnings = "Show Render Pipeline Warnings";
|
||||||
const QString PlaySlaps = "Play Slaps";
|
|
||||||
const QString Preferences = "Preferences...";
|
const QString Preferences = "Preferences...";
|
||||||
const QString Quit = "Quit";
|
const QString Quit = "Quit";
|
||||||
const QString ReloadAllScripts = "Reload All Scripts";
|
const QString ReloadAllScripts = "Reload All Scripts";
|
||||||
|
|
|
@ -153,14 +153,14 @@ bool ModelUploader::zip() {
|
||||||
|
|
||||||
// mixamo/autodesk defaults
|
// mixamo/autodesk defaults
|
||||||
if (!mapping.contains(SCALE_FIELD)) {
|
if (!mapping.contains(SCALE_FIELD)) {
|
||||||
mapping.insert(SCALE_FIELD, 10.0);
|
mapping.insert(SCALE_FIELD, geometry.author == "www.makehuman.org" ? 150.0 : 15.0);
|
||||||
}
|
}
|
||||||
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
||||||
if (!joints.contains("jointEyeLeft")) {
|
if (!joints.contains("jointEyeLeft")) {
|
||||||
joints.insert("jointEyeLeft", "LeftEye");
|
joints.insert("jointEyeLeft", geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye");
|
||||||
}
|
}
|
||||||
if (!joints.contains("jointEyeRight")) {
|
if (!joints.contains("jointEyeRight")) {
|
||||||
joints.insert("jointEyeRight", "RightEye");
|
joints.insert("jointEyeRight", geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye");
|
||||||
}
|
}
|
||||||
if (!joints.contains("jointNeck")) {
|
if (!joints.contains("jointNeck")) {
|
||||||
joints.insert("jointNeck", "Neck");
|
joints.insert("jointNeck", "Neck");
|
||||||
|
@ -172,7 +172,8 @@ bool ModelUploader::zip() {
|
||||||
joints.insert("jointLean", "Spine");
|
joints.insert("jointLean", "Spine");
|
||||||
}
|
}
|
||||||
if (!joints.contains("jointHead")) {
|
if (!joints.contains("jointHead")) {
|
||||||
joints.insert("jointHead", geometry.applicationName == "mixamo.com" ? "HeadTop_End" : "HeadEnd");
|
const char* topName = (geometry.applicationName == "mixamo.com") ? "HeadTop_End" : "HeadEnd";
|
||||||
|
joints.insert("jointHead", geometry.jointIndices.contains(topName) ? topName : "Head");
|
||||||
}
|
}
|
||||||
if (!joints.contains("jointLeftHand")) {
|
if (!joints.contains("jointLeftHand")) {
|
||||||
joints.insert("jointLeftHand", "LeftHand");
|
joints.insert("jointLeftHand", "LeftHand");
|
||||||
|
@ -600,7 +601,7 @@ static void setJointText(QComboBox* box, const QString& text) {
|
||||||
void ModelPropertiesDialog::reset() {
|
void ModelPropertiesDialog::reset() {
|
||||||
_name->setText(_originalMapping.value(NAME_FIELD).toString());
|
_name->setText(_originalMapping.value(NAME_FIELD).toString());
|
||||||
_textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString());
|
_textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString());
|
||||||
_scale->setValue(_originalMapping.value(SCALE_FIELD, 1.0).toDouble());
|
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
|
||||||
|
|
||||||
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
||||||
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
|
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
|
||||||
|
|
|
@ -34,12 +34,13 @@ XmppClient& XmppClient::getInstance() {
|
||||||
|
|
||||||
void XmppClient::xmppConnected() {
|
void XmppClient::xmppConnected() {
|
||||||
_publicChatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM);
|
_publicChatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM);
|
||||||
_publicChatRoom->setNickName(AccountManager::getInstance().getUsername());
|
_publicChatRoom->setNickName(AccountManager::getInstance().getAccountInfo().getUsername());
|
||||||
_publicChatRoom->join();
|
_publicChatRoom->join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void XmppClient::xmppError(QXmppClient::Error error) {
|
void XmppClient::xmppError(QXmppClient::Error error) {
|
||||||
qDebug() << "Error connnecting to XMPP for user " << AccountManager::getInstance().getUsername() << ": " << error;
|
qDebug() << "Error connnecting to XMPP for user "
|
||||||
|
<< AccountManager::getInstance().getAccountInfo().getUsername() << ": " << error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XmppClient::connectToServer() {
|
void XmppClient::connectToServer() {
|
||||||
|
@ -50,8 +51,8 @@ void XmppClient::connectToServer() {
|
||||||
connect(&_xmppClient, SIGNAL(error(QXmppClient::Error)), this, SLOT(xmppError(QXmppClient::Error)));
|
connect(&_xmppClient, SIGNAL(error(QXmppClient::Error)), this, SLOT(xmppError(QXmppClient::Error)));
|
||||||
}
|
}
|
||||||
AccountManager& accountManager = AccountManager::getInstance();
|
AccountManager& accountManager = AccountManager::getInstance();
|
||||||
QString user = accountManager.getUsername();
|
QString user = accountManager.getAccountInfo().getUsername();
|
||||||
const QString& password = accountManager.getXMPPPassword();
|
const QString& password = accountManager.getAccountInfo().getXMPPPassword();
|
||||||
_xmppClient.connectToServer(user + "@" + DEFAULT_XMPP_SERVER, password);
|
_xmppClient.connectToServer(user + "@" + DEFAULT_XMPP_SERVER, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -776,7 +776,16 @@ float Avatar::getSkeletonHeight() const {
|
||||||
|
|
||||||
float Avatar::getHeadHeight() const {
|
float Avatar::getHeadHeight() const {
|
||||||
Extents extents = getHead()->getFaceModel().getBindExtents();
|
Extents extents = getHead()->getFaceModel().getBindExtents();
|
||||||
return extents.maximum.y - extents.minimum.y;
|
if (!extents.isEmpty()) {
|
||||||
|
return extents.maximum.y - extents.minimum.y;
|
||||||
|
}
|
||||||
|
glm::vec3 neckPosition;
|
||||||
|
glm::vec3 headPosition;
|
||||||
|
if (_skeletonModel.getNeckPosition(neckPosition) && _skeletonModel.getHeadPosition(headPosition)) {
|
||||||
|
return glm::distance(neckPosition, headPosition);
|
||||||
|
}
|
||||||
|
const float DEFAULT_HEAD_HEIGHT = 0.1f;
|
||||||
|
return DEFAULT_HEAD_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
||||||
|
|
|
@ -29,11 +29,11 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
|
||||||
neckPosition = owningAvatar->getPosition();
|
neckPosition = owningAvatar->getPosition();
|
||||||
}
|
}
|
||||||
setTranslation(neckPosition);
|
setTranslation(neckPosition);
|
||||||
glm::quat neckRotation;
|
glm::quat neckParentRotation;
|
||||||
if (!owningAvatar->getSkeletonModel().getNeckRotation(neckRotation)) {
|
if (!owningAvatar->getSkeletonModel().getNeckParentRotation(neckParentRotation)) {
|
||||||
neckRotation = owningAvatar->getOrientation();
|
neckParentRotation = owningAvatar->getOrientation();
|
||||||
}
|
}
|
||||||
setRotation(neckRotation);
|
setRotation(neckParentRotation);
|
||||||
const float MODEL_SCALE = 0.0006f;
|
const float MODEL_SCALE = 0.0006f;
|
||||||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale() * MODEL_SCALE);
|
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale() * MODEL_SCALE);
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,6 @@ public:
|
||||||
|
|
||||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||||
virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||||
|
|
||||||
|
|
|
@ -55,76 +55,10 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hand::playSlaps(PalmData& palm, Avatar* avatar) {
|
|
||||||
// Check for palm collisions
|
|
||||||
glm::vec3 myPalmPosition = palm.getPosition();
|
|
||||||
float palmCollisionDistance = 0.1f;
|
|
||||||
bool wasColliding = palm.getIsCollidingWithPalm();
|
|
||||||
palm.setIsCollidingWithPalm(false);
|
|
||||||
// If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound
|
|
||||||
for (size_t j = 0; j < avatar->getHand()->getNumPalms(); j++) {
|
|
||||||
PalmData& otherPalm = avatar->getHand()->getPalms()[j];
|
|
||||||
if (!otherPalm.isActive()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
glm::vec3 otherPalmPosition = otherPalm.getPosition();
|
|
||||||
if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) {
|
|
||||||
palm.setIsCollidingWithPalm(true);
|
|
||||||
if (!wasColliding) {
|
|
||||||
const float PALM_COLLIDE_VOLUME = 1.f;
|
|
||||||
const float PALM_COLLIDE_FREQUENCY = 1000.f;
|
|
||||||
const float PALM_COLLIDE_DURATION_MAX = 0.75f;
|
|
||||||
const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f;
|
|
||||||
Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME,
|
|
||||||
PALM_COLLIDE_FREQUENCY,
|
|
||||||
PALM_COLLIDE_DURATION_MAX,
|
|
||||||
PALM_COLLIDE_DECAY_PER_SAMPLE);
|
|
||||||
// If the other person's palm is in motion, move mine downward to show I was hit
|
|
||||||
const float MIN_VELOCITY_FOR_SLAP = 0.05f;
|
|
||||||
if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) {
|
|
||||||
// add slapback here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We create a static CollisionList that is recycled for each collision test.
|
// We create a static CollisionList that is recycled for each collision test.
|
||||||
const float MAX_COLLISIONS_PER_AVATAR = 32;
|
const float MAX_COLLISIONS_PER_AVATAR = 32;
|
||||||
static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR);
|
static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR);
|
||||||
|
|
||||||
void Hand::collideAgainstAvatarOld(Avatar* avatar, bool isMyHand) {
|
|
||||||
if (!avatar || avatar == _owningAvatar) {
|
|
||||||
// don't collide with our own hands (that is done elsewhere)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
|
|
||||||
for (size_t i = 0; i < getNumPalms(); i++) {
|
|
||||||
PalmData& palm = getPalms()[i];
|
|
||||||
if (!palm.isActive()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
|
||||||
playSlaps(palm, avatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 totalPenetration;
|
|
||||||
handCollisions.clear();
|
|
||||||
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions)) {
|
|
||||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
|
||||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
|
||||||
if (isMyHand) {
|
|
||||||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isMyHand) {
|
|
||||||
// resolve penetration
|
|
||||||
palm.addToPosition(-totalPenetration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
||||||
if (!avatar || avatar == _owningAvatar) {
|
if (!avatar || avatar == _owningAvatar) {
|
||||||
// don't collide hands against ourself (that is done elsewhere)
|
// don't collide hands against ourself (that is done elsewhere)
|
||||||
|
|
|
@ -58,7 +58,6 @@ public:
|
||||||
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
|
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
|
||||||
const glm::vec3& getLeapFingerRootBallPosition(int ball) const { return _leapFingerRootBalls[ball].position;}
|
const glm::vec3& getLeapFingerRootBallPosition(int ball) const { return _leapFingerRootBalls[ball].position;}
|
||||||
|
|
||||||
void collideAgainstAvatarOld(Avatar* avatar, bool isMyHand);
|
|
||||||
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);
|
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);
|
||||||
void collideAgainstOurself();
|
void collideAgainstOurself();
|
||||||
|
|
||||||
|
@ -80,8 +79,6 @@ private:
|
||||||
void renderLeapFingerTrails();
|
void renderLeapFingerTrails();
|
||||||
|
|
||||||
void calculateGeometry();
|
void calculateGeometry();
|
||||||
|
|
||||||
void playSlaps(PalmData& palm, Avatar* avatar);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Hand_h
|
#endif // hifi_Hand_h
|
||||||
|
|
|
@ -161,7 +161,9 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
||||||
_leftEyePosition = _rightEyePosition = getPosition();
|
_leftEyePosition = _rightEyePosition = getPosition();
|
||||||
if (!billboard) {
|
if (!billboard) {
|
||||||
_faceModel.simulate(deltaTime);
|
_faceModel.simulate(deltaTime);
|
||||||
_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition);
|
if (!_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition)) {
|
||||||
|
static_cast<Avatar*>(_owningAvatar)->getSkeletonModel().getEyePositions(_leftEyePosition, _rightEyePosition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_eyePosition = calculateAverageEyePosition();
|
_eyePosition = calculateAverageEyePosition();
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,6 +221,14 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
|
||||||
glm::normalize(inverse * axes[0])) * joint.rotation;
|
glm::normalize(inverse * axes[0])) * joint.rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||||
|
_owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||||
|
_owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(parentState, joint, state);
|
||||||
|
}
|
||||||
|
|
||||||
void SkeletonModel::renderJointConstraints(int jointIndex) {
|
void SkeletonModel::renderJointConstraints(int jointIndex) {
|
||||||
if (jointIndex == -1) {
|
if (jointIndex == -1) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -46,6 +46,8 @@ protected:
|
||||||
virtual void updateJointState(int index);
|
virtual void updateJointState(int index);
|
||||||
|
|
||||||
virtual void maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
virtual void maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||||
|
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||||
|
virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -434,6 +434,17 @@ bool Model::getNeckRotation(glm::quat& neckRotation) const {
|
||||||
return isActive() && getJointRotation(_geometry->getFBXGeometry().neckJointIndex, neckRotation);
|
return isActive() && getJointRotation(_geometry->getFBXGeometry().neckJointIndex, neckRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Model::getNeckParentRotation(glm::quat& neckParentRotation) const {
|
||||||
|
if (!isActive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
|
if (geometry.neckJointIndex == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return getJointRotation(geometry.joints.at(geometry.neckJointIndex).parentIndex, neckParentRotation);
|
||||||
|
}
|
||||||
|
|
||||||
bool Model::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
bool Model::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||||
if (!isActive()) {
|
if (!isActive()) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -132,6 +132,10 @@ public:
|
||||||
/// \return whether or not the neck was found
|
/// \return whether or not the neck was found
|
||||||
bool getNeckRotation(glm::quat& neckRotation) const;
|
bool getNeckRotation(glm::quat& neckRotation) const;
|
||||||
|
|
||||||
|
/// Returns the rotation of the neck joint's parent.
|
||||||
|
/// \return whether or not the neck was found
|
||||||
|
bool getNeckParentRotation(glm::quat& neckRotation) const;
|
||||||
|
|
||||||
/// Retrieve the positions of up to two eye meshes.
|
/// Retrieve the positions of up to two eye meshes.
|
||||||
/// \return whether or not both eye meshes were found
|
/// \return whether or not both eye meshes were found
|
||||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||||
|
|
|
@ -254,7 +254,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update background if this is a message from the current user
|
// Update background if this is a message from the current user
|
||||||
bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getUsername();
|
bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getAccountInfo().getUsername();
|
||||||
|
|
||||||
// Create message area
|
// Create message area
|
||||||
ChatMessageArea* messageArea = new ChatMessageArea(true);
|
ChatMessageArea* messageArea = new ChatMessageArea(true);
|
||||||
|
|
116
interface/src/ui/OAuthWebViewHandler.cpp
Normal file
116
interface/src/ui/OAuthWebViewHandler.cpp
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
//
|
||||||
|
// OAuthWebViewHandler.cpp
|
||||||
|
// interface/src/ui
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2014-05-01.
|
||||||
|
// 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 <QtWebKitWidgets/QWebView>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
|
#include "OAuthWebViewHandler.h"
|
||||||
|
|
||||||
|
OAuthWebViewHandler& OAuthWebViewHandler::getInstance() {
|
||||||
|
static OAuthWebViewHandler sharedInstance;
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuthWebViewHandler::OAuthWebViewHandler() :
|
||||||
|
_activeWebView(NULL),
|
||||||
|
_webViewRedisplayTimer(),
|
||||||
|
_lastAuthorizationURL()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig() {
|
||||||
|
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||||
|
|
||||||
|
// add the High Fidelity root CA to the list of trusted CA certificates
|
||||||
|
QByteArray highFidelityCACertificate(reinterpret_cast<char*>(DTLSSession::highFidelityCADatum()->data),
|
||||||
|
DTLSSession::highFidelityCADatum()->size);
|
||||||
|
sslConfig.setCaCertificates(sslConfig.caCertificates() + QSslCertificate::fromData(highFidelityCACertificate));
|
||||||
|
|
||||||
|
// set the modified configuration
|
||||||
|
QSslConfiguration::setDefaultConfiguration(sslConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int WEB_VIEW_REDISPLAY_ELAPSED_MSECS = 5 * 1000;
|
||||||
|
|
||||||
|
void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authorizationURL) {
|
||||||
|
if (!_activeWebView) {
|
||||||
|
|
||||||
|
if (!_lastAuthorizationURL.isEmpty()) {
|
||||||
|
if (_lastAuthorizationURL.host() == authorizationURL.host()
|
||||||
|
&& _webViewRedisplayTimer.elapsed() < WEB_VIEW_REDISPLAY_ELAPSED_MSECS) {
|
||||||
|
// this would be re-displaying an OAuth dialog for the same auth URL inside of the redisplay ms
|
||||||
|
// so return instead
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastAuthorizationURL = authorizationURL;
|
||||||
|
|
||||||
|
_activeWebView = new QWebView;
|
||||||
|
|
||||||
|
// keep the window on top and delete it when it closes
|
||||||
|
_activeWebView->setWindowFlags(Qt::WindowStaysOnTopHint);
|
||||||
|
_activeWebView->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
|
qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString();
|
||||||
|
|
||||||
|
AccountManager& accountManager = AccountManager::getInstance();
|
||||||
|
|
||||||
|
QUrl codedAuthorizationURL = authorizationURL;
|
||||||
|
|
||||||
|
// check if we have an access token for this host - if so we can bypass login by adding it to the URL
|
||||||
|
if (accountManager.getAuthURL().host() == authorizationURL.host()
|
||||||
|
&& accountManager.hasValidAccessToken()) {
|
||||||
|
|
||||||
|
const QString ACCESS_TOKEN_QUERY_STRING_KEY = "access_token";
|
||||||
|
|
||||||
|
QUrlQuery authQuery(codedAuthorizationURL);
|
||||||
|
authQuery.addQueryItem(ACCESS_TOKEN_QUERY_STRING_KEY, accountManager.getAccountInfo().getAccessToken().token);
|
||||||
|
|
||||||
|
codedAuthorizationURL.setQuery(authQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
_activeWebView->load(codedAuthorizationURL);
|
||||||
|
_activeWebView->show();
|
||||||
|
|
||||||
|
connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors,
|
||||||
|
this, &OAuthWebViewHandler::handleSSLErrors);
|
||||||
|
connect(_activeWebView.data(), &QWebView::loadFinished, this, &OAuthWebViewHandler::handleLoadFinished);
|
||||||
|
|
||||||
|
// connect to the destroyed signal so after the web view closes we can start a timer
|
||||||
|
connect(_activeWebView.data(), &QWebView::destroyed, this, &OAuthWebViewHandler::handleWebViewDestroyed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OAuthWebViewHandler::handleSSLErrors(QNetworkReply* networkReply, const QList<QSslError>& errorList) {
|
||||||
|
qDebug() << "SSL Errors:" << errorList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OAuthWebViewHandler::handleLoadFinished(bool success) {
|
||||||
|
if (success && _activeWebView->url().host() == NodeList::getInstance()->getDomainHandler().getHostname()) {
|
||||||
|
qDebug() << "OAuth authorization code passed successfully to domain-server.";
|
||||||
|
|
||||||
|
// grab the UUID that is set as the state parameter in the auth URL
|
||||||
|
// since that is our new session UUID
|
||||||
|
QUrlQuery authQuery(_activeWebView->url());
|
||||||
|
|
||||||
|
const QString AUTH_STATE_QUERY_KEY = "state";
|
||||||
|
NodeList::getInstance()->setSessionUUID(QUuid(authQuery.queryItemValue(AUTH_STATE_QUERY_KEY)));
|
||||||
|
|
||||||
|
_activeWebView->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) {
|
||||||
|
_webViewRedisplayTimer.restart();
|
||||||
|
}
|
41
interface/src/ui/OAuthWebViewHandler.h
Normal file
41
interface/src/ui/OAuthWebViewHandler.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// OAuthWebviewHandler.h
|
||||||
|
// interface/src/ui
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2014-05-01.
|
||||||
|
// 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_OAuthWebviewHandler_h
|
||||||
|
#define hifi_OAuthWebviewHandler_h
|
||||||
|
|
||||||
|
#include <QtCore/QUrl>
|
||||||
|
|
||||||
|
class QWebView;
|
||||||
|
|
||||||
|
class OAuthWebViewHandler : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
OAuthWebViewHandler();
|
||||||
|
static OAuthWebViewHandler& getInstance();
|
||||||
|
static void addHighFidelityRootCAToSSLConfig();
|
||||||
|
|
||||||
|
void clearLastAuthorizationURL() { _lastAuthorizationURL = QUrl(); }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void displayWebviewForAuthorizationURL(const QUrl& authorizationURL);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleSSLErrors(QNetworkReply* networkReply, const QList<QSslError>& errorList);
|
||||||
|
void handleLoadFinished(bool success);
|
||||||
|
void handleWebViewDestroyed(QObject* destroyedObject);
|
||||||
|
private:
|
||||||
|
QPointer<QWebView> _activeWebView;
|
||||||
|
QElapsedTimer _webViewRedisplayTimer;
|
||||||
|
QUrl _lastAuthorizationURL;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_OAuthWebviewHandler_h
|
|
@ -86,7 +86,7 @@ void Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
|
||||||
// replace decimal . with '-'
|
// replace decimal . with '-'
|
||||||
formattedLocation.replace('.', '-');
|
formattedLocation.replace('.', '-');
|
||||||
|
|
||||||
QString username = AccountManager::getInstance().getUsername();
|
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
|
||||||
// normalize username, replace all non alphanumeric with '-'
|
// normalize username, replace all non alphanumeric with '-'
|
||||||
username.replace(QRegExp("[^A-Za-z0-9_]"), "-");
|
username.replace(QRegExp("[^A-Za-z0-9_]"), "-");
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,9 @@ void Sphere3DOverlay::render() {
|
||||||
|
|
||||||
glDisable(GL_LIGHTING);
|
glDisable(GL_LIGHTING);
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(_position.x + _size * 0.5f,
|
glTranslatef(_position.x,
|
||||||
_position.y + _size * 0.5f,
|
_position.y,
|
||||||
_position.z + _size * 0.5f);
|
_position.z);
|
||||||
glLineWidth(_lineWidth);
|
glLineWidth(_lineWidth);
|
||||||
const int slices = 15;
|
const int slices = 15;
|
||||||
if (_isSolid) {
|
if (_isSolid) {
|
||||||
|
|
|
@ -28,8 +28,8 @@ HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager)
|
||||||
_parentManager(parentManager),
|
_parentManager(parentManager),
|
||||||
_socket(socket),
|
_socket(socket),
|
||||||
_stream(socket),
|
_stream(socket),
|
||||||
_address(socket->peerAddress()) {
|
_address(socket->peerAddress())
|
||||||
|
{
|
||||||
// take over ownership of the socket
|
// take over ownership of the socket
|
||||||
_socket->setParent(this);
|
_socket->setParent(this);
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager)
|
||||||
HTTPConnection::~HTTPConnection() {
|
HTTPConnection::~HTTPConnection() {
|
||||||
// log the destruction
|
// log the destruction
|
||||||
if (_socket->error() != QAbstractSocket::UnknownSocketError) {
|
if (_socket->error() != QAbstractSocket::UnknownSocketError) {
|
||||||
qDebug() << _socket->errorString();
|
qDebug() << _socket->errorString() << "-" << _socket->error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
|
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QHostAddress>
|
#include <QtNetwork/QHostAddress>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QNetworkAccessManager>
|
#include <QtNetwork/QNetworkAccessManager>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
|
@ -18,116 +18,6 @@
|
||||||
#include "HTTPConnection.h"
|
#include "HTTPConnection.h"
|
||||||
#include "HTTPManager.h"
|
#include "HTTPManager.h"
|
||||||
|
|
||||||
bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) {
|
|
||||||
if (_requestHandler && _requestHandler->handleHTTPRequest(connection, url)) {
|
|
||||||
// this request was handled by our _requestHandler object
|
|
||||||
// so we don't need to attempt to do so in the document root
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to see if there is a file to serve from the document root for this path
|
|
||||||
QString subPath = url.path();
|
|
||||||
|
|
||||||
// remove any slash at the beginning of the path
|
|
||||||
if (subPath.startsWith('/')) {
|
|
||||||
subPath.remove(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString filePath;
|
|
||||||
|
|
||||||
if (QFileInfo(_documentRoot + subPath).isFile()) {
|
|
||||||
filePath = _documentRoot + subPath;
|
|
||||||
} else if (subPath.size() > 0 && !subPath.endsWith('/')) {
|
|
||||||
// this could be a directory with a trailing slash
|
|
||||||
// send a redirect to the path with a slash so we can
|
|
||||||
QString redirectLocation = '/' + subPath + '/';
|
|
||||||
|
|
||||||
if (!url.query().isEmpty()) {
|
|
||||||
redirectLocation += "?" + url.query();
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<QByteArray, QByteArray> redirectHeader;
|
|
||||||
redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8());
|
|
||||||
|
|
||||||
connection->respond(HTTPConnection::StatusCode301, "", HTTPConnection::DefaultContentType, redirectHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the last thing is a trailing slash then we want to look for index file
|
|
||||||
if (subPath.endsWith('/') || subPath.size() == 0) {
|
|
||||||
QStringList possibleIndexFiles = QStringList() << "index.html" << "index.shtml";
|
|
||||||
|
|
||||||
foreach (const QString& possibleIndexFilename, possibleIndexFiles) {
|
|
||||||
if (QFileInfo(_documentRoot + subPath + possibleIndexFilename).exists()) {
|
|
||||||
filePath = _documentRoot + subPath + possibleIndexFilename;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filePath.isEmpty()) {
|
|
||||||
// file exists, serve it
|
|
||||||
static QMimeDatabase mimeDatabase;
|
|
||||||
|
|
||||||
QFile localFile(filePath);
|
|
||||||
localFile.open(QIODevice::ReadOnly);
|
|
||||||
QByteArray localFileData = localFile.readAll();
|
|
||||||
|
|
||||||
QFileInfo localFileInfo(filePath);
|
|
||||||
|
|
||||||
if (localFileInfo.completeSuffix() == "shtml") {
|
|
||||||
// this is a file that may have some SSI statements
|
|
||||||
// the only thing we support is the include directive, but check the contents for that
|
|
||||||
|
|
||||||
// setup our static QRegExp that will catch <!--#include virtual ... --> and <!--#include file .. --> directives
|
|
||||||
const QString includeRegExpString = "<!--\\s*#include\\s+(virtual|file)\\s?=\\s?\"(\\S+)\"\\s*-->";
|
|
||||||
QRegExp includeRegExp(includeRegExpString);
|
|
||||||
|
|
||||||
int matchPosition = 0;
|
|
||||||
|
|
||||||
QString localFileString(localFileData);
|
|
||||||
|
|
||||||
while ((matchPosition = includeRegExp.indexIn(localFileString, matchPosition)) != -1) {
|
|
||||||
// check if this is a file or vitual include
|
|
||||||
bool isFileInclude = includeRegExp.cap(1) == "file";
|
|
||||||
|
|
||||||
// setup the correct file path for the included file
|
|
||||||
QString includeFilePath = isFileInclude
|
|
||||||
? localFileInfo.canonicalPath() + "/" + includeRegExp.cap(2)
|
|
||||||
: _documentRoot + includeRegExp.cap(2);
|
|
||||||
|
|
||||||
QString replacementString;
|
|
||||||
|
|
||||||
if (QFileInfo(includeFilePath).isFile()) {
|
|
||||||
|
|
||||||
QFile includedFile(includeFilePath);
|
|
||||||
includedFile.open(QIODevice::ReadOnly);
|
|
||||||
|
|
||||||
replacementString = QString(includedFile.readAll());
|
|
||||||
} else {
|
|
||||||
qDebug() << "SSI include directive referenced a missing file:" << includeFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace the match with the contents of the file, or an empty string if the file was not found
|
|
||||||
localFileString.replace(matchPosition, includeRegExp.matchedLength(), replacementString);
|
|
||||||
|
|
||||||
// push the match position forward so we can check the next match
|
|
||||||
matchPosition += includeRegExp.matchedLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
localFileData = localFileString.toLocal8Bit();
|
|
||||||
}
|
|
||||||
|
|
||||||
connection->respond(HTTPConnection::StatusCode200, localFileData,
|
|
||||||
qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// respond with a 404
|
|
||||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) :
|
HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) :
|
||||||
QTcpServer(parent),
|
QTcpServer(parent),
|
||||||
_documentRoot(documentRoot),
|
_documentRoot(documentRoot),
|
||||||
|
@ -138,14 +28,131 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH
|
||||||
qDebug() << "Failed to open HTTP server socket:" << errorString();
|
qDebug() << "Failed to open HTTP server socket:" << errorString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect the connection signal
|
|
||||||
connect(this, SIGNAL(newConnection()), SLOT(acceptConnections()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTTPManager::acceptConnections() {
|
void HTTPManager::incomingConnection(qintptr socketDescriptor) {
|
||||||
QTcpSocket* socket;
|
QTcpSocket* socket = new QTcpSocket(this);
|
||||||
while ((socket = nextPendingConnection()) != 0) {
|
|
||||||
|
if (socket->setSocketDescriptor(socketDescriptor)) {
|
||||||
new HTTPConnection(socket, this);
|
new HTTPConnection(socket, this);
|
||||||
|
} else {
|
||||||
|
delete socket;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) {
|
||||||
|
if (requestHandledByRequestHandler(connection, url)) {
|
||||||
|
// this request was handled by our request handler object
|
||||||
|
// so we don't need to attempt to do so in the document root
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_documentRoot.isEmpty()) {
|
||||||
|
// check to see if there is a file to serve from the document root for this path
|
||||||
|
QString subPath = url.path();
|
||||||
|
|
||||||
|
// remove any slash at the beginning of the path
|
||||||
|
if (subPath.startsWith('/')) {
|
||||||
|
subPath.remove(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString filePath;
|
||||||
|
|
||||||
|
if (QFileInfo(_documentRoot + subPath).isFile()) {
|
||||||
|
filePath = _documentRoot + subPath;
|
||||||
|
} else if (subPath.size() > 0 && !subPath.endsWith('/')) {
|
||||||
|
// this could be a directory with a trailing slash
|
||||||
|
// send a redirect to the path with a slash so we can
|
||||||
|
QString redirectLocation = '/' + subPath + '/';
|
||||||
|
|
||||||
|
if (!url.query().isEmpty()) {
|
||||||
|
redirectLocation += "?" + url.query();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QByteArray, QByteArray> redirectHeader;
|
||||||
|
redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8());
|
||||||
|
|
||||||
|
connection->respond(HTTPConnection::StatusCode301, "", HTTPConnection::DefaultContentType, redirectHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the last thing is a trailing slash then we want to look for index file
|
||||||
|
if (subPath.endsWith('/') || subPath.size() == 0) {
|
||||||
|
QStringList possibleIndexFiles = QStringList() << "index.html" << "index.shtml";
|
||||||
|
|
||||||
|
foreach (const QString& possibleIndexFilename, possibleIndexFiles) {
|
||||||
|
if (QFileInfo(_documentRoot + subPath + possibleIndexFilename).exists()) {
|
||||||
|
filePath = _documentRoot + subPath + possibleIndexFilename;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filePath.isEmpty()) {
|
||||||
|
// file exists, serve it
|
||||||
|
static QMimeDatabase mimeDatabase;
|
||||||
|
|
||||||
|
QFile localFile(filePath);
|
||||||
|
localFile.open(QIODevice::ReadOnly);
|
||||||
|
QByteArray localFileData = localFile.readAll();
|
||||||
|
|
||||||
|
QFileInfo localFileInfo(filePath);
|
||||||
|
|
||||||
|
if (localFileInfo.completeSuffix() == "shtml") {
|
||||||
|
// this is a file that may have some SSI statements
|
||||||
|
// the only thing we support is the include directive, but check the contents for that
|
||||||
|
|
||||||
|
// setup our static QRegExp that will catch <!--#include virtual ... --> and <!--#include file .. --> directives
|
||||||
|
const QString includeRegExpString = "<!--\\s*#include\\s+(virtual|file)\\s?=\\s?\"(\\S+)\"\\s*-->";
|
||||||
|
QRegExp includeRegExp(includeRegExpString);
|
||||||
|
|
||||||
|
int matchPosition = 0;
|
||||||
|
|
||||||
|
QString localFileString(localFileData);
|
||||||
|
|
||||||
|
while ((matchPosition = includeRegExp.indexIn(localFileString, matchPosition)) != -1) {
|
||||||
|
// check if this is a file or vitual include
|
||||||
|
bool isFileInclude = includeRegExp.cap(1) == "file";
|
||||||
|
|
||||||
|
// setup the correct file path for the included file
|
||||||
|
QString includeFilePath = isFileInclude
|
||||||
|
? localFileInfo.canonicalPath() + "/" + includeRegExp.cap(2)
|
||||||
|
: _documentRoot + includeRegExp.cap(2);
|
||||||
|
|
||||||
|
QString replacementString;
|
||||||
|
|
||||||
|
if (QFileInfo(includeFilePath).isFile()) {
|
||||||
|
|
||||||
|
QFile includedFile(includeFilePath);
|
||||||
|
includedFile.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
replacementString = QString(includedFile.readAll());
|
||||||
|
} else {
|
||||||
|
qDebug() << "SSI include directive referenced a missing file:" << includeFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace the match with the contents of the file, or an empty string if the file was not found
|
||||||
|
localFileString.replace(matchPosition, includeRegExp.matchedLength(), replacementString);
|
||||||
|
|
||||||
|
// push the match position forward so we can check the next match
|
||||||
|
matchPosition += includeRegExp.matchedLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
localFileData = localFileString.toLocal8Bit();
|
||||||
|
}
|
||||||
|
|
||||||
|
connection->respond(HTTPConnection::StatusCode200, localFileData,
|
||||||
|
qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// respond with a 404
|
||||||
|
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HTTPManager::requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url) {
|
||||||
|
return _requestHandler && _requestHandler->handleHTTPRequest(connection, url);
|
||||||
|
}
|
|
@ -19,6 +19,7 @@
|
||||||
#include <QtNetwork/QTcpServer>
|
#include <QtNetwork/QTcpServer>
|
||||||
|
|
||||||
class HTTPConnection;
|
class HTTPConnection;
|
||||||
|
class HTTPSConnection;
|
||||||
|
|
||||||
class HTTPRequestHandler {
|
class HTTPRequestHandler {
|
||||||
public:
|
public:
|
||||||
|
@ -35,9 +36,10 @@ public:
|
||||||
|
|
||||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
|
|
||||||
protected slots:
|
protected:
|
||||||
/// Accepts all pending connections
|
/// Accepts all pending connections
|
||||||
void acceptConnections();
|
virtual void incomingConnection(qintptr socketDescriptor);
|
||||||
|
virtual bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url);
|
||||||
protected:
|
protected:
|
||||||
QString _documentRoot;
|
QString _documentRoot;
|
||||||
HTTPRequestHandler* _requestHandler;
|
HTTPRequestHandler* _requestHandler;
|
||||||
|
|
23
libraries/embedded-webserver/src/HTTPSConnection.cpp
Normal file
23
libraries/embedded-webserver/src/HTTPSConnection.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// HTTPSConnection.cpp
|
||||||
|
// libraries/embedded-webserver/src
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2014-04-24.
|
||||||
|
// 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 "HTTPSConnection.h"
|
||||||
|
|
||||||
|
HTTPSConnection::HTTPSConnection(QSslSocket* sslSocket, HTTPSManager* parentManager) :
|
||||||
|
HTTPConnection(sslSocket, parentManager)
|
||||||
|
{
|
||||||
|
connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(handleSSLErrors(const QList<QSslError>&)));
|
||||||
|
sslSocket->startServerEncryption();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPSConnection::handleSSLErrors(const QList<QSslError>& errors) {
|
||||||
|
qDebug() << "SSL errors:" << errors;
|
||||||
|
}
|
26
libraries/embedded-webserver/src/HTTPSConnection.h
Normal file
26
libraries/embedded-webserver/src/HTTPSConnection.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// HTTPSConnection.h
|
||||||
|
// libraries/embedded-webserver/src
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2014-04-24.
|
||||||
|
// 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_HTTPSConnection_h
|
||||||
|
#define hifi_HTTPSConnection_h
|
||||||
|
|
||||||
|
#include "HTTPConnection.h"
|
||||||
|
#include "HTTPSManager.h"
|
||||||
|
|
||||||
|
class HTTPSConnection : public HTTPConnection {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
HTTPSConnection(QSslSocket* sslSocket, HTTPSManager* parentManager);
|
||||||
|
protected slots:
|
||||||
|
void handleSSLErrors(const QList<QSslError>& errors);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_HTTPSConnection_h
|
51
libraries/embedded-webserver/src/HTTPSManager.cpp
Normal file
51
libraries/embedded-webserver/src/HTTPSManager.cpp
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
//
|
||||||
|
// HTTPSManager.cpp
|
||||||
|
// libraries/embedded-webserver/src
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2014-04-24.
|
||||||
|
// 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 <QtNetwork/QSslSocket>
|
||||||
|
|
||||||
|
#include "HTTPSConnection.h"
|
||||||
|
|
||||||
|
#include "HTTPSManager.h"
|
||||||
|
|
||||||
|
HTTPSManager::HTTPSManager(quint16 port, const QSslCertificate& certificate, const QSslKey& privateKey,
|
||||||
|
const QString& documentRoot, HTTPSRequestHandler* requestHandler, QObject* parent) :
|
||||||
|
HTTPManager(port, documentRoot, requestHandler, parent),
|
||||||
|
_certificate(certificate),
|
||||||
|
_privateKey(privateKey),
|
||||||
|
_sslRequestHandler(requestHandler)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPSManager::incomingConnection(qintptr socketDescriptor) {
|
||||||
|
QSslSocket* sslSocket = new QSslSocket(this);
|
||||||
|
|
||||||
|
sslSocket->setLocalCertificate(_certificate);
|
||||||
|
sslSocket->setPrivateKey(_privateKey);
|
||||||
|
|
||||||
|
if (sslSocket->setSocketDescriptor(socketDescriptor)) {
|
||||||
|
new HTTPSConnection(sslSocket, this);
|
||||||
|
} else {
|
||||||
|
delete sslSocket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HTTPSManager::handleHTTPRequest(HTTPConnection* connection, const QUrl &url) {
|
||||||
|
return handleHTTPSRequest(reinterpret_cast<HTTPSConnection*>(connection), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HTTPSManager::handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url) {
|
||||||
|
return HTTPManager::handleHTTPRequest(connection, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HTTPSManager::requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url) {
|
||||||
|
return _sslRequestHandler && _sslRequestHandler->handleHTTPSRequest(reinterpret_cast<HTTPSConnection*>(connection), url);
|
||||||
|
}
|
50
libraries/embedded-webserver/src/HTTPSManager.h
Normal file
50
libraries/embedded-webserver/src/HTTPSManager.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// HTTPSManager.h
|
||||||
|
// libraries/embedded-webserver/src
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2014-04-24.
|
||||||
|
// 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_HTTPSManager_h
|
||||||
|
#define hifi_HTTPSManager_h
|
||||||
|
|
||||||
|
#include <QtNetwork/QSslKey>
|
||||||
|
#include <QtNetwork/QSslCertificate>
|
||||||
|
|
||||||
|
#include "HTTPManager.h"
|
||||||
|
|
||||||
|
class HTTPSRequestHandler : public HTTPRequestHandler {
|
||||||
|
public:
|
||||||
|
/// Handles an HTTPS request
|
||||||
|
virtual bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HTTPSManager : public HTTPManager, public HTTPSRequestHandler {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
HTTPSManager(quint16 port,
|
||||||
|
const QSslCertificate& certificate,
|
||||||
|
const QSslKey& privateKey,
|
||||||
|
const QString& documentRoot,
|
||||||
|
HTTPSRequestHandler* requestHandler = NULL, QObject* parent = 0);
|
||||||
|
|
||||||
|
void setCertificate(const QSslCertificate& certificate) { _certificate = certificate; }
|
||||||
|
void setPrivateKey(const QSslKey& privateKey) { _privateKey = privateKey; }
|
||||||
|
|
||||||
|
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
|
bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void incomingConnection(qintptr socketDescriptor);
|
||||||
|
bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url);
|
||||||
|
private:
|
||||||
|
QSslCertificate _certificate;
|
||||||
|
QSslKey _privateKey;
|
||||||
|
HTTPSRequestHandler* _sslRequestHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_HTTPSManager_h
|
|
@ -406,7 +406,7 @@ QVariantHash parseMapping(QIODevice* device) {
|
||||||
|
|
||||||
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
|
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
|
||||||
QVector<glm::vec3> values;
|
QVector<glm::vec3> values;
|
||||||
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
|
for (const double* it = doubleVector.constData(), *end = it + (doubleVector.size() / 3 * 3); it != end; ) {
|
||||||
float x = *it++;
|
float x = *it++;
|
||||||
float y = *it++;
|
float y = *it++;
|
||||||
float z = *it++;
|
float z = *it++;
|
||||||
|
@ -417,7 +417,7 @@ QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
|
||||||
|
|
||||||
QVector<glm::vec2> createVec2Vector(const QVector<double>& doubleVector) {
|
QVector<glm::vec2> createVec2Vector(const QVector<double>& doubleVector) {
|
||||||
QVector<glm::vec2> values;
|
QVector<glm::vec2> values;
|
||||||
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
|
for (const double* it = doubleVector.constData(), *end = it + (doubleVector.size() / 2 * 2); it != end; ) {
|
||||||
float s = *it++;
|
float s = *it++;
|
||||||
float t = *it++;
|
float t = *it++;
|
||||||
values.append(glm::vec2(s, -t));
|
values.append(glm::vec2(s, -t));
|
||||||
|
@ -432,58 +432,59 @@ glm::mat4 createMat4(const QVector<double>& doubleVector) {
|
||||||
doubleVector.at(12), doubleVector.at(13), doubleVector.at(14), doubleVector.at(15));
|
doubleVector.at(12), doubleVector.at(13), doubleVector.at(14), doubleVector.at(15));
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<int> getIntVector(const QVariantList& properties, int index) {
|
QVector<int> getIntVector(const FBXNode& node) {
|
||||||
if (index >= properties.size()) {
|
foreach (const FBXNode& child, node.children) {
|
||||||
|
if (child.name == "a") {
|
||||||
|
return getIntVector(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node.properties.isEmpty()) {
|
||||||
return QVector<int>();
|
return QVector<int>();
|
||||||
}
|
}
|
||||||
QVector<int> vector = properties.at(index).value<QVector<int> >();
|
QVector<int> vector = node.properties.at(0).value<QVector<int> >();
|
||||||
if (!vector.isEmpty()) {
|
if (!vector.isEmpty()) {
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
for (; index < properties.size(); index++) {
|
for (int i = 0; i < node.properties.size(); i++) {
|
||||||
vector.append(properties.at(index).toInt());
|
vector.append(node.properties.at(i).toInt());
|
||||||
}
|
}
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<qlonglong> getLongVector(const QVariantList& properties, int index) {
|
QVector<float> getFloatVector(const FBXNode& node) {
|
||||||
if (index >= properties.size()) {
|
foreach (const FBXNode& child, node.children) {
|
||||||
return QVector<qlonglong>();
|
if (child.name == "a") {
|
||||||
|
return getFloatVector(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
QVector<qlonglong> vector = properties.at(index).value<QVector<qlonglong> >();
|
if (node.properties.isEmpty()) {
|
||||||
if (!vector.isEmpty()) {
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
for (; index < properties.size(); index++) {
|
|
||||||
vector.append(properties.at(index).toLongLong());
|
|
||||||
}
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<float> getFloatVector(const QVariantList& properties, int index) {
|
|
||||||
if (index >= properties.size()) {
|
|
||||||
return QVector<float>();
|
return QVector<float>();
|
||||||
}
|
}
|
||||||
QVector<float> vector = properties.at(index).value<QVector<float> >();
|
QVector<float> vector = node.properties.at(0).value<QVector<float> >();
|
||||||
if (!vector.isEmpty()) {
|
if (!vector.isEmpty()) {
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
for (; index < properties.size(); index++) {
|
for (int i = 0; i < node.properties.size(); i++) {
|
||||||
vector.append(properties.at(index).toFloat());
|
vector.append(node.properties.at(i).toFloat());
|
||||||
}
|
}
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<double> getDoubleVector(const QVariantList& properties, int index) {
|
QVector<double> getDoubleVector(const FBXNode& node) {
|
||||||
if (index >= properties.size()) {
|
foreach (const FBXNode& child, node.children) {
|
||||||
|
if (child.name == "a") {
|
||||||
|
return getDoubleVector(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node.properties.isEmpty()) {
|
||||||
return QVector<double>();
|
return QVector<double>();
|
||||||
}
|
}
|
||||||
QVector<double> vector = properties.at(index).value<QVector<double> >();
|
QVector<double> vector = node.properties.at(0).value<QVector<double> >();
|
||||||
if (!vector.isEmpty()) {
|
if (!vector.isEmpty()) {
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
for (; index < properties.size(); index++) {
|
for (int i = 0; i < node.properties.size(); i++) {
|
||||||
vector.append(properties.at(index).toDouble());
|
vector.append(node.properties.at(i).toDouble());
|
||||||
}
|
}
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
@ -697,21 +698,30 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
void appendIndex(MeshData& data, QVector<int>& indices, int index) {
|
void appendIndex(MeshData& data, QVector<int>& indices, int index) {
|
||||||
|
if (index >= data.polygonIndices.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
int vertexIndex = data.polygonIndices.at(index);
|
int vertexIndex = data.polygonIndices.at(index);
|
||||||
if (vertexIndex < 0) {
|
if (vertexIndex < 0) {
|
||||||
vertexIndex = -vertexIndex - 1;
|
vertexIndex = -vertexIndex - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vertex vertex;
|
Vertex vertex;
|
||||||
vertex.originalIndex = vertexIndex;
|
vertex.originalIndex = vertexIndex;
|
||||||
|
|
||||||
|
glm::vec3 position;
|
||||||
|
if (vertexIndex < data.vertices.size()) {
|
||||||
|
position = data.vertices.at(vertexIndex);
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec3 normal;
|
glm::vec3 normal;
|
||||||
if (data.normalIndices.isEmpty()) {
|
int normalIndex = data.normalsByVertex ? vertexIndex : index;
|
||||||
normal = data.normals.at(data.normalsByVertex ? vertexIndex : index);
|
if (data.normalIndices.isEmpty()) {
|
||||||
|
if (normalIndex < data.normals.size()) {
|
||||||
} else {
|
normal = data.normals.at(normalIndex);
|
||||||
int normalIndex = data.normalIndices.at(data.normalsByVertex ? vertexIndex : index);
|
}
|
||||||
if (normalIndex >= 0) {
|
} else if (normalIndex < data.normalIndices.size()) {
|
||||||
|
normalIndex = data.normalIndices.at(normalIndex);
|
||||||
|
if (normalIndex >= 0 && normalIndex < data.normals.size()) {
|
||||||
normal = data.normals.at(normalIndex);
|
normal = data.normals.at(normalIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -720,9 +730,9 @@ void appendIndex(MeshData& data, QVector<int>& indices, int index) {
|
||||||
if (index < data.texCoords.size()) {
|
if (index < data.texCoords.size()) {
|
||||||
vertex.texCoord = data.texCoords.at(index);
|
vertex.texCoord = data.texCoords.at(index);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (index < data.texCoordIndices.size()) {
|
||||||
int texCoordIndex = data.texCoordIndices.at(index);
|
int texCoordIndex = data.texCoordIndices.at(index);
|
||||||
if (texCoordIndex >= 0) {
|
if (texCoordIndex >= 0 && texCoordIndex < data.texCoords.size()) {
|
||||||
vertex.texCoord = data.texCoords.at(texCoordIndex);
|
vertex.texCoord = data.texCoords.at(texCoordIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -733,7 +743,7 @@ void appendIndex(MeshData& data, QVector<int>& indices, int index) {
|
||||||
indices.append(newIndex);
|
indices.append(newIndex);
|
||||||
data.indices.insert(vertex, newIndex);
|
data.indices.insert(vertex, newIndex);
|
||||||
data.extracted.newIndices.insert(vertexIndex, newIndex);
|
data.extracted.newIndices.insert(vertexIndex, newIndex);
|
||||||
data.extracted.mesh.vertices.append(data.vertices.at(vertexIndex));
|
data.extracted.mesh.vertices.append(position);
|
||||||
data.extracted.mesh.normals.append(normal);
|
data.extracted.mesh.normals.append(normal);
|
||||||
data.extracted.mesh.texCoords.append(vertex.texCoord);
|
data.extracted.mesh.texCoords.append(vertex.texCoord);
|
||||||
|
|
||||||
|
@ -749,44 +759,51 @@ ExtractedMesh extractMesh(const FBXNode& object) {
|
||||||
QVector<int> textures;
|
QVector<int> textures;
|
||||||
foreach (const FBXNode& child, object.children) {
|
foreach (const FBXNode& child, object.children) {
|
||||||
if (child.name == "Vertices") {
|
if (child.name == "Vertices") {
|
||||||
data.vertices = createVec3Vector(getDoubleVector(child.properties, 0));
|
data.vertices = createVec3Vector(getDoubleVector(child));
|
||||||
|
|
||||||
} else if (child.name == "PolygonVertexIndex") {
|
} else if (child.name == "PolygonVertexIndex") {
|
||||||
data.polygonIndices = getIntVector(child.properties, 0);
|
data.polygonIndices = getIntVector(child);
|
||||||
|
|
||||||
} else if (child.name == "LayerElementNormal") {
|
} else if (child.name == "LayerElementNormal") {
|
||||||
data.normalsByVertex = false;
|
data.normalsByVertex = false;
|
||||||
|
bool indexToDirect = false;
|
||||||
foreach (const FBXNode& subdata, child.children) {
|
foreach (const FBXNode& subdata, child.children) {
|
||||||
if (subdata.name == "Normals") {
|
if (subdata.name == "Normals") {
|
||||||
data.normals = createVec3Vector(getDoubleVector(subdata.properties, 0));
|
data.normals = createVec3Vector(getDoubleVector(subdata));
|
||||||
|
|
||||||
} else if (subdata.name == "NormalsIndex") {
|
} else if (subdata.name == "NormalsIndex") {
|
||||||
data.normalIndices = getIntVector(subdata.properties, 0);
|
data.normalIndices = getIntVector(subdata);
|
||||||
|
|
||||||
} else if (subdata.name == "MappingInformationType" &&
|
} else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == "ByVertice") {
|
||||||
subdata.properties.at(0) == "ByVertice") {
|
|
||||||
data.normalsByVertex = true;
|
data.normalsByVertex = true;
|
||||||
|
|
||||||
|
} else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == "IndexToDirect") {
|
||||||
|
indexToDirect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (indexToDirect && data.normalIndices.isEmpty()) {
|
||||||
|
// hack to work around wacky Makehuman exports
|
||||||
|
data.normalsByVertex = true;
|
||||||
|
}
|
||||||
} else if (child.name == "LayerElementUV" && child.properties.at(0).toInt() == 0) {
|
} else if (child.name == "LayerElementUV" && child.properties.at(0).toInt() == 0) {
|
||||||
foreach (const FBXNode& subdata, child.children) {
|
foreach (const FBXNode& subdata, child.children) {
|
||||||
if (subdata.name == "UV") {
|
if (subdata.name == "UV") {
|
||||||
data.texCoords = createVec2Vector(getDoubleVector(subdata.properties, 0));
|
data.texCoords = createVec2Vector(getDoubleVector(subdata));
|
||||||
|
|
||||||
} else if (subdata.name == "UVIndex") {
|
} else if (subdata.name == "UVIndex") {
|
||||||
data.texCoordIndices = getIntVector(subdata.properties, 0);
|
data.texCoordIndices = getIntVector(subdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (child.name == "LayerElementMaterial") {
|
} else if (child.name == "LayerElementMaterial") {
|
||||||
foreach (const FBXNode& subdata, child.children) {
|
foreach (const FBXNode& subdata, child.children) {
|
||||||
if (subdata.name == "Materials") {
|
if (subdata.name == "Materials") {
|
||||||
materials = getIntVector(subdata.properties, 0);
|
materials = getIntVector(subdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (child.name == "LayerElementTexture") {
|
} else if (child.name == "LayerElementTexture") {
|
||||||
foreach (const FBXNode& subdata, child.children) {
|
foreach (const FBXNode& subdata, child.children) {
|
||||||
if (subdata.name == "TextureId") {
|
if (subdata.name == "TextureId") {
|
||||||
textures = getIntVector(subdata.properties, 0);
|
textures = getIntVector(subdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -797,7 +814,7 @@ ExtractedMesh extractMesh(const FBXNode& object) {
|
||||||
QHash<QPair<int, int>, int> materialTextureParts;
|
QHash<QPair<int, int>, int> materialTextureParts;
|
||||||
for (int beginIndex = 0; beginIndex < data.polygonIndices.size(); polygonIndex++) {
|
for (int beginIndex = 0; beginIndex < data.polygonIndices.size(); polygonIndex++) {
|
||||||
int endIndex = beginIndex;
|
int endIndex = beginIndex;
|
||||||
while (data.polygonIndices.at(endIndex++) >= 0);
|
while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0);
|
||||||
|
|
||||||
QPair<int, int> materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0,
|
QPair<int, int> materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0,
|
||||||
(polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0);
|
(polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0);
|
||||||
|
@ -820,7 +837,7 @@ ExtractedMesh extractMesh(const FBXNode& object) {
|
||||||
appendIndex(data, part.triangleIndices, beginIndex);
|
appendIndex(data, part.triangleIndices, beginIndex);
|
||||||
appendIndex(data, part.triangleIndices, nextIndex++);
|
appendIndex(data, part.triangleIndices, nextIndex++);
|
||||||
appendIndex(data, part.triangleIndices, nextIndex);
|
appendIndex(data, part.triangleIndices, nextIndex);
|
||||||
if (data.polygonIndices.at(nextIndex) < 0) {
|
if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -835,13 +852,13 @@ FBXBlendshape extractBlendshape(const FBXNode& object) {
|
||||||
FBXBlendshape blendshape;
|
FBXBlendshape blendshape;
|
||||||
foreach (const FBXNode& data, object.children) {
|
foreach (const FBXNode& data, object.children) {
|
||||||
if (data.name == "Indexes") {
|
if (data.name == "Indexes") {
|
||||||
blendshape.indices = getIntVector(data.properties, 0);
|
blendshape.indices = getIntVector(data);
|
||||||
|
|
||||||
} else if (data.name == "Vertices") {
|
} else if (data.name == "Vertices") {
|
||||||
blendshape.vertices = createVec3Vector(getDoubleVector(data.properties, 0));
|
blendshape.vertices = createVec3Vector(getDoubleVector(data));
|
||||||
|
|
||||||
} else if (data.name == "Normals") {
|
} else if (data.name == "Normals") {
|
||||||
blendshape.normals = createVec3Vector(getDoubleVector(data.properties, 0));
|
blendshape.normals = createVec3Vector(getDoubleVector(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return blendshape;
|
return blendshape;
|
||||||
|
@ -1016,7 +1033,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
foreach (const FBXNode& object, child.children) {
|
foreach (const FBXNode& object, child.children) {
|
||||||
if (object.name == "SceneInfo") {
|
if (object.name == "SceneInfo") {
|
||||||
foreach (const FBXNode& subobject, object.children) {
|
foreach (const FBXNode& subobject, object.children) {
|
||||||
if (subobject.name == "Properties70") {
|
if (subobject.name == "MetaData") {
|
||||||
|
foreach (const FBXNode& subsubobject, subobject.children) {
|
||||||
|
if (subsubobject.name == "Author") {
|
||||||
|
geometry.author = subsubobject.properties.at(0).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (subobject.name == "Properties70") {
|
||||||
foreach (const FBXNode& subsubobject, subobject.children) {
|
foreach (const FBXNode& subsubobject, subobject.children) {
|
||||||
if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 &&
|
if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 &&
|
||||||
subsubobject.properties.at(0) == "Original|ApplicationName") {
|
subsubobject.properties.at(0) == "Original|ApplicationName") {
|
||||||
|
@ -1262,13 +1285,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
Cluster cluster;
|
Cluster cluster;
|
||||||
foreach (const FBXNode& subobject, object.children) {
|
foreach (const FBXNode& subobject, object.children) {
|
||||||
if (subobject.name == "Indexes") {
|
if (subobject.name == "Indexes") {
|
||||||
cluster.indices = getIntVector(subobject.properties, 0);
|
cluster.indices = getIntVector(subobject);
|
||||||
|
|
||||||
} else if (subobject.name == "Weights") {
|
} else if (subobject.name == "Weights") {
|
||||||
cluster.weights = getDoubleVector(subobject.properties, 0);
|
cluster.weights = getDoubleVector(subobject);
|
||||||
|
|
||||||
} else if (subobject.name == "TransformLink") {
|
} else if (subobject.name == "TransformLink") {
|
||||||
QVector<double> values = getDoubleVector(subobject.properties, 0);
|
QVector<double> values = getDoubleVector(subobject);
|
||||||
cluster.transformLink = createMat4(values);
|
cluster.transformLink = createMat4(values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1290,7 +1313,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
AnimationCurve curve;
|
AnimationCurve curve;
|
||||||
foreach (const FBXNode& subobject, object.children) {
|
foreach (const FBXNode& subobject, object.children) {
|
||||||
if (subobject.name == "KeyValueFloat") {
|
if (subobject.name == "KeyValueFloat") {
|
||||||
curve.values = getFloatVector(subobject.properties, 0);
|
curve.values = getFloatVector(subobject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
animationCurves.insert(getID(object.properties), curve);
|
animationCurves.insert(getID(object.properties), curve);
|
||||||
|
|
|
@ -47,6 +47,9 @@ public:
|
||||||
/// \return true if point is within current limits
|
/// \return true if point is within current limits
|
||||||
bool containsPoint(const glm::vec3& point) const;
|
bool containsPoint(const glm::vec3& point) const;
|
||||||
|
|
||||||
|
/// \return whether or not the extents are empty
|
||||||
|
bool isEmpty() { return minimum == maximum; }
|
||||||
|
|
||||||
glm::vec3 minimum;
|
glm::vec3 minimum;
|
||||||
glm::vec3 maximum;
|
glm::vec3 maximum;
|
||||||
};
|
};
|
||||||
|
@ -174,6 +177,7 @@ public:
|
||||||
class FBXGeometry {
|
class FBXGeometry {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
QString author;
|
||||||
QString applicationName; ///< the name of the application that generated the model
|
QString applicationName; ///< the name of the application that generated the model
|
||||||
|
|
||||||
QVector<FBXJoint> joints;
|
QVector<FBXJoint> joints;
|
||||||
|
|
|
@ -55,9 +55,7 @@ public:
|
||||||
|
|
||||||
void requestAccessToken(const QString& login, const QString& password);
|
void requestAccessToken(const QString& login, const QString& password);
|
||||||
|
|
||||||
QString getUsername() const { return _accountInfo.getUsername(); }
|
const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; }
|
||||||
|
|
||||||
const QString& getXMPPPassword() const { return _accountInfo.getXMPPPassword(); }
|
|
||||||
|
|
||||||
void destroy() { delete _networkAccessManager; }
|
void destroy() { delete _networkAccessManager; }
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,8 @@ Assignment::Assignment() :
|
||||||
_type(Assignment::AllTypes),
|
_type(Assignment::AllTypes),
|
||||||
_pool(),
|
_pool(),
|
||||||
_location(Assignment::LocalLocation),
|
_location(Assignment::LocalLocation),
|
||||||
_payload()
|
_payload(),
|
||||||
|
_isStatic(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -61,7 +62,8 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const
|
||||||
_type(type),
|
_type(type),
|
||||||
_pool(pool),
|
_pool(pool),
|
||||||
_location(location),
|
_location(location),
|
||||||
_payload()
|
_payload(),
|
||||||
|
_isStatic(false)
|
||||||
{
|
{
|
||||||
if (_command == Assignment::CreateCommand) {
|
if (_command == Assignment::CreateCommand) {
|
||||||
// this is a newly created assignment, generate a random UUID
|
// this is a newly created assignment, generate a random UUID
|
||||||
|
|
|
@ -78,6 +78,9 @@ public:
|
||||||
void setPool(const QString& pool) { _pool = pool; };
|
void setPool(const QString& pool) { _pool = pool; };
|
||||||
const QString& getPool() const { return _pool; }
|
const QString& getPool() const { return _pool; }
|
||||||
|
|
||||||
|
void setIsStatic(bool isStatic) { _isStatic = isStatic; }
|
||||||
|
bool isStatic() const { return _isStatic; }
|
||||||
|
|
||||||
const char* getTypeName() const;
|
const char* getTypeName() const;
|
||||||
|
|
||||||
// implement parseData to return 0 so we can be a subclass of NodeData
|
// implement parseData to return 0 so we can be a subclass of NodeData
|
||||||
|
@ -94,6 +97,7 @@ protected:
|
||||||
QString _pool; /// the destination pool for this assignment
|
QString _pool; /// the destination pool for this assignment
|
||||||
Assignment::Location _location; /// the location of the assignment, allows a domain to preferentially use local ACs
|
Assignment::Location _location; /// the location of the assignment, allows a domain to preferentially use local ACs
|
||||||
QByteArray _payload; /// an optional payload attached to this assignment, a maximum for 1024 bytes will be packed
|
QByteArray _payload; /// an optional payload attached to this assignment, a maximum for 1024 bytes will be packed
|
||||||
|
bool _isStatic; /// defines if this assignment needs to be re-queued in the domain-server if it stops being fulfilled
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Assignment_h
|
#endif // hifi_Assignment_h
|
||||||
|
|
|
@ -78,7 +78,7 @@ gnutls_datum_t* DTLSSession::highFidelityCADatum() {
|
||||||
static bool datumInitialized = false;
|
static bool datumInitialized = false;
|
||||||
|
|
||||||
static unsigned char HIGHFIDELITY_ROOT_CA_CERT[] =
|
static unsigned char HIGHFIDELITY_ROOT_CA_CERT[] =
|
||||||
"-----BEGIN CERTIFICATE-----"
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
"MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
|
"MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
|
||||||
"VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n"
|
"VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n"
|
||||||
"aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n"
|
"aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n"
|
||||||
|
@ -100,7 +100,7 @@ gnutls_datum_t* DTLSSession::highFidelityCADatum() {
|
||||||
"SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n"
|
"SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n"
|
||||||
"FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n"
|
"FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n"
|
||||||
"Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n"
|
"Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n"
|
||||||
"-----END CERTIFICATE-----";
|
"-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
if (!datumInitialized) {
|
if (!datumInitialized) {
|
||||||
hifiCADatum.data = HIGHFIDELITY_ROOT_CA_CERT;
|
hifiCADatum.data = HIGHFIDELITY_ROOT_CA_CERT;
|
||||||
|
|
|
@ -22,8 +22,11 @@
|
||||||
#include "HifiSockAddr.h"
|
#include "HifiSockAddr.h"
|
||||||
|
|
||||||
const QString DEFAULT_DOMAIN_HOSTNAME = "alpha.highfidelity.io";
|
const QString DEFAULT_DOMAIN_HOSTNAME = "alpha.highfidelity.io";
|
||||||
|
|
||||||
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
|
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
|
||||||
const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
|
const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
|
||||||
|
const quint16 DOMAIN_SERVER_HTTP_PORT = 40100;
|
||||||
|
const quint16 DOMAIN_SERVER_HTTPS_PORT = 40101;
|
||||||
|
|
||||||
class DomainHandler : public QObject {
|
class DomainHandler : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
|
@ -106,6 +106,10 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() {
|
||||||
|
|
||||||
_dtlsSocket->bind(QHostAddress::AnyIPv4, 0, QAbstractSocket::DontShareAddress);
|
_dtlsSocket->bind(QHostAddress::AnyIPv4, 0, QAbstractSocket::DontShareAddress);
|
||||||
|
|
||||||
|
// we're using DTLS and our socket is good to go, so make the required DTLS changes
|
||||||
|
// DTLS requires that IP_DONTFRAG be set
|
||||||
|
// This is not accessible on some platforms (OS X) so we need to make sure DTLS still works without it
|
||||||
|
|
||||||
#if defined(IP_DONTFRAG) || defined(IP_MTU_DISCOVER)
|
#if defined(IP_DONTFRAG) || defined(IP_MTU_DISCOVER)
|
||||||
qDebug() << "Making required DTLS changes to LimitedNodeList DTLS socket.";
|
qDebug() << "Making required DTLS changes to LimitedNodeList DTLS socket.";
|
||||||
|
|
||||||
|
|
|
@ -378,12 +378,17 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketType domainPacketType = _sessionUUID.isNull()
|
PacketType domainPacketType = !_domainHandler.isConnected()
|
||||||
? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
|
? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
|
||||||
|
|
||||||
// construct the DS check in packet
|
// construct the DS check in packet
|
||||||
QUuid packetUUID = (domainPacketType == PacketTypeDomainListRequest
|
QUuid packetUUID = _sessionUUID;
|
||||||
? _sessionUUID : _domainHandler.getAssignmentUUID());
|
|
||||||
|
if (!_domainHandler.getAssignmentUUID().isNull() && domainPacketType == PacketTypeDomainConnectRequest) {
|
||||||
|
// this is a connect request and we're an assigned node
|
||||||
|
// so set our packetUUID as the assignment UUID
|
||||||
|
packetUUID = _domainHandler.getAssignmentUUID();
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID);
|
QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID);
|
||||||
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
||||||
|
@ -404,7 +409,6 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
dtlsSession->writeDatagram(domainServerPacket);
|
dtlsSession->writeDatagram(domainServerPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
||||||
static unsigned int numDomainCheckins = 0;
|
static unsigned int numDomainCheckins = 0;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ enum PacketType {
|
||||||
PacketTypeDomainListRequest,
|
PacketTypeDomainListRequest,
|
||||||
PacketTypeRequestAssignment,
|
PacketTypeRequestAssignment,
|
||||||
PacketTypeCreateAssignment,
|
PacketTypeCreateAssignment,
|
||||||
PacketTypeDataServerPut, // reusable
|
PacketTypeDomainOAuthRequest,
|
||||||
PacketTypeDataServerGet, // reusable
|
PacketTypeDataServerGet, // reusable
|
||||||
PacketTypeDataServerSend, // reusable
|
PacketTypeDataServerSend, // reusable
|
||||||
PacketTypeDataServerConfirm,
|
PacketTypeDataServerConfirm,
|
||||||
|
@ -72,7 +72,7 @@ typedef char PacketVersion;
|
||||||
|
|
||||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||||
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
|
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
|
||||||
<< PacketTypeDomainList << PacketTypeDomainListRequest
|
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest
|
||||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
||||||
<< PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeModelQuery;
|
<< PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeModelQuery;
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,10 @@ glm::vec3 Vec3::cross(const glm::vec3& v1, const glm::vec3& v2) {
|
||||||
return glm::cross(v1,v2);
|
return glm::cross(v1,v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Vec3::dot(const glm::vec3& v1, const glm::vec3& v2) {
|
||||||
|
return glm::dot(v1,v2);
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) {
|
glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) {
|
||||||
return v1 * f;
|
return v1 * f;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ class Vec3 : public QObject {
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2);
|
glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2);
|
||||||
|
float dot(const glm::vec3& v1, const glm::vec3& v2);
|
||||||
glm::vec3 multiply(const glm::vec3& v1, float f);
|
glm::vec3 multiply(const glm::vec3& v1, float f);
|
||||||
glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v);
|
glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v);
|
||||||
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2);
|
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2);
|
||||||
|
|
Loading…
Reference in a new issue