Merge branch 'master' of https://github.com/worklist/hifi into modelserver

This commit is contained in:
ZappoMan 2014-05-05 12:36:03 -07:00
commit 3bb72c04a5
48 changed files with 1384 additions and 497 deletions

View file

@ -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
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.
@ -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.
./assignment-client -n 5
./assignment-client -n 6
To test things out you'll want to run the Interface client.

View file

@ -191,9 +191,10 @@ void Agent::run() {
// figure out the URL for the script for this agent assignment
QUrl scriptURL;
if (_payload.isEmpty()) {
scriptURL = QUrl(QString("http://%1:8080/assignment/%2")
.arg(NodeList::getInstance()->getDomainHandler().getIP().toString(),
uuidStringWithoutCurlyBraces(_uuid)));
scriptURL = QUrl(QString("http://%1:%2/assignment/%3")
.arg(NodeList::getInstance()->getDomainHandler().getIP().toString())
.arg(DOMAIN_SERVER_HTTP_PORT)
.arg(uuidStringWithoutCurlyBraces(_uuid)));
} else {
scriptURL = QUrl(_payload);
}

View file

@ -16,6 +16,7 @@
#include <QtCore/QProcess>
#include <QtCore/QStandardPaths>
#include <QtCore/QTimer>
#include <QtCore/QUrlQuery>
#include <gnutls/dtls.h>
@ -31,18 +32,22 @@
#include "DomainServer.h"
const quint16 DOMAIN_SERVER_HTTP_PORT = 8080;
DomainServer::DomainServer(int argc, char* argv[]) :
QCoreApplication(argc, argv),
_HTTPManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
_staticAssignmentHash(),
_assignmentQueue(),
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
_httpsManager(NULL),
_allAssignments(),
_unfulfilledAssignments(),
_isUsingDTLS(false),
_x509Credentials(NULL),
_dhParams(NULL),
_priorityCache(NULL),
_dtlsSessions()
_dtlsSessions(),
_oauthProviderURL(),
_oauthClientID(),
_hostname(),
_networkReplyUUIDMap(),
_sessionAuthenticationHash()
{
gnutls_global_init();
@ -53,22 +58,20 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_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
// and set up the node list
qDebug() << "Setting up LimitedNodeList and assignments.";
setupNodeListAndAssignments();
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();
// connect our socket to read datagrams received on the DTLS socket
connect(&nodeList->getDTLSSocket(), &QUdpSocket::readyRead, this, &DomainServer::readAvailableDTLSDatagrams);
}
_networkAccessManager = new QNetworkAccessManager(this);
}
}
@ -86,44 +89,7 @@ DomainServer::~DomainServer() {
gnutls_global_deinit();
}
bool DomainServer::optionallySetupDTLS() {
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() {
bool DomainServer::optionallyReadX509KeyAndCertificate() {
const QString X509_CERTIFICATE_OPTION = "cert";
const QString X509_PRIVATE_KEY_OPTION = "key";
const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE";
@ -157,6 +123,22 @@ bool DomainServer::readX509KeyAndCertificate() {
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()) {
qDebug() << "Missing certificate or private key. domain-server will now quit.";
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
@ -166,6 +148,67 @@ bool DomainServer::readX509KeyAndCertificate() {
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) {
const QString CUSTOM_PORT_OPTION = "port";
@ -246,7 +289,8 @@ void DomainServer::parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes)
void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment) {
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) {
@ -279,7 +323,9 @@ void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configAr
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
_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
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer << NodeType::ModelServer
<< NodeType::MetavoxelServer;
void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
NodeType_t nodeType;
HifiSockAddr publicSockAddr, localSockAddr;
int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr);
QUuid assignmentUUID = uuidFromPacketHeader(packet);
bool isStaticAssignment = _staticAssignmentHash.contains(assignmentUUID);
SharedAssignmentPointer matchingAssignment = SharedAssignmentPointer();
QUuid packetUUID = uuidFromPacketHeader(packet);
if (isStaticAssignment) {
// this is a static assignment, make sure the UUID sent is for an assignment we're actually trying to give out
matchingAssignment = matchingQueuedAssignmentForCheckIn(assignmentUUID, nodeType);
// check if this connect request matches an assignment in the queue
bool isFulfilledOrUnfulfilledAssignment = _allAssignments.contains(packetUUID);
SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer();
if (isFulfilledOrUnfulfilledAssignment) {
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);
if (!matchingQueuedAssignment && !_oauthProviderURL.isEmpty() && _argumentVariantMap.contains(ALLOWED_ROLES_CONFIG_KEY)) {
// 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 {
assignmentUUID = QUuid();
// 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;
}
}
// make sure this was either not a static assignment or it was and we had a matching one in teh queue
if ((!isStaticAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) || (isStaticAssignment && matchingAssignment)) {
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
QUuid nodeUUID = QUuid::createUuid();
SharedNodePointer newNode = LimitedNodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType,
publicSockAddr, localSockAddr);
// when the newNode is created the linked data is also created
// if this was a static assignment set the UUID, set the sendingSockAddr
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
nodeData->setStaticAssignmentUUID(assignmentUUID);
if (isFulfilledOrUnfulfilledAssignment) {
nodeData->setAssignmentUUID(packetUUID);
}
nodeData->setSendingSockAddr(senderSockAddr);
// 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,
HifiSockAddr& localSockAddr, const QByteArray& packet,
const HifiSockAddr& senderSockAddr) {
@ -451,7 +555,8 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
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()) {
// 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
@ -499,6 +604,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
broadcastPacket.append(nodeByteArray);
}
}
}
// always write the last broadcastPacket
if (!dtlsSession) {
@ -670,9 +776,7 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS
PacketType requestType = packetTypeForPacket(receivedPacket);
if (requestType == PacketTypeDomainConnectRequest) {
// add this node to our NodeList
// and send back session UUID right away
addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr);
handleConnectRequest(receivedPacket, senderSockAddr);
} else if (requestType == PacketTypeDomainListRequest) {
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
@ -738,7 +842,8 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp());
// 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) {
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
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
QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());
QString uuidString = uuidStringWithoutCurlyBraces(nodeData->getAssignmentUUID());
assignedNodesJSON[uuidString] = jsonObjectForNode(node);
}
}
@ -786,7 +893,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
QJsonObject queuedAssignmentsJSON;
// add the queued but unfilled assignments to the json
foreach(const SharedAssignmentPointer& assignment, _assignmentQueue) {
foreach(const SharedAssignmentPointer& assignment, _unfulfilledAssignments) {
QJsonObject queuedAssignmentJSON;
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));
// 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
@ -950,6 +1059,114 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
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) {
QUuid oldUUID = assignment->getUUID();
assignment->resetUUID();
@ -963,13 +1180,8 @@ void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer&
}
// add the static assignment back under the right UUID, and to the queue
_staticAssignmentHash.insert(assignment->getUUID(), 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);
_allAssignments.insert(assignment->getUUID(), assignment);
_unfulfilledAssignments.enqueue(assignment);
}
void DomainServer::nodeAdded(SharedNodePointer node) {
@ -983,10 +1195,10 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
if (nodeData) {
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
if (!nodeData->getStaticAssignmentUUID().isNull()) {
SharedAssignmentPointer matchedAssignment = _staticAssignmentHash.value(nodeData->getStaticAssignmentUUID());
if (!nodeData->getAssignmentUUID().isNull()) {
SharedAssignmentPointer matchedAssignment = _allAssignments.take(nodeData->getAssignmentUUID());
if (matchedAssignment) {
if (matchedAssignment && matchedAssignment->isStatic()) {
refreshStaticAssignmentAndAddToQueue(matchedAssignment);
}
}
@ -1010,11 +1222,11 @@ void DomainServer::nodeKilled(SharedNodePointer node) {
}
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) {
return _assignmentQueue.takeAt(i - _assignmentQueue.begin());
return _unfulfilledAssignments.takeAt(i - _unfulfilledAssignments.begin());
} else {
++i;
}
@ -1026,9 +1238,9 @@ SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const Q
SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assignment& requestAssignment) {
// 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
QQueue<SharedAssignmentPointer>::iterator sharedAssignment = _assignmentQueue.begin();
QQueue<SharedAssignmentPointer>::iterator sharedAssignment = _unfulfilledAssignments.begin();
while (sharedAssignment != _assignmentQueue.end()) {
while (sharedAssignment != _unfulfilledAssignments.end()) {
Assignment* assignment = sharedAssignment->data();
bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes;
bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType();
@ -1038,16 +1250,12 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig
if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) {
// remove the assignment from the queue
SharedAssignmentPointer deployableAssignment = _assignmentQueue.takeAt(sharedAssignment
- _assignmentQueue.begin());
SharedAssignmentPointer deployableAssignment = _unfulfilledAssignments.takeAt(sharedAssignment
- _unfulfilledAssignments.begin());
if (deployableAssignment->getType() != Assignment::AgentType
|| _staticAssignmentHash.contains(deployableAssignment->getUUID())) {
// this is a static assignment
// until we get a check-in from that GUID
// until we get a connection for this assignment
// put assignment back in queue but stick it at the back so the others have a chance to go out
_assignmentQueue.enqueue(deployableAssignment);
}
_unfulfilledAssignments.enqueue(deployableAssignment);
// stop looping, we've handed out an assignment
return deployableAssignment;
@ -1061,10 +1269,10 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig
}
void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment) {
QQueue<SharedAssignmentPointer>::iterator potentialMatchingAssignment = _assignmentQueue.begin();
while (potentialMatchingAssignment != _assignmentQueue.end()) {
QQueue<SharedAssignmentPointer>::iterator potentialMatchingAssignment = _unfulfilledAssignments.begin();
while (potentialMatchingAssignment != _unfulfilledAssignments.end()) {
if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) {
_assignmentQueue.erase(potentialMatchingAssignment);
_unfulfilledAssignments.erase(potentialMatchingAssignment);
// we matched and removed an assignment, bail out
break;
@ -1078,7 +1286,7 @@ void DomainServer::addStaticAssignmentsToQueue() {
// if the domain-server has just restarted,
// 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();
while (staticAssignment != staticHashCopy.end()) {
// add any of the un-matched static assignments to the queue

View file

@ -23,20 +23,21 @@
#include <gnutls/gnutls.h>
#include <Assignment.h>
#include <HTTPManager.h>
#include <HTTPSConnection.h>
#include <LimitedNodeList.h>
#include "DTLSServerSession.h"
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
class DomainServer : public QCoreApplication, public HTTPRequestHandler {
class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
Q_OBJECT
public:
DomainServer(int argc, char* argv[]);
~DomainServer();
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url);
void exit(int retCode = 0);
@ -52,12 +53,13 @@ private slots:
void readAvailableDTLSDatagrams();
private:
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
bool optionallySetupOAuth();
bool optionallySetupDTLS();
bool readX509KeyAndCertificate();
bool optionallyReadX509KeyAndCertificate();
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,
HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr);
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
@ -76,13 +78,20 @@ private:
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
void addStaticAssignmentsToQueue();
QUrl oauthRedirectURL();
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());
void handleTokenRequestFinished();
void handleProfileRequestFinished();
QJsonObject jsonForSocket(const HifiSockAddr& socket);
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
HTTPManager _HTTPManager;
HTTPManager _httpManager;
HTTPSManager* _httpsManager;
QHash<QUuid, SharedAssignmentPointer> _staticAssignmentHash;
QQueue<SharedAssignmentPointer> _assignmentQueue;
QHash<QUuid, SharedAssignmentPointer> _allAssignments;
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
QVariantMap _argumentVariantMap;
@ -93,6 +102,15 @@ private:
gnutls_priority_t* _priorityCache;
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

View file

@ -19,9 +19,10 @@
DomainServerNodeData::DomainServerNodeData() :
_sessionSecretHash(),
_staticAssignmentUUID(),
_assignmentUUID(),
_statsJSONObject(),
_sendingSockAddr()
_sendingSockAddr(),
_isAuthenticated(true)
{
}

View file

@ -27,20 +27,24 @@ public:
void parseJSONStatsPacket(const QByteArray& statsPacket);
void setStaticAssignmentUUID(const QUuid& staticAssignmentUUID) { _staticAssignmentUUID = staticAssignmentUUID; }
const QUuid& getStaticAssignmentUUID() const { return _staticAssignmentUUID; }
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; }
const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; }
void setIsAuthenticated(bool isAuthenticated) { _isAuthenticated = isAuthenticated; }
bool isAuthenticated() const { return _isAuthenticated; }
QHash<QUuid, QUuid>& getSessionSecretHash() { return _sessionSecretHash; }
private:
QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject);
QHash<QUuid, QUuid> _sessionSecretHash;
QUuid _staticAssignmentUUID;
QUuid _assignmentUUID;
QJsonObject _statsJSONObject;
HifiSockAddr _sendingSockAddr;
bool _isAuthenticated;
};
#endif // hifi_DomainServerNodeData_h

342
examples/editModels.js Normal file
View 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);

View file

@ -81,9 +81,10 @@
#include "scripting/LocationScriptingInterface.h"
#include "ui/InfoView.h"
#include "ui/OAuthWebViewHandler.h"
#include "ui/Snapshot.h"
#include "ui/TextRenderer.h"
#include "ui/Stats.h"
#include "ui/TextRenderer.h"
using namespace std;
@ -367,6 +368,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
//When -url in command line, teleport to location
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() {
@ -555,41 +561,6 @@ void Application::paintGL() {
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
_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) {
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
@ -3124,9 +3095,9 @@ void Application::updateWindowTitle(){
QString buildVersion = " (build " + applicationVersion() + ")";
NodeList* nodeList = NodeList::getInstance();
QString username = AccountManager::getInstance().getUsername();
QString title = QString() + (!username.isEmpty() ? username + " " : QString()) + nodeList->getSessionUUID().toString()
+ " @ " + nodeList->getDomainHandler().getHostname() + buildVersion;
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
+ nodeList->getDomainHandler().getHostname() + buildVersion;
qDebug("Application title set to: %s", title.toStdString().c_str());
_window->setWindowTitle(title);
}
@ -3150,6 +3121,9 @@ void Application::domainChanged(const QString& domainHostname) {
// reset the voxels renderer
_voxels.killLocalVoxels();
// reset the auth URL for OAuth web view handler
OAuthWebViewHandler::getInstance().clearLastAuthorizationURL();
}
void Application::connectedToDomain(const QString& hostname) {

View file

@ -15,6 +15,7 @@
#include "Application.h"
#include "Menu.h"
#include "ui/OAuthWebViewHandler.h"
#include "DatagramProcessor.h"
@ -56,13 +57,11 @@ void DatagramProcessor::processDatagrams() {
Particle::handleAddParticleResponse(incomingPacket);
application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket);
break;
case PacketTypeModelAddResponse:
// this will keep creatorTokenIDs to IDs mapped correctly
ModelItem::handleAddModelResponse(incomingPacket);
application->getModels()->getTree()->handleAddModelResponse(incomingPacket);
break;
case PacketTypeParticleData:
case PacketTypeParticleErase:
case PacketTypeModelData:
@ -120,6 +119,18 @@ void DatagramProcessor::processDatagrams() {
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size());
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:
nodeList->processNodeData(senderSockAddr, incomingPacket);
break;

View file

@ -336,7 +336,6 @@ Menu::Menu() :
SLOT(setFilter(bool)));
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, true);
@ -1100,7 +1099,7 @@ void Menu::toggleLoginMenuItem() {
if (accountManager.isLoggedIn()) {
// change the menu item to logout
_loginAction->setText("Logout " + accountManager.getUsername());
_loginAction->setText("Logout " + accountManager.getAccountInfo().getUsername());
connect(_loginAction, &QAction::triggered, &accountManager, &AccountManager::logout);
} else {
// change the menu item to login

View file

@ -340,7 +340,6 @@ namespace MenuOption {
const QString Particles = "Particles";
const QString PasteToVoxel = "Paste to Voxel...";
const QString PipelineWarnings = "Show Render Pipeline Warnings";
const QString PlaySlaps = "Play Slaps";
const QString Preferences = "Preferences...";
const QString Quit = "Quit";
const QString ReloadAllScripts = "Reload All Scripts";

View file

@ -153,14 +153,14 @@ bool ModelUploader::zip() {
// mixamo/autodesk defaults
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();
if (!joints.contains("jointEyeLeft")) {
joints.insert("jointEyeLeft", "LeftEye");
joints.insert("jointEyeLeft", geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye");
}
if (!joints.contains("jointEyeRight")) {
joints.insert("jointEyeRight", "RightEye");
joints.insert("jointEyeRight", geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye");
}
if (!joints.contains("jointNeck")) {
joints.insert("jointNeck", "Neck");
@ -172,7 +172,8 @@ bool ModelUploader::zip() {
joints.insert("jointLean", "Spine");
}
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")) {
joints.insert("jointLeftHand", "LeftHand");
@ -600,7 +601,7 @@ static void setJointText(QComboBox* box, const QString& text) {
void ModelPropertiesDialog::reset() {
_name->setText(_originalMapping.value(NAME_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();
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());

View file

@ -34,12 +34,13 @@ XmppClient& XmppClient::getInstance() {
void XmppClient::xmppConnected() {
_publicChatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM);
_publicChatRoom->setNickName(AccountManager::getInstance().getUsername());
_publicChatRoom->setNickName(AccountManager::getInstance().getAccountInfo().getUsername());
_publicChatRoom->join();
}
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() {
@ -50,8 +51,8 @@ void XmppClient::connectToServer() {
connect(&_xmppClient, SIGNAL(error(QXmppClient::Error)), this, SLOT(xmppError(QXmppClient::Error)));
}
AccountManager& accountManager = AccountManager::getInstance();
QString user = accountManager.getUsername();
const QString& password = accountManager.getXMPPPassword();
QString user = accountManager.getAccountInfo().getUsername();
const QString& password = accountManager.getAccountInfo().getXMPPPassword();
_xmppClient.connectToServer(user + "@" + DEFAULT_XMPP_SERVER, password);
}

View file

@ -776,8 +776,17 @@ float Avatar::getSkeletonHeight() const {
float Avatar::getHeadHeight() const {
Extents extents = getHead()->getFaceModel().getBindExtents();
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 {
if (!collision._data || collision._type != MODEL_COLLISION) {

View file

@ -29,11 +29,11 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
neckPosition = owningAvatar->getPosition();
}
setTranslation(neckPosition);
glm::quat neckRotation;
if (!owningAvatar->getSkeletonModel().getNeckRotation(neckRotation)) {
neckRotation = owningAvatar->getOrientation();
glm::quat neckParentRotation;
if (!owningAvatar->getSkeletonModel().getNeckParentRotation(neckParentRotation)) {
neckParentRotation = owningAvatar->getOrientation();
}
setRotation(neckRotation);
setRotation(neckParentRotation);
const float MODEL_SCALE = 0.0006f;
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale() * MODEL_SCALE);

View file

@ -26,8 +26,6 @@ public:
virtual void simulate(float deltaTime, bool fullUpdate = true);
protected:
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);

View file

@ -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.
const float MAX_COLLISIONS_PER_AVATAR = 32;
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) {
if (!avatar || avatar == _owningAvatar) {
// don't collide hands against ourself (that is done elsewhere)

View file

@ -58,7 +58,6 @@ public:
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [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 collideAgainstOurself();
@ -80,8 +79,6 @@ private:
void renderLeapFingerTrails();
void calculateGeometry();
void playSlaps(PalmData& palm, Avatar* avatar);
};
#endif // hifi_Hand_h

View file

@ -161,7 +161,9 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
_leftEyePosition = _rightEyePosition = getPosition();
if (!billboard) {
_faceModel.simulate(deltaTime);
_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition);
if (!_faceModel.getEyePositions(_leftEyePosition, _rightEyePosition)) {
static_cast<Avatar*>(_owningAvatar)->getSkeletonModel().getEyePositions(_leftEyePosition, _rightEyePosition);
}
}
_eyePosition = calculateAverageEyePosition();
}

View file

@ -221,6 +221,14 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
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) {
if (jointIndex == -1) {
return;

View file

@ -46,6 +46,8 @@ protected:
virtual void updateJointState(int index);
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:

View file

@ -434,6 +434,17 @@ bool Model::getNeckRotation(glm::quat& neckRotation) const {
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 {
if (!isActive()) {
return false;

View file

@ -132,6 +132,10 @@ public:
/// \return whether or not the neck was found
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.
/// \return whether or not both eye meshes were found
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;

View file

@ -254,7 +254,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
}
// 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
ChatMessageArea* messageArea = new ChatMessageArea(true);

View 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();
}

View 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

View file

@ -86,7 +86,7 @@ void Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
// replace decimal . with '-'
formattedLocation.replace('.', '-');
QString username = AccountManager::getInstance().getUsername();
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
// normalize username, replace all non alphanumeric with '-'
username.replace(QRegExp("[^A-Za-z0-9_]"), "-");

View file

@ -33,9 +33,9 @@ void Sphere3DOverlay::render() {
glDisable(GL_LIGHTING);
glPushMatrix();
glTranslatef(_position.x + _size * 0.5f,
_position.y + _size * 0.5f,
_position.z + _size * 0.5f);
glTranslatef(_position.x,
_position.y,
_position.z);
glLineWidth(_lineWidth);
const int slices = 15;
if (_isSolid) {

View file

@ -28,8 +28,8 @@ HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager)
_parentManager(parentManager),
_socket(socket),
_stream(socket),
_address(socket->peerAddress()) {
_address(socket->peerAddress())
{
// take over ownership of the socket
_socket->setParent(this);
@ -42,7 +42,7 @@ HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager)
HTTPConnection::~HTTPConnection() {
// log the destruction
if (_socket->error() != QAbstractSocket::UnknownSocketError) {
qDebug() << _socket->errorString();
qDebug() << _socket->errorString() << "-" << _socket->error();
}
}

View file

@ -18,10 +18,10 @@
#include <QDataStream>
#include <QHash>
#include <QHostAddress>
#include <QtNetwork/QHostAddress>
#include <QIODevice>
#include <QList>
#include <QNetworkAccessManager>
#include <QtNetwork/QNetworkAccessManager>
#include <QObject>
#include <QPair>
#include <QUrl>

View file

@ -18,13 +18,36 @@
#include "HTTPConnection.h"
#include "HTTPManager.h"
HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) :
QTcpServer(parent),
_documentRoot(documentRoot),
_requestHandler(requestHandler)
{
// start listening on the passed port
if (!listen(QHostAddress("0.0.0.0"), port)) {
qDebug() << "Failed to open HTTP server socket:" << errorString();
return;
}
}
void HTTPManager::incomingConnection(qintptr socketDescriptor) {
QTcpSocket* socket = new QTcpSocket(this);
if (socket->setSocketDescriptor(socketDescriptor)) {
new HTTPConnection(socket, this);
} else {
delete socket;
}
}
bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) {
if (_requestHandler && _requestHandler->handleHTTPRequest(connection, url)) {
// this request was handled by our _requestHandler object
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();
@ -119,33 +142,17 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url)
connection->respond(HTTPConnection::StatusCode200, localFileData,
qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
} else {
return true;
}
}
// respond with a 404
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
}
return true;
}
HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) :
QTcpServer(parent),
_documentRoot(documentRoot),
_requestHandler(requestHandler)
{
// start listening on the passed port
if (!listen(QHostAddress("0.0.0.0"), port)) {
qDebug() << "Failed to open HTTP server socket:" << errorString();
return;
}
// connect the connection signal
connect(this, SIGNAL(newConnection()), SLOT(acceptConnections()));
}
void HTTPManager::acceptConnections() {
QTcpSocket* socket;
while ((socket = nextPendingConnection()) != 0) {
new HTTPConnection(socket, this);
}
bool HTTPManager::requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url) {
return _requestHandler && _requestHandler->handleHTTPRequest(connection, url);
}

View file

@ -19,6 +19,7 @@
#include <QtNetwork/QTcpServer>
class HTTPConnection;
class HTTPSConnection;
class HTTPRequestHandler {
public:
@ -35,9 +36,10 @@ public:
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
protected slots:
protected:
/// Accepts all pending connections
void acceptConnections();
virtual void incomingConnection(qintptr socketDescriptor);
virtual bool requestHandledByRequestHandler(HTTPConnection* connection, const QUrl& url);
protected:
QString _documentRoot;
HTTPRequestHandler* _requestHandler;

View 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;
}

View 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

View 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);
}

View 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

View file

@ -406,7 +406,7 @@ QVariantHash parseMapping(QIODevice* device) {
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
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 y = *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> 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 t = *it++;
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));
}
QVector<int> getIntVector(const QVariantList& properties, int index) {
if (index >= properties.size()) {
QVector<int> getIntVector(const FBXNode& node) {
foreach (const FBXNode& child, node.children) {
if (child.name == "a") {
return getIntVector(child);
}
}
if (node.properties.isEmpty()) {
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()) {
return vector;
}
for (; index < properties.size(); index++) {
vector.append(properties.at(index).toInt());
for (int i = 0; i < node.properties.size(); i++) {
vector.append(node.properties.at(i).toInt());
}
return vector;
}
QVector<qlonglong> getLongVector(const QVariantList& properties, int index) {
if (index >= properties.size()) {
return QVector<qlonglong>();
QVector<float> getFloatVector(const FBXNode& node) {
foreach (const FBXNode& child, node.children) {
if (child.name == "a") {
return getFloatVector(child);
}
QVector<qlonglong> vector = properties.at(index).value<QVector<qlonglong> >();
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()) {
if (node.properties.isEmpty()) {
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()) {
return vector;
}
for (; index < properties.size(); index++) {
vector.append(properties.at(index).toFloat());
for (int i = 0; i < node.properties.size(); i++) {
vector.append(node.properties.at(i).toFloat());
}
return vector;
}
QVector<double> getDoubleVector(const QVariantList& properties, int index) {
if (index >= properties.size()) {
QVector<double> getDoubleVector(const FBXNode& node) {
foreach (const FBXNode& child, node.children) {
if (child.name == "a") {
return getDoubleVector(child);
}
}
if (node.properties.isEmpty()) {
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()) {
return vector;
}
for (; index < properties.size(); index++) {
vector.append(properties.at(index).toDouble());
for (int i = 0; i < node.properties.size(); i++) {
vector.append(node.properties.at(i).toDouble());
}
return vector;
}
@ -697,21 +698,30 @@ public:
};
void appendIndex(MeshData& data, QVector<int>& indices, int index) {
if (index >= data.polygonIndices.size()) {
return;
}
int vertexIndex = data.polygonIndices.at(index);
if (vertexIndex < 0) {
vertexIndex = -vertexIndex - 1;
}
Vertex vertex;
vertex.originalIndex = vertexIndex;
glm::vec3 normal;
if (data.normalIndices.isEmpty()) {
normal = data.normals.at(data.normalsByVertex ? vertexIndex : index);
glm::vec3 position;
if (vertexIndex < data.vertices.size()) {
position = data.vertices.at(vertexIndex);
}
} else {
int normalIndex = data.normalIndices.at(data.normalsByVertex ? vertexIndex : index);
if (normalIndex >= 0) {
glm::vec3 normal;
int normalIndex = data.normalsByVertex ? vertexIndex : index;
if (data.normalIndices.isEmpty()) {
if (normalIndex < data.normals.size()) {
normal = data.normals.at(normalIndex);
}
} else if (normalIndex < data.normalIndices.size()) {
normalIndex = data.normalIndices.at(normalIndex);
if (normalIndex >= 0 && normalIndex < data.normals.size()) {
normal = data.normals.at(normalIndex);
}
}
@ -720,9 +730,9 @@ void appendIndex(MeshData& data, QVector<int>& indices, int index) {
if (index < data.texCoords.size()) {
vertex.texCoord = data.texCoords.at(index);
}
} else {
} else if (index < data.texCoordIndices.size()) {
int texCoordIndex = data.texCoordIndices.at(index);
if (texCoordIndex >= 0) {
if (texCoordIndex >= 0 && texCoordIndex < data.texCoords.size()) {
vertex.texCoord = data.texCoords.at(texCoordIndex);
}
}
@ -733,7 +743,7 @@ void appendIndex(MeshData& data, QVector<int>& indices, int index) {
indices.append(newIndex);
data.indices.insert(vertex, 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.texCoords.append(vertex.texCoord);
@ -749,44 +759,51 @@ ExtractedMesh extractMesh(const FBXNode& object) {
QVector<int> textures;
foreach (const FBXNode& child, object.children) {
if (child.name == "Vertices") {
data.vertices = createVec3Vector(getDoubleVector(child.properties, 0));
data.vertices = createVec3Vector(getDoubleVector(child));
} else if (child.name == "PolygonVertexIndex") {
data.polygonIndices = getIntVector(child.properties, 0);
data.polygonIndices = getIntVector(child);
} else if (child.name == "LayerElementNormal") {
data.normalsByVertex = false;
bool indexToDirect = false;
foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "Normals") {
data.normals = createVec3Vector(getDoubleVector(subdata.properties, 0));
data.normals = createVec3Vector(getDoubleVector(subdata));
} else if (subdata.name == "NormalsIndex") {
data.normalIndices = getIntVector(subdata.properties, 0);
data.normalIndices = getIntVector(subdata);
} else if (subdata.name == "MappingInformationType" &&
subdata.properties.at(0) == "ByVertice") {
} else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == "ByVertice") {
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) {
foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "UV") {
data.texCoords = createVec2Vector(getDoubleVector(subdata.properties, 0));
data.texCoords = createVec2Vector(getDoubleVector(subdata));
} else if (subdata.name == "UVIndex") {
data.texCoordIndices = getIntVector(subdata.properties, 0);
data.texCoordIndices = getIntVector(subdata);
}
}
} else if (child.name == "LayerElementMaterial") {
foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "Materials") {
materials = getIntVector(subdata.properties, 0);
materials = getIntVector(subdata);
}
}
} else if (child.name == "LayerElementTexture") {
foreach (const FBXNode& subdata, child.children) {
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;
for (int beginIndex = 0; beginIndex < data.polygonIndices.size(); polygonIndex++) {
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,
(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, nextIndex++);
appendIndex(data, part.triangleIndices, nextIndex);
if (data.polygonIndices.at(nextIndex) < 0) {
if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) {
break;
}
}
@ -835,13 +852,13 @@ FBXBlendshape extractBlendshape(const FBXNode& object) {
FBXBlendshape blendshape;
foreach (const FBXNode& data, object.children) {
if (data.name == "Indexes") {
blendshape.indices = getIntVector(data.properties, 0);
blendshape.indices = getIntVector(data);
} else if (data.name == "Vertices") {
blendshape.vertices = createVec3Vector(getDoubleVector(data.properties, 0));
blendshape.vertices = createVec3Vector(getDoubleVector(data));
} else if (data.name == "Normals") {
blendshape.normals = createVec3Vector(getDoubleVector(data.properties, 0));
blendshape.normals = createVec3Vector(getDoubleVector(data));
}
}
return blendshape;
@ -1016,7 +1033,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
foreach (const FBXNode& object, child.children) {
if (object.name == "SceneInfo") {
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) {
if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 &&
subsubobject.properties.at(0) == "Original|ApplicationName") {
@ -1262,13 +1285,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
Cluster cluster;
foreach (const FBXNode& subobject, object.children) {
if (subobject.name == "Indexes") {
cluster.indices = getIntVector(subobject.properties, 0);
cluster.indices = getIntVector(subobject);
} else if (subobject.name == "Weights") {
cluster.weights = getDoubleVector(subobject.properties, 0);
cluster.weights = getDoubleVector(subobject);
} else if (subobject.name == "TransformLink") {
QVector<double> values = getDoubleVector(subobject.properties, 0);
QVector<double> values = getDoubleVector(subobject);
cluster.transformLink = createMat4(values);
}
}
@ -1290,7 +1313,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
AnimationCurve curve;
foreach (const FBXNode& subobject, object.children) {
if (subobject.name == "KeyValueFloat") {
curve.values = getFloatVector(subobject.properties, 0);
curve.values = getFloatVector(subobject);
}
}
animationCurves.insert(getID(object.properties), curve);

View file

@ -47,6 +47,9 @@ public:
/// \return true if point is within current limits
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 maximum;
};
@ -174,6 +177,7 @@ public:
class FBXGeometry {
public:
QString author;
QString applicationName; ///< the name of the application that generated the model
QVector<FBXJoint> joints;

View file

@ -55,9 +55,7 @@ public:
void requestAccessToken(const QString& login, const QString& password);
QString getUsername() const { return _accountInfo.getUsername(); }
const QString& getXMPPPassword() const { return _accountInfo.getXMPPPassword(); }
const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; }
void destroy() { delete _networkAccessManager; }

View file

@ -50,7 +50,8 @@ Assignment::Assignment() :
_type(Assignment::AllTypes),
_pool(),
_location(Assignment::LocalLocation),
_payload()
_payload(),
_isStatic(false)
{
}
@ -61,7 +62,8 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const
_type(type),
_pool(pool),
_location(location),
_payload()
_payload(),
_isStatic(false)
{
if (_command == Assignment::CreateCommand) {
// this is a newly created assignment, generate a random UUID

View file

@ -78,6 +78,9 @@ public:
void setPool(const QString& pool) { _pool = pool; };
const QString& getPool() const { return _pool; }
void setIsStatic(bool isStatic) { _isStatic = isStatic; }
bool isStatic() const { return _isStatic; }
const char* getTypeName() const;
// 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
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
bool _isStatic; /// defines if this assignment needs to be re-queued in the domain-server if it stops being fulfilled
};
#endif // hifi_Assignment_h

View file

@ -78,7 +78,7 @@ gnutls_datum_t* DTLSSession::highFidelityCADatum() {
static bool datumInitialized = false;
static unsigned char HIGHFIDELITY_ROOT_CA_CERT[] =
"-----BEGIN CERTIFICATE-----"
"-----BEGIN CERTIFICATE-----\n"
"MIID6TCCA1KgAwIBAgIJANlfRkRD9A8bMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
"VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j\n"
"aXNjbzEbMBkGA1UEChMSSGlnaCBGaWRlbGl0eSwgSW5jMRMwEQYDVQQLEwpPcGVy\n"
@ -100,7 +100,7 @@ gnutls_datum_t* DTLSSession::highFidelityCADatum() {
"SIb3DQEBBQUAA4GBAEkQl3p+lH5vuoCNgyfa67nL0MsBEt+5RSBOgjwCjjASjzou\n"
"FTv5w0he2OypgMQb8i/BYtS1lJSFqjPJcSM1Salzrm3xDOK5pOXJ7h6SQLPDVEyf\n"
"Hy2/9d/to+99+SOUlvfzfgycgjOc+s/AV7Y+GBd7uzGxUdrN4egCZW1F6/mH\n"
"-----END CERTIFICATE-----";
"-----END CERTIFICATE-----\n";
if (!datumInitialized) {
hifiCADatum.data = HIGHFIDELITY_ROOT_CA_CERT;

View file

@ -22,8 +22,11 @@
#include "HifiSockAddr.h"
const QString DEFAULT_DOMAIN_HOSTNAME = "alpha.highfidelity.io";
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
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 {
Q_OBJECT

View file

@ -106,6 +106,10 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() {
_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)
qDebug() << "Making required DTLS changes to LimitedNodeList DTLS socket.";

View file

@ -378,12 +378,17 @@ void NodeList::sendDomainServerCheckIn() {
}
}
PacketType domainPacketType = _sessionUUID.isNull()
PacketType domainPacketType = !_domainHandler.isConnected()
? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
// construct the DS check in packet
QUuid packetUUID = (domainPacketType == PacketTypeDomainListRequest
? _sessionUUID : _domainHandler.getAssignmentUUID());
QUuid packetUUID = _sessionUUID;
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);
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
@ -404,7 +409,6 @@ void NodeList::sendDomainServerCheckIn() {
dtlsSession->writeDatagram(domainServerPacket);
}
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
static unsigned int numDomainCheckins = 0;

View file

@ -38,7 +38,7 @@ enum PacketType {
PacketTypeDomainListRequest,
PacketTypeRequestAssignment,
PacketTypeCreateAssignment,
PacketTypeDataServerPut, // reusable
PacketTypeDomainOAuthRequest,
PacketTypeDataServerGet, // reusable
PacketTypeDataServerSend, // reusable
PacketTypeDataServerConfirm,
@ -72,7 +72,7 @@ typedef char PacketVersion;
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
<< PacketTypeDomainList << PacketTypeDomainListRequest
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
<< PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeModelQuery;

View file

@ -17,6 +17,10 @@ glm::vec3 Vec3::cross(const glm::vec3& v1, const glm::vec3& 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) {
return v1 * f;
}

View file

@ -26,6 +26,7 @@ class Vec3 : public QObject {
public slots:
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 multiplyQbyV(const glm::quat& q, const glm::vec3& v);
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2);