From d0070796174b64b43348f13f770487e4658953cb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 21 Jul 2014 10:30:11 -0700 Subject: [PATCH 01/56] associate username with agent nodes in domain-server --- domain-server/src/DomainServer.cpp | 33 +++++++++++++++++----- domain-server/src/DomainServer.h | 2 +- domain-server/src/DomainServerNodeData.cpp | 1 + domain-server/src/DomainServerNodeData.h | 4 +++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 366a5016f9..b57f3ac383 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -426,11 +426,14 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } } + + QString connectedUsername; if (!isAssignment && !_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)) { + connectedUsername = _sessionAuthenticationHash.take(packetUUID); + if (connectedUsername.isEmpty()) { // 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; @@ -473,6 +476,9 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock // now that we've pulled the wallet UUID and added the node to our list, delete the pending assignee data delete pendingAssigneeData; } + + // if we have a username from an OAuth connect request, set it on the DomainServerNodeData + nodeData->setUsername(connectedUsername); nodeData->setSendingSockAddr(senderSockAddr); @@ -860,6 +866,7 @@ const char JSON_KEY_LOCAL_SOCKET[] = "local"; const char JSON_KEY_POOL[] = "pool"; const char JSON_KEY_PENDING_CREDITS[] = "pending_credits"; const char JSON_KEY_WAKE_TIMESTAMP[] = "wake_timestamp"; +const char JSON_KEY_USERNAME[] = "username"; QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { QJsonObject nodeJson; @@ -884,6 +891,10 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { // if the node has pool information, add it DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); + + // add the node username, if it exists + nodeJson[JSON_KEY_USERNAME] = nodeData->getUsername(); + SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID()); if (matchingAssignment) { nodeJson[JSON_KEY_POOL] = matchingAssignment->getPool(); @@ -1254,22 +1265,30 @@ void DomainServer::handleProfileRequestFinished() { QJsonArray allowedRolesArray = _argumentVariantMap.value(ALLOWED_ROLES_CONFIG_KEY).toJsonValue().toArray(); - bool shouldAllowUserToConnect = false; + QString connectableUsername; + QString profileUsername = profileJSON.object()["data"].toObject()["user"].toObject()["username"].toString(); 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; + connectableUsername = profileUsername; break; } } - - qDebug() << "Confirmed authentication state for user" << uuidStringWithoutCurlyBraces(matchingSessionUUID) - << "-" << shouldAllowUserToConnect; + + if (connectableUsername.isEmpty()) { + qDebug() << "User" << profileUsername << "with session UUID" + << uuidStringWithoutCurlyBraces(matchingSessionUUID) + << "does not have an allowable role. Refusing connection."; + } else { + qDebug() << "User" << profileUsername << "with session UUID" + << uuidStringWithoutCurlyBraces(matchingSessionUUID) + << "has an allowable role. Can connect."; + } // insert this UUID and a flag that indicates if they are allowed to connect - _sessionAuthenticationHash.insert(matchingSessionUUID, shouldAllowUserToConnect); + _sessionAuthenticationHash.insert(matchingSessionUUID, connectableUsername); } } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index cc44bd95a8..e15e277aaf 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -108,7 +108,7 @@ private: QString _oauthClientSecret; QString _hostname; QMap _networkReplyUUIDMap; - QHash _sessionAuthenticationHash; + QHash _sessionAuthenticationHash; DomainServerSettingsManager _settingsManager; }; diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp index 68903cc106..fd95ea9a67 100644 --- a/domain-server/src/DomainServerNodeData.cpp +++ b/domain-server/src/DomainServerNodeData.cpp @@ -21,6 +21,7 @@ DomainServerNodeData::DomainServerNodeData() : _sessionSecretHash(), _assignmentUUID(), _walletUUID(), + _username(), _paymentIntervalTimer(), _statsJSONObject(), _sendingSockAddr(), diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index a7d7233874..366ee8c730 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -35,6 +35,9 @@ public: void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; } const QUuid& getWalletUUID() const { return _walletUUID; } + void setUsername(const QString& username) { _username = username; } + const QString& getUsername() const { return _username; } + QElapsedTimer& getPaymentIntervalTimer() { return _paymentIntervalTimer; } void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; } @@ -50,6 +53,7 @@ private: QHash _sessionSecretHash; QUuid _assignmentUUID; QUuid _walletUUID; + QString _username; QElapsedTimer _paymentIntervalTimer; QJsonObject _statsJSONObject; HifiSockAddr _sendingSockAddr; From 624bb33b1d2294932961958b5aadc67481f73a36 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 21 Jul 2014 10:34:12 -0700 Subject: [PATCH 02/56] display the node's username in underscore template --- domain-server/resources/web/index.shtml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/domain-server/resources/web/index.shtml b/domain-server/resources/web/index.shtml index f0315a113f..7d9f3c80c5 100644 --- a/domain-server/resources/web/index.shtml +++ b/domain-server/resources/web/index.shtml @@ -10,6 +10,7 @@ Type UUID Pool + Username Public Local Uptime (s) @@ -24,6 +25,7 @@ <%- node.type %> <%- node.uuid %> <%- node.pool %> + <%- node.username %> <%- node.public.ip %><%- node.public.port %> <%- node.local.ip %><%- node.local.port %> <%- ((Date.now() - node.wake_timestamp) / 1000).toLocaleString() %> From a77e49d2abc0a7015cacd38f71bb8bdbfa239a86 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 21 Jul 2014 10:47:07 -0700 Subject: [PATCH 03/56] add back missing colon for node port --- domain-server/resources/web/index.shtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/web/index.shtml b/domain-server/resources/web/index.shtml index 7d9f3c80c5..2f83172d4a 100644 --- a/domain-server/resources/web/index.shtml +++ b/domain-server/resources/web/index.shtml @@ -26,8 +26,8 @@ <%- node.uuid %> <%- node.pool %> <%- node.username %> - <%- node.public.ip %><%- node.public.port %> - <%- node.local.ip %><%- node.local.port %> + <%- node.public.ip %>:<%- node.public.port %> + <%- node.local.ip %>:<%- node.local.port %> <%- ((Date.now() - node.wake_timestamp) / 1000).toLocaleString() %> <%- (typeof node.pending_credits == 'number' ? node.pending_credits.toLocaleString() : 'N/A') %> From f45a50950891f1cf6eb188a8d7e0f0a4d71d92f5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 21 Jul 2014 16:54:27 -0700 Subject: [PATCH 04/56] initial hook-in of OAuth authentication for DS web pages --- domain-server/src/DomainServer.cpp | 177 +++++++++++++++--- domain-server/src/DomainServer.h | 7 + .../embedded-webserver/src/HTTPConnection.cpp | 1 + .../embedded-webserver/src/HTTPConnection.h | 1 + 4 files changed, 162 insertions(+), 24 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b57f3ac383..b0248d3b57 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -42,6 +42,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _hostname(), _networkReplyUUIDMap(), _sessionAuthenticationHash(), + _webAuthenticationStateSet(), + _cookieProfileJSONHash(), _settingsManager() { setOrganizationName("High Fidelity"); @@ -932,7 +934,13 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString URI_NODES = "/nodes"; const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; - + + if (!isAuthenticatedRequest(connection, url)) { + // this is not an authenticated request + // return true from the handler since it was handled with a 401 or re-direct to auth + return true; + } + if (connection->requestOperation() == QNetworkAccessManager::GetOperation) { if (url.path() == "/assignments.json") { // user is asking for json list of assignments @@ -1176,6 +1184,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url return _settingsManager.handleHTTPRequest(connection, url); } +const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID"; + bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url) { const QString URI_OAUTH = "/oauth"; qDebug() << "HTTPS request received at" << url.toString(); @@ -1189,7 +1199,6 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u 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 @@ -1204,15 +1213,45 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u QNetworkRequest tokenRequest(tokenRequestUrl); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - + QNetworkReply* tokenReply = NetworkAccessManager::getInstance().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); + + if (_webAuthenticationStateSet.remove(stateUUID)) { + // this is a web user who wants to auth to access web interface + // we hold the response back to them until we get their profile information + // and can decide if they are let in or not + + QEventLoop loop; + connect(tokenReply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + + // start the loop for the token request + loop.exec(); + + QNetworkReply* profileReply = profileRequestGivenTokenReply(tokenReply); + + // stop the loop once the profileReply is complete + connect(profileReply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + + // restart the loop for the profile request + loop.exec(); + + // call helper method to get cookieHeaders + Headers cookieHeaders = setupCookieHeadersFromProfileReply(profileReply); + + connection->respond(HTTPConnection::StatusCode302, QByteArray(), + HTTPConnection::DefaultContentType, cookieHeaders); + + // we've redirected the user back to our homepage + return true; + + } else { + 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 @@ -1224,6 +1263,65 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u } } +bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl& url) { + + const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie"; + const QString ADMIN_USERS_CONFIG_KEY = "admin-users"; + const QString ADMIN_ROLES_CONFIG_KEY = "admin-roles"; + + if (!_oauthProviderURL.isEmpty() + && (_argumentVariantMap.contains(ADMIN_USERS_CONFIG_KEY) || _argumentVariantMap.contains(ADMIN_ROLES_CONFIG_KEY))) { + QString cookieString = connection->requestHeaders().value(HTTP_COOKIE_HEADER_KEY); + + const QString COOKIE_UUID_REGEX_STRING = HIFI_SESSION_COOKIE_KEY + "=([\\d\\w-]+)($|;)"; + QRegExp cookieUUIDRegex(COOKIE_UUID_REGEX_STRING); + + QUuid cookieUUID; + if (cookieString.indexOf(cookieUUIDRegex) != -1) { + cookieUUID = cookieUUIDRegex.cap(1); + } + + if (!cookieUUID.isNull() && _cookieProfileJSONHash.contains(cookieUUID)) { + // pull the QJSONObject for the user with this cookie UUID + QJsonObject profileObject = _cookieProfileJSONHash.value(cookieUUID); + QString profileUsername = profileObject.value("username").toString(); + + if (_argumentVariantMap.value(ADMIN_USERS_CONFIG_KEY).toJsonValue().toArray().contains(profileUsername)) { + // this is an authenticated user + return true; + } else { + QString unauthenticatedRequest = "You do not have permission to access this domain-server."; + connection->respond(HTTPConnection::StatusCode401, unauthenticatedRequest.toUtf8()); + + // the user does not have allowed username or role, return 401 + return false; + } + } else { + // re-direct this user to OAuth page + + // generate a random state UUID to use + QUuid stateUUID = QUuid::createUuid(); + + // add it to the set so we can handle the callback from the OAuth provider + _webAuthenticationStateSet.insert(stateUUID); + + QUrl oauthRedirectURL = oauthAuthorizationURL(stateUUID); + + Headers redirectHeaders; + redirectHeaders.insert("Location", oauthRedirectURL.toEncoded()); + + connection->respond(HTTPConnection::StatusCode302, + QByteArray(), HTTPConnection::DefaultContentType, redirectHeaders); + + // we don't know about this user yet, so they are not yet authenticated + return false; + } + } else { + // we don't have an OAuth URL + admin roles/usernames, so all users are authenticated + return true; + } +} + const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token"; void DomainServer::handleTokenRequestFinished() { @@ -1231,20 +1329,11 @@ void DomainServer::handleTokenRequestFinished() { 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::getInstance().get(QNetworkRequest(profileURL)); - - qDebug() << "Requesting access token for user with session UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID); + + qDebug() << "Received access token for user with UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID) + << "-" << "requesting profile."; + + QNetworkReply* profileReply = profileRequestGivenTokenReply(networkReply); connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished); @@ -1252,6 +1341,19 @@ void DomainServer::handleTokenRequestFinished() { } } +QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenReply) { + // pull the access token from the returned JSON and store it with the matching session UUID + QJsonDocument returnedJSON = QJsonDocument::fromJson(tokenReply->readAll()); + QString accessToken = returnedJSON.object()[OAUTH_JSON_ACCESS_TOKEN_KEY].toString(); + + // 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)); + + return NetworkAccessManager::getInstance().get(QNetworkRequest(profileURL)); +} + void DomainServer::handleProfileRequestFinished() { QNetworkReply* networkReply = reinterpret_cast(sender()); QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); @@ -1293,6 +1395,33 @@ void DomainServer::handleProfileRequestFinished() { } } +Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileReply) { + Headers cookieHeaders; + + // create a UUID for this cookie + QUuid cookieUUID = QUuid::createUuid(); + + QJsonDocument profileDocument = QJsonDocument::fromJson(profileReply->readAll()); + + // add the profile to our in-memory data structure so we know who the user is when they send us their cookie + _cookieProfileJSONHash.insert(cookieUUID, profileDocument.object()["data"].toObject()["user"].toObject()); + + // setup expiry for cookie to 1 month from today + QDateTime cookieExpiry = QDateTime::currentDateTimeUtc().addMonths(1); + + QString cookieString = HIFI_SESSION_COOKIE_KEY + "=" + uuidStringWithoutCurlyBraces(cookieUUID.toString()); + cookieString += "; expires=" + cookieExpiry.toString("ddd, dd MMM yyyy HH:mm:ss") + " GMT"; + cookieString += "; domain=" + _hostname + "; path=/"; + + cookieHeaders.insert("Set-Cookie", cookieString.toUtf8()); + + // redirect the user back to the homepage so they can present their cookie and be authenticated + QString redirectString = "http://" + _hostname + ":" + QString::number(_httpManager.serverPort()); + cookieHeaders.insert("Location", redirectString.toUtf8()); + + return cookieHeaders; +} + void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) { QUuid oldUUID = assignment->getUUID(); assignment->resetUUID(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index e15e277aaf..98e5b96f25 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -85,8 +85,12 @@ private: QUrl oauthRedirectURL(); QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid()); + bool isAuthenticatedRequest(HTTPConnection* connection, const QUrl& url); + void handleTokenRequestFinished(); + QNetworkReply* profileRequestGivenTokenReply(QNetworkReply* tokenReply); void handleProfileRequestFinished(); + Headers setupCookieHeadersFromProfileReply(QNetworkReply* profileReply); QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); @@ -110,6 +114,9 @@ private: QMap _networkReplyUUIDMap; QHash _sessionAuthenticationHash; + QSet _webAuthenticationStateSet; + QHash _cookieProfileJSONHash; + DomainServerSettingsManager _settingsManager; }; diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index beb107c4cf..7112a90825 100755 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -21,6 +21,7 @@ const char* HTTPConnection::StatusCode200 = "200 OK"; const char* HTTPConnection::StatusCode301 = "301 Moved Permanently"; const char* HTTPConnection::StatusCode302 = "302 Found"; const char* HTTPConnection::StatusCode400 = "400 Bad Request"; +const char* HTTPConnection::StatusCode401 = "401 Unauthorized"; const char* HTTPConnection::StatusCode404 = "404 Not Found"; const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1"; diff --git a/libraries/embedded-webserver/src/HTTPConnection.h b/libraries/embedded-webserver/src/HTTPConnection.h index e2352ed250..c981537c15 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.h +++ b/libraries/embedded-webserver/src/HTTPConnection.h @@ -46,6 +46,7 @@ public: static const char* StatusCode301; static const char* StatusCode302; static const char* StatusCode400; + static const char* StatusCode401; static const char* StatusCode404; static const char* DefaultContentType; From 1903fff45c29f5200d37f5112896a084667fa7ea Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 21 Jul 2014 17:03:09 -0700 Subject: [PATCH 05/56] hide remote socket closed error message in HTTPConnection --- libraries/embedded-webserver/src/HTTPConnection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index 7112a90825..82d3d7eba6 100755 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -43,7 +43,8 @@ HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager) HTTPConnection::~HTTPConnection() { // log the destruction - if (_socket->error() != QAbstractSocket::UnknownSocketError) { + if (_socket->error() != QAbstractSocket::UnknownSocketError + && _socket->error() != QAbstractSocket::RemoteHostClosedError) { qDebug() << _socket->errorString() << "-" << _socket->error(); } } From e75ed2c4fa8982468da9fd0d05897f40c5279013 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 21 Jul 2014 17:23:57 -0700 Subject: [PATCH 06/56] add a class to hold web session data --- .../src/DomainServerWebSessionData.cpp | 26 ++++++++++++++++ .../src/DomainServerWebSessionData.h | 31 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 domain-server/src/DomainServerWebSessionData.cpp create mode 100644 domain-server/src/DomainServerWebSessionData.h diff --git a/domain-server/src/DomainServerWebSessionData.cpp b/domain-server/src/DomainServerWebSessionData.cpp new file mode 100644 index 0000000000..e59f255822 --- /dev/null +++ b/domain-server/src/DomainServerWebSessionData.cpp @@ -0,0 +1,26 @@ +// +// DomainServerWebSessionData.cpp +// domain-server/src +// +// Created by Stephen Birarda on 2014-07-21. +// 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 +#include + +#include "DomainServerWebSessionData.h" + +DomainServerWebSessionData::DomainServerWebSessionData(const QJsonDocument& profileDocument) : + _roles() +{ + _username = profileDocument.object()["user"].toObject()["username"].toString(); + + // pull each of the roles and throw them into our set + foreach(const QJsonValue& rolesValue, profileDocument.object()["user"].toObject()["roles"].toObject()) { + _roles.insert(rolesValue.toString()); + } +} \ No newline at end of file diff --git a/domain-server/src/DomainServerWebSessionData.h b/domain-server/src/DomainServerWebSessionData.h new file mode 100644 index 0000000000..80088c9362 --- /dev/null +++ b/domain-server/src/DomainServerWebSessionData.h @@ -0,0 +1,31 @@ +// +// DomainServerWebSessionData.h +// domain-server/src +// +// Created by Stephen Birarda on 2014-07-21. +// 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_DomainServerWebSessionData_h +#define hifi_DomainServerWebSessionData_h + +#include +#include + +class DomainServerWebSessionData : public QObject { + Q_OBJECT +public: + DomainServerWebSessionData(const QJsonDocument& profileDocument); + + const QString& getUsername() const { return _username; } + const QSet& getRoles() const { return _roles; } + +private: + QString _username; + QSet _roles; +}; + +#endif // hifi_DomainServerWebSessionData_h \ No newline at end of file From f78a1f703374dcde09826d78b6a800d06d48fdd4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 21 Jul 2014 17:32:38 -0700 Subject: [PATCH 07/56] hook domain-server to user DomainServerWebSessionData class --- domain-server/src/DomainServer.cpp | 10 +++---- domain-server/src/DomainServer.h | 3 +- .../src/DomainServerWebSessionData.cpp | 28 ++++++++++++++++++- .../src/DomainServerWebSessionData.h | 5 ++++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b0248d3b57..3c0a088869 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -43,7 +43,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _networkReplyUUIDMap(), _sessionAuthenticationHash(), _webAuthenticationStateSet(), - _cookieProfileJSONHash(), + _cookieSessionHash(), _settingsManager() { setOrganizationName("High Fidelity"); @@ -1281,10 +1281,10 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl cookieUUID = cookieUUIDRegex.cap(1); } - if (!cookieUUID.isNull() && _cookieProfileJSONHash.contains(cookieUUID)) { + if (!cookieUUID.isNull() && _cookieSessionHash.contains(cookieUUID)) { // pull the QJSONObject for the user with this cookie UUID - QJsonObject profileObject = _cookieProfileJSONHash.value(cookieUUID); - QString profileUsername = profileObject.value("username").toString(); + DomainServerWebSessionData sessionData = _cookieSessionHash.value(cookieUUID); + QString profileUsername = sessionData.getUsername(); if (_argumentVariantMap.value(ADMIN_USERS_CONFIG_KEY).toJsonValue().toArray().contains(profileUsername)) { // this is an authenticated user @@ -1404,7 +1404,7 @@ Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileR QJsonDocument profileDocument = QJsonDocument::fromJson(profileReply->readAll()); // add the profile to our in-memory data structure so we know who the user is when they send us their cookie - _cookieProfileJSONHash.insert(cookieUUID, profileDocument.object()["data"].toObject()["user"].toObject()); + _cookieSessionHash.insert(cookieUUID, DomainServerWebSessionData(profileDocument)); // setup expiry for cookie to 1 month from today QDateTime cookieExpiry = QDateTime::currentDateTimeUtc().addMonths(1); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 98e5b96f25..f8daa9a529 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -25,6 +25,7 @@ #include #include "DomainServerSettingsManager.h" +#include "DomainServerWebSessionData.h" #include "WalletTransaction.h" #include "PendingAssignedNodeData.h" @@ -115,7 +116,7 @@ private: QHash _sessionAuthenticationHash; QSet _webAuthenticationStateSet; - QHash _cookieProfileJSONHash; + QHash _cookieSessionHash; DomainServerSettingsManager _settingsManager; }; diff --git a/domain-server/src/DomainServerWebSessionData.cpp b/domain-server/src/DomainServerWebSessionData.cpp index e59f255822..de73ca77dd 100644 --- a/domain-server/src/DomainServerWebSessionData.cpp +++ b/domain-server/src/DomainServerWebSessionData.cpp @@ -14,6 +14,13 @@ #include "DomainServerWebSessionData.h" +DomainServerWebSessionData::DomainServerWebSessionData() : + _username(), + _roles() +{ + +} + DomainServerWebSessionData::DomainServerWebSessionData(const QJsonDocument& profileDocument) : _roles() { @@ -23,4 +30,23 @@ DomainServerWebSessionData::DomainServerWebSessionData(const QJsonDocument& prof foreach(const QJsonValue& rolesValue, profileDocument.object()["user"].toObject()["roles"].toObject()) { _roles.insert(rolesValue.toString()); } -} \ No newline at end of file +} + +DomainServerWebSessionData::DomainServerWebSessionData(const DomainServerWebSessionData& otherSessionData) { + _username = otherSessionData._username; + _roles = otherSessionData._roles; +} + +DomainServerWebSessionData& DomainServerWebSessionData::operator=(const DomainServerWebSessionData& otherSessionData) { + DomainServerWebSessionData temp(otherSessionData); + swap(temp); + return *this; +} + +void DomainServerWebSessionData::swap(DomainServerWebSessionData& otherSessionData) { + using std::swap; + + swap(_username, otherSessionData._username); + swap(_roles, otherSessionData._roles); +} + diff --git a/domain-server/src/DomainServerWebSessionData.h b/domain-server/src/DomainServerWebSessionData.h index 80088c9362..cd2410cf66 100644 --- a/domain-server/src/DomainServerWebSessionData.h +++ b/domain-server/src/DomainServerWebSessionData.h @@ -18,12 +18,17 @@ class DomainServerWebSessionData : public QObject { Q_OBJECT public: + DomainServerWebSessionData(); DomainServerWebSessionData(const QJsonDocument& profileDocument); + DomainServerWebSessionData(const DomainServerWebSessionData& otherSessionData); + DomainServerWebSessionData& operator=(const DomainServerWebSessionData& otherSessionData); const QString& getUsername() const { return _username; } const QSet& getRoles() const { return _roles; } private: + void swap(DomainServerWebSessionData& otherSessionData); + QString _username; QSet _roles; }; From 8082e2f88b08c263cb4603099562b4249df7c7ae Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 21 Jul 2014 17:41:22 -0700 Subject: [PATCH 08/56] allow a user to be let into domain-server based on role --- domain-server/src/DomainServer.cpp | 27 ++++++++++++++----- .../src/DomainServerWebSessionData.cpp | 9 ++++--- .../src/DomainServerWebSessionData.h | 2 +- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 3c0a088869..52da966d46 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1289,13 +1289,25 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl if (_argumentVariantMap.value(ADMIN_USERS_CONFIG_KEY).toJsonValue().toArray().contains(profileUsername)) { // this is an authenticated user return true; - } else { - QString unauthenticatedRequest = "You do not have permission to access this domain-server."; - connection->respond(HTTPConnection::StatusCode401, unauthenticatedRequest.toUtf8()); - - // the user does not have allowed username or role, return 401 - return false; } + + // loop the roles of this user and see if they are in the admin-roles array + QJsonArray adminRolesArray = _argumentVariantMap.value(ADMIN_ROLES_CONFIG_KEY).toJsonValue().toArray(); + + if (!adminRolesArray.isEmpty()) { + foreach(const QString& userRole, sessionData.getRoles()) { + if (adminRolesArray.contains(userRole)) { + // this user has a role that allows them to administer the domain-server + return true; + } + } + } + + QString unauthenticatedRequest = "You do not have permission to access this domain-server."; + connection->respond(HTTPConnection::StatusCode401, unauthenticatedRequest.toUtf8()); + + // the user does not have allowed username or role, return 401 + return false; } else { // re-direct this user to OAuth page @@ -1402,9 +1414,10 @@ Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileR QUuid cookieUUID = QUuid::createUuid(); QJsonDocument profileDocument = QJsonDocument::fromJson(profileReply->readAll()); + QJsonObject userObject = profileDocument.object()["data"].toObject()["user"].toObject(); // add the profile to our in-memory data structure so we know who the user is when they send us their cookie - _cookieSessionHash.insert(cookieUUID, DomainServerWebSessionData(profileDocument)); + _cookieSessionHash.insert(cookieUUID, DomainServerWebSessionData(userObject)); // setup expiry for cookie to 1 month from today QDateTime cookieExpiry = QDateTime::currentDateTimeUtc().addMonths(1); diff --git a/domain-server/src/DomainServerWebSessionData.cpp b/domain-server/src/DomainServerWebSessionData.cpp index de73ca77dd..b0c56cc59e 100644 --- a/domain-server/src/DomainServerWebSessionData.cpp +++ b/domain-server/src/DomainServerWebSessionData.cpp @@ -9,7 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include +#include #include #include "DomainServerWebSessionData.h" @@ -21,13 +22,13 @@ DomainServerWebSessionData::DomainServerWebSessionData() : } -DomainServerWebSessionData::DomainServerWebSessionData(const QJsonDocument& profileDocument) : +DomainServerWebSessionData::DomainServerWebSessionData(const QJsonObject& userObject) : _roles() { - _username = profileDocument.object()["user"].toObject()["username"].toString(); + _username = userObject["username"].toString(); // pull each of the roles and throw them into our set - foreach(const QJsonValue& rolesValue, profileDocument.object()["user"].toObject()["roles"].toObject()) { + foreach(const QJsonValue& rolesValue, userObject["roles"].toArray()) { _roles.insert(rolesValue.toString()); } } diff --git a/domain-server/src/DomainServerWebSessionData.h b/domain-server/src/DomainServerWebSessionData.h index cd2410cf66..15e4171b57 100644 --- a/domain-server/src/DomainServerWebSessionData.h +++ b/domain-server/src/DomainServerWebSessionData.h @@ -19,7 +19,7 @@ class DomainServerWebSessionData : public QObject { Q_OBJECT public: DomainServerWebSessionData(); - DomainServerWebSessionData(const QJsonDocument& profileDocument); + DomainServerWebSessionData(const QJsonObject& userObject); DomainServerWebSessionData(const DomainServerWebSessionData& otherSessionData); DomainServerWebSessionData& operator=(const DomainServerWebSessionData& otherSessionData); From 1a13fbb437b5d79fa66793d7170d8defc399b555 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 23 Jul 2014 10:54:55 -0700 Subject: [PATCH 09/56] add a setting section for voxel costs --- .../resources/web/settings/describe.json | 20 +++++++++++++++++++ .../resources/web/settings/index.shtml | 18 +++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index 227b6bf0cd..97280bfd32 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -16,5 +16,25 @@ "default": false } } + }, + "voxels": { + "label": "Voxels", + "assignment-types": [3], + "settings": { + "per-voxel-cost": { + "label": "Per Voxel Cost", + "help": "Credit cost to change each voxel", + "placeholder": "0.00001", + "default": "0.00001", + "input_addon": "₵" + }, + "volume-cost": { + "label": "Volume Cost", + "help": "Credit cost to change each cubed meter of voxel space", + "placeholder": "0.0005", + "default": "0.0005", + "input_addon": "₵" + } + } } } \ No newline at end of file diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 3bb669b32e..73e3cdbea2 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -16,16 +16,22 @@ <% var setting_id = group_key + "." + setting_key %>
- <% if(setting.type) %> - <% if (setting.type === "checkbox") { %> - <% var checked_box = (values[group_key] || {})[setting_key] || setting.default %> - > - <% } else { %> + <% if (setting.type === "checkbox") { %> + <% var checked_box = (values[group_key] || {})[setting_key] || setting.default %> + > + <% } else { %> + <% if (setting.input_addon) { %> +
+
<%- setting.input_addon %>
+ <% } %> + + <% if (setting.input_addon) { %> +
+ <% } %> <% } %> -

<%- setting.help %>

From ebf5379275a5376d23839a722b96bded3e267836 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 23 Jul 2014 11:13:12 -0700 Subject: [PATCH 10/56] default voxel costs to free, add a wallet ID for voxel change payments --- domain-server/resources/web/settings/describe.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index 97280bfd32..0ab647734b 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -21,18 +21,24 @@ "label": "Voxels", "assignment-types": [3], "settings": { + "voxel-wallet": { + "label": "Destination Wallet ID", + "help": "Wallet to be paid for voxel changes", + "placeholder": "00000000-0000-0000-0000-000000000000", + "default": "" + }, "per-voxel-cost": { "label": "Per Voxel Cost", "help": "Credit cost to change each voxel", - "placeholder": "0.00001", - "default": "0.00001", + "placeholder": "0.0", + "default": "0.0", "input_addon": "₵" }, "volume-cost": { "label": "Volume Cost", "help": "Credit cost to change each cubed meter of voxel space", - "placeholder": "0.0005", - "default": "0.0005", + "placeholder": "0.0", + "default": "0.0", "input_addon": "₵" } } From 74d17a094f0a190c3aa080143e02c4f5ad6b0434 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 23 Jul 2014 11:47:56 -0700 Subject: [PATCH 11/56] tweaks to string default handling in DomainServerSettingsManager --- .../src/DomainServerSettingsManager.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index f9996aa0e7..0520426ea8 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -145,7 +145,12 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ // we don't continue if this key is not present in our descriptionObject if (descriptionObject.contains(key)) { if (rootValue.isString()) { - settingsVariant[key] = rootValue.toString(); + if (rootValue.toString().isEmpty()) { + // this is an empty value, clear it in settings variant so the default is sent + settingsVariant.remove(key); + } else { + settingsVariant[key] = rootValue.toString(); + } } else if (rootValue.isBool()) { settingsVariant[key] = rootValue.toBool(); } else if (rootValue.isObject()) { @@ -158,9 +163,16 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ settingsVariant[key] = QVariantMap(); } + QVariantMap& thisMap = *reinterpret_cast(settingsVariant[key].data()); + recurseJSONObjectAndOverwriteSettings(rootValue.toObject(), - *reinterpret_cast(settingsVariant[key].data()), + thisMap, nextDescriptionObject[DESCRIPTION_SETTINGS_KEY].toObject()); + + if (thisMap.isEmpty()) { + // we've cleared all of the settings below this value, so remove this one too + settingsVariant.remove(key); + } } } } From 2e5dc2320d65d16df83afbca64717f7d34d5e767 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 24 Jul 2014 11:09:09 -0700 Subject: [PATCH 12/56] don't flip order on node socket, HifiSockAddr is handling --- domain-server/src/DomainServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 52da966d46..4261b5033b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -856,7 +856,7 @@ QJsonObject DomainServer::jsonForSocket(const HifiSockAddr& socket) { QJsonObject socketJSON; socketJSON["ip"] = socket.getAddress().toString(); - socketJSON["port"] = ntohs(socket.getPort()); + socketJSON["port"] = socket.getPort(); return socketJSON; } From 9b629a73266a94d0d457089a82411415f4e84ab6 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 24 Jul 2014 16:55:23 -0700 Subject: [PATCH 13/56] Added slerp and squad to the Quat scripting interface --- libraries/script-engine/src/Quat.cpp | 10 ++++++++++ libraries/script-engine/src/Quat.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 4acc60e7b4..8308536f97 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -66,6 +66,16 @@ glm::quat Quat::mix(const glm::quat& q1, const glm::quat& q2, float alpha) { return safeMix(q1, q2, alpha); } +/// Spherical Linear Interpolation +glm::quat Quat::slerp(const glm::quat& q1, const glm::quat& q2, float alpha) { + return glm::slerp(q1, q2, alpha); +} + +// Spherical Quadratic Interpolation +glm::quat Quat::squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h) { + return glm::squad(q1, q2, s1, s2, h); +} + void Quat::print(const QString& lable, const glm::quat& q) { qDebug() << qPrintable(lable) << q.x << "," << q.y << "," << q.z << "," << q.w; } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index c97ccf9a1e..190c823118 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -36,6 +36,8 @@ public slots: glm::vec3 safeEulerAngles(const glm::quat& orientation); // degrees glm::quat angleAxis(float angle, const glm::vec3& v); // degrees glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha); + glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha); + glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h); void print(const QString& lable, const glm::quat& q); }; From 005a3c7c1239b156f7df2cd8e6d8efd107717f64 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 24 Jul 2014 17:17:57 -0700 Subject: [PATCH 14/56] persist and recall domain-server web sessions from ini settings --- domain-server/src/DomainServer.cpp | 28 ++++++++++++++++++- domain-server/src/DomainServer.h | 2 ++ .../src/DomainServerWebSessionData.cpp | 10 +++++++ .../src/DomainServerWebSessionData.h | 5 ++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 4261b5033b..e462e05aa6 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -50,6 +50,9 @@ DomainServer::DomainServer(int argc, char* argv[]) : setOrganizationDomain("highfidelity.io"); setApplicationName("domain-server"); QSettings::setDefaultFormat(QSettings::IniFormat); + + qRegisterMetaType("DomainServerWebSessionData"); + qRegisterMetaTypeStreamOperators("DomainServerWebSessionData"); _argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); @@ -59,6 +62,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : qDebug() << "Setting up LimitedNodeList and assignments."; setupNodeListAndAssignments(); + + loadExistingSessionsFromSettings(); } } @@ -1407,6 +1412,9 @@ void DomainServer::handleProfileRequestFinished() { } } + +const QString DS_SETTINGS_SESSIONS_GROUP = "web-sessions"; + Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileReply) { Headers cookieHeaders; @@ -1417,7 +1425,14 @@ Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileR QJsonObject userObject = profileDocument.object()["data"].toObject()["user"].toObject(); // add the profile to our in-memory data structure so we know who the user is when they send us their cookie - _cookieSessionHash.insert(cookieUUID, DomainServerWebSessionData(userObject)); + DomainServerWebSessionData sessionData(userObject); + _cookieSessionHash.insert(cookieUUID, sessionData); + + // persist the cookie to settings file so we can get it back on DS relaunch + QSettings localSettings; + localSettings.beginGroup(DS_SETTINGS_SESSIONS_GROUP); + QVariant sessionVariant = QVariant::fromValue(sessionData); + localSettings.setValue(cookieUUID.toString(), QVariant::fromValue(sessionData)); // setup expiry for cookie to 1 month from today QDateTime cookieExpiry = QDateTime::currentDateTimeUtc().addMonths(1); @@ -1435,6 +1450,17 @@ Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileR return cookieHeaders; } +void DomainServer::loadExistingSessionsFromSettings() { + // read data for existing web sessions into memory so existing sessions can be leveraged + QSettings domainServerSettings; + domainServerSettings.beginGroup(DS_SETTINGS_SESSIONS_GROUP); + + foreach(const QString& uuidKey, domainServerSettings.childKeys()) { + _cookieSessionHash.insert(QUuid(uuidKey), domainServerSettings.value(uuidKey).value()); + qDebug() << "Pulled web session from settings - cookie UUID is" << uuidKey; + } +} + void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) { QUuid oldUUID = assignment->getUUID(); assignment->resetUUID(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index f8daa9a529..666994c818 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -93,6 +93,8 @@ private: void handleProfileRequestFinished(); Headers setupCookieHeadersFromProfileReply(QNetworkReply* profileReply); + void loadExistingSessionsFromSettings(); + QJsonObject jsonForSocket(const HifiSockAddr& socket); QJsonObject jsonObjectForNode(const SharedNodePointer& node); diff --git a/domain-server/src/DomainServerWebSessionData.cpp b/domain-server/src/DomainServerWebSessionData.cpp index b0c56cc59e..ee32a17570 100644 --- a/domain-server/src/DomainServerWebSessionData.cpp +++ b/domain-server/src/DomainServerWebSessionData.cpp @@ -51,3 +51,13 @@ void DomainServerWebSessionData::swap(DomainServerWebSessionData& otherSessionDa swap(_roles, otherSessionData._roles); } +QDataStream& operator<<(QDataStream &out, const DomainServerWebSessionData& session) { + out << session._username << session._roles; + return out; +} + +QDataStream& operator>>(QDataStream &in, DomainServerWebSessionData& session) { + in >> session._username >> session._roles; + return in; +} + diff --git a/domain-server/src/DomainServerWebSessionData.h b/domain-server/src/DomainServerWebSessionData.h index 15e4171b57..450bc13a9b 100644 --- a/domain-server/src/DomainServerWebSessionData.h +++ b/domain-server/src/DomainServerWebSessionData.h @@ -26,6 +26,9 @@ public: const QString& getUsername() const { return _username; } const QSet& getRoles() const { return _roles; } + friend QDataStream& operator<<(QDataStream &out, const DomainServerWebSessionData& session); + friend QDataStream& operator>>(QDataStream &in, DomainServerWebSessionData& session); + private: void swap(DomainServerWebSessionData& otherSessionData); @@ -33,4 +36,6 @@ private: QSet _roles; }; +Q_DECLARE_METATYPE(DomainServerWebSessionData) + #endif // hifi_DomainServerWebSessionData_h \ No newline at end of file From 6334116070369afbffcd4ef4f88b17efe7bc7fdd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 28 Jul 2014 12:07:19 -0700 Subject: [PATCH 15/56] request domain settings after successful connection to domain --- libraries/networking/src/DomainHandler.cpp | 60 +++++++++++++++++++++- libraries/networking/src/DomainHandler.h | 8 +++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index f603d21240..286909279b 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "NodeList.h" #include "PacketHeaders.h" #include "UserActivityLogger.h" @@ -21,7 +23,9 @@ DomainHandler::DomainHandler(QObject* parent) : _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), _assignmentUUID(), _isConnected(false), - _handshakeTimer(NULL) + _handshakeTimer(NULL), + _settingsObject(), + _failedSettingsRequests(0) { } @@ -37,8 +41,14 @@ void DomainHandler::clearConnectionInfo() { } } +void DomainHandler::clearSettings() { + _settingsObject = QJsonObject(); + _failedSettingsRequests = 0; +} + void DomainHandler::reset() { clearConnectionInfo(); + clearSettings(); _hostname = QString(); _sockAddr.setAddress(QHostAddress::Null); } @@ -109,10 +119,58 @@ void DomainHandler::setIsConnected(bool isConnected) { if (_isConnected) { emit connectedToDomain(_hostname); + + // we've connected to new domain - time to ask it for global settings + requestDomainSettings(); } } } +void DomainHandler::requestDomainSettings() const { + + // setup the URL required to grab settings JSON + QUrl settingsJSONURL; + settingsJSONURL.setScheme("http"); + settingsJSONURL.setHost(_hostname); + settingsJSONURL.setPort(DOMAIN_SERVER_HTTP_PORT); + settingsJSONURL.setPath("/settings.json"); + settingsJSONURL.setQuery(QString("type=%1").arg(NodeList::getInstance()->getOwnerType())); + + qDebug() << settingsJSONURL; + + QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(settingsJSONURL)); + connect(reply, &QNetworkReply::finished, this, &DomainHandler::settingsRequestFinished); +} + +const int MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS = 5; + +void DomainHandler::settingsRequestFinished() { + QNetworkReply* settingsReply = reinterpret_cast(sender()); + + if (settingsReply->error() == QNetworkReply::NoError) { + // parse the JSON to a QJsonObject and save it + _settingsObject = QJsonDocument::fromJson(settingsReply->readAll()).object(); + + qDebug() << settingsReply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + qDebug() << "Received domain settings."; + + // reset failed settings requests to 0, we got them + _failedSettingsRequests = 0; + } else { + // error grabbing the settings - in some cases this means we are stuck + // so we should retry until we get it + qDebug() << "Error getting domain settings -" << settingsReply->errorString() << "- retrying"; + + if (++_failedSettingsRequests >= MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS) { + qDebug() << "Failed to retreive domain-server settings" << MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS << "times. Re-setting connection to domain."; + clearSettings(); + clearConnectionInfo(); + } + + requestDomainSettings(); + } +} + void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) { // figure out the port that the DS wants us to use for us to talk to them with DTLS int numBytesPacketHeader = numBytesForPacketHeader(dtlsRequirementPacket); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 782332f06a..6761b8eb84 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -12,6 +12,7 @@ #ifndef hifi_DomainHandler_h #define hifi_DomainHandler_h +#include #include #include #include @@ -33,6 +34,7 @@ public: DomainHandler(QObject* parent = 0); void clearConnectionInfo(); + void clearSettings(); const QUuid& getUUID() const { return _uuid; } void setUUID(const QUuid& uuid) { _uuid = uuid; } @@ -54,10 +56,14 @@ public: bool isConnected() const { return _isConnected; } void setIsConnected(bool isConnected); + bool hasSettings() const { return !_settingsObject.isEmpty(); } + void requestDomainSettings() const; + void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket); private slots: void completedHostnameLookup(const QHostInfo& hostInfo); + void settingsRequestFinished(); signals: void hostnameChanged(const QString& hostname); void connectedToDomain(const QString& hostname); @@ -71,6 +77,8 @@ private: QUuid _assignmentUUID; bool _isConnected; QTimer* _handshakeTimer; + QJsonObject _settingsObject; + int _failedSettingsRequests; }; #endif // hifi_DomainHandler_h From dbf41c8a422b90aed2bfe1ec1194f3b452330943 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 28 Jul 2014 14:56:26 -0700 Subject: [PATCH 16/56] allow domain-server to serve settings publicly, fix retry in DomainHandler --- domain-server/src/DomainServer.cpp | 8 +++- .../src/DomainServerSettingsManager.cpp | 42 +++++++++++-------- .../src/DomainServerSettingsManager.h | 5 ++- libraries/networking/src/DomainHandler.cpp | 19 +++++---- 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index e462e05aa6..2ea66c2ee3 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -940,6 +940,12 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; + // allow sub-handlers to handle requests that do not require authentication + if (_settingsManager.handlePublicHTTPRequest(connection, url)) { + return true; + } + + // all requests below require a cookie to prove authentication so check that first if (!isAuthenticatedRequest(connection, url)) { // this is not an authenticated request // return true from the handler since it was handled with a 401 or re-direct to auth @@ -1186,7 +1192,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url } // didn't process the request, let our DomainServerSettingsManager or HTTPManager handle - return _settingsManager.handleHTTPRequest(connection, url); + return _settingsManager.handleAuthenticatedHTTPRequest(connection, url); } const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID"; diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 0520426ea8..4ec88996a6 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -47,24 +47,8 @@ DomainServerSettingsManager::DomainServerSettingsManager() : const QString DESCRIPTION_SETTINGS_KEY = "settings"; const QString SETTING_DEFAULT_KEY = "default"; -bool DomainServerSettingsManager::handleHTTPRequest(HTTPConnection* connection, const QUrl &url) { - if (connection->requestOperation() == QNetworkAccessManager::PostOperation && url.path() == "/settings.json") { - // this is a POST operation to change one or more settings - QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent()); - QJsonObject postedObject = postedDocument.object(); - - // we recurse one level deep below each group for the appropriate setting - recurseJSONObjectAndOverwriteSettings(postedObject, _settingsMap, _descriptionObject); - - // store whatever the current _settingsMap is to file - persistToFile(); - - // return success to the caller - QString jsonSuccess = "{\"status\": \"success\"}"; - connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json"); - - return true; - } else if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == "/settings.json") { +bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) { + if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == "/settings.json") { // this is a GET operation for our settings // check if there is a query parameter for settings affecting a particular type of assignment @@ -135,6 +119,28 @@ bool DomainServerSettingsManager::handleHTTPRequest(HTTPConnection* connection, return false; } +bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection *connection, const QUrl &url) { + if (connection->requestOperation() == QNetworkAccessManager::PostOperation && url.path() == "/settings.json") { + // this is a POST operation to change one or more settings + QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent()); + QJsonObject postedObject = postedDocument.object(); + + // we recurse one level deep below each group for the appropriate setting + recurseJSONObjectAndOverwriteSettings(postedObject, _settingsMap, _descriptionObject); + + // store whatever the current _settingsMap is to file + persistToFile(); + + // return success to the caller + QString jsonSuccess = "{\"status\": \"success\"}"; + connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json"); + + return true; + } + + return false; +} + void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant, QJsonObject descriptionObject) { diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 8b80cad280..26bfe57ab4 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -16,11 +16,12 @@ #include -class DomainServerSettingsManager : public QObject, HTTPRequestHandler { +class DomainServerSettingsManager : public QObject { Q_OBJECT public: DomainServerSettingsManager(); - bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url); + bool handlePublicHTTPRequest(HTTPConnection* connection, const QUrl& url); + bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url); QByteArray getJSONSettingsMap() const; private: diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 286909279b..ce6a264b34 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -11,6 +11,7 @@ #include +#include "Assignment.h" #include "NodeList.h" #include "PacketHeaders.h" #include "UserActivityLogger.h" @@ -133,10 +134,9 @@ void DomainHandler::requestDomainSettings() const { settingsJSONURL.setScheme("http"); settingsJSONURL.setHost(_hostname); settingsJSONURL.setPort(DOMAIN_SERVER_HTTP_PORT); - settingsJSONURL.setPath("/settings.json"); - settingsJSONURL.setQuery(QString("type=%1").arg(NodeList::getInstance()->getOwnerType())); - - qDebug() << settingsJSONURL; + settingsJSONURL.setPath("/settingz.json/"); + Assignment::Type assignmentType = Assignment::typeForNodeType(NodeList::getInstance()->getOwnerType()); + settingsJSONURL.setQuery(QString("type=%1").arg(assignmentType)); QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(settingsJSONURL)); connect(reply, &QNetworkReply::finished, this, &DomainHandler::settingsRequestFinished); @@ -147,11 +147,12 @@ const int MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS = 5; void DomainHandler::settingsRequestFinished() { QNetworkReply* settingsReply = reinterpret_cast(sender()); - if (settingsReply->error() == QNetworkReply::NoError) { + int replyCode = settingsReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (settingsReply->error() == QNetworkReply::NoError && replyCode != 301 && replyCode != 302) { // parse the JSON to a QJsonObject and save it _settingsObject = QJsonDocument::fromJson(settingsReply->readAll()).object(); - qDebug() << settingsReply->attribute(QNetworkRequest::HttpStatusCodeAttribute); qDebug() << "Received domain settings."; // reset failed settings requests to 0, we got them @@ -165,9 +166,9 @@ void DomainHandler::settingsRequestFinished() { qDebug() << "Failed to retreive domain-server settings" << MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS << "times. Re-setting connection to domain."; clearSettings(); clearConnectionInfo(); - } - - requestDomainSettings(); + } else { + requestDomainSettings(); + } } } From 84b86c17b7d75a76d6afc1d4e6a03b7e537058b9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 28 Jul 2014 15:16:59 -0700 Subject: [PATCH 17/56] use common settings retreival code to block before run in AudioMixer --- assignment-client/src/audio/AudioMixer.cpp | 43 +++++++--------------- libraries/networking/src/DomainHandler.cpp | 29 +++++++++------ libraries/networking/src/DomainHandler.h | 3 ++ 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index ad4787b407..91ecee76e2 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -408,41 +408,24 @@ void AudioMixer::run() { nodeList->linkedDataCreateCallback = attachNewBufferToNode; - // setup a NetworkAccessManager to ask the domain-server for our settings - NetworkAccessManager& networkManager = NetworkAccessManager::getInstance(); + // wait until we have the domain-server settings, otherwise we bail + DomainHandler& domainHandler = nodeList->getDomainHandler(); - QUrl settingsJSONURL; - settingsJSONURL.setScheme("http"); - settingsJSONURL.setHost(nodeList->getDomainHandler().getHostname()); - settingsJSONURL.setPort(DOMAIN_SERVER_HTTP_PORT); - settingsJSONURL.setPath("/settings.json"); - settingsJSONURL.setQuery(QString("type=%1").arg(_type)); + qDebug() << "Waiting for domain settings from domain-server."; - QNetworkReply *reply = NULL; + // block until we get the settingsRequestComplete signal + QEventLoop loop; + loop.connect(&domainHandler, &DomainHandler::settingsRequestComplete, &loop, &QEventLoop::quit); + domainHandler.requestDomainSettings(); + loop.exec(); - int failedAttempts = 0; - const int MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS = 5; - - qDebug() << "Requesting settings for assignment from domain-server at" << settingsJSONURL.toString(); - - while (!reply || reply->error() != QNetworkReply::NoError) { - reply = networkManager.get(QNetworkRequest(settingsJSONURL)); - - QEventLoop loop; - QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - - loop.exec(); - - ++failedAttempts; - - if (failedAttempts == MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS) { - qDebug() << "Failed to get settings from domain-server. Bailing on assignment."; - setFinished(true); - return; - } + if (domainHandler.getSettingsObject().isEmpty()) { + qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; + setFinished(true); + return; } - QJsonObject settingsObject = QJsonDocument::fromJson(reply->readAll()).object(); + const QJsonObject& settingsObject = domainHandler.getSettingsObject(); // check the settings object to see if we have anything we can parse out const QString AUDIO_GROUP_KEY = "audio"; diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index ce6a264b34..0f37160512 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -128,18 +128,21 @@ void DomainHandler::setIsConnected(bool isConnected) { } void DomainHandler::requestDomainSettings() const { - - // setup the URL required to grab settings JSON - QUrl settingsJSONURL; - settingsJSONURL.setScheme("http"); - settingsJSONURL.setHost(_hostname); - settingsJSONURL.setPort(DOMAIN_SERVER_HTTP_PORT); - settingsJSONURL.setPath("/settingz.json/"); - Assignment::Type assignmentType = Assignment::typeForNodeType(NodeList::getInstance()->getOwnerType()); - settingsJSONURL.setQuery(QString("type=%1").arg(assignmentType)); - - QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(settingsJSONURL)); - connect(reply, &QNetworkReply::finished, this, &DomainHandler::settingsRequestFinished); + if (_settingsObject.isEmpty()) { + // setup the URL required to grab settings JSON + QUrl settingsJSONURL; + settingsJSONURL.setScheme("http"); + settingsJSONURL.setHost(_hostname); + settingsJSONURL.setPort(DOMAIN_SERVER_HTTP_PORT); + settingsJSONURL.setPath("/settings.json"); + Assignment::Type assignmentType = Assignment::typeForNodeType(NodeList::getInstance()->getOwnerType()); + settingsJSONURL.setQuery(QString("type=%1").arg(assignmentType)); + + qDebug() << "Requesting domain-server settings at" << settingsJSONURL.toString(); + + QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(settingsJSONURL)); + connect(reply, &QNetworkReply::finished, this, &DomainHandler::settingsRequestFinished); + } } const int MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS = 5; @@ -154,6 +157,7 @@ void DomainHandler::settingsRequestFinished() { _settingsObject = QJsonDocument::fromJson(settingsReply->readAll()).object(); qDebug() << "Received domain settings."; + emit settingsRequestComplete(true); // reset failed settings requests to 0, we got them _failedSettingsRequests = 0; @@ -166,6 +170,7 @@ void DomainHandler::settingsRequestFinished() { qDebug() << "Failed to retreive domain-server settings" << MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS << "times. Re-setting connection to domain."; clearSettings(); clearConnectionInfo(); + emit settingsRequestComplete(false); } else { requestDomainSettings(); } diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 6761b8eb84..67edd64172 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -58,6 +58,7 @@ public: bool hasSettings() const { return !_settingsObject.isEmpty(); } void requestDomainSettings() const; + const QJsonObject& getSettingsObject() const { return _settingsObject; } void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket); @@ -68,6 +69,8 @@ signals: void hostnameChanged(const QString& hostname); void connectedToDomain(const QString& hostname); + void settingsRequestComplete(bool wasSuccessful); + private: void reset(); From 5c47e9013c696b9baf13fb9f5289ab8c75f4bf69 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 28 Jul 2014 18:36:25 -0700 Subject: [PATCH 18/56] Working procedural walk animation with two keyframes --- examples/dancer.js | 561 +++++++++++++++++++++++++++ libraries/script-engine/src/Quat.cpp | 4 + libraries/script-engine/src/Quat.h | 1 + 3 files changed, 566 insertions(+) create mode 100644 examples/dancer.js diff --git a/examples/dancer.js b/examples/dancer.js new file mode 100644 index 0000000000..e7a75f7596 --- /dev/null +++ b/examples/dancer.js @@ -0,0 +1,561 @@ +// +// dancer.js +// hifi +// +// Created by Stephen Birarda on 2/20/14. +// Modified by Philip on 3/3/14 +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates an NPC avatar. +// +// + +function getRandomFloat(min, max) { +return Math.random() * (max - min) + min; +} + +function getRandomInt (min, max) { +return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function printVector(string, vector) { +print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); +} + +var CHANCE_OF_MOVING = 0.01; +var CHANCE_OF_SOUND = 0; +var CHANCE_OF_HEAD_TURNING = 0.05; +var CHANCE_OF_BIG_MOVE = 0.1; +var CHANCE_OF_WAVING = 0.009; + +var isMoving = true; +var isTurningHead = false; +var isPlay +ingAudio = false; +var isWaving = false; +var waveFrequency = 0.0; +var waveAmplitude = 0.0; + +var X_MIN = 5.50; +var X_MAX = 5.60; +var Z_MIN = 5.00; +var Z_MAX = 5.10; +var Y_PELVIS = 1.0; +var MAX_PELVIS_DELTA = 2.5; + +var AVATAR_PELVIS_HEIGHT = 0.75; + +var MOVE_RANGE_SMALL = 1.0; +var TURN_RANGE = 70.0; +var STOP_TOLERANCE = 0.05; +var MOVE_RATE = 0.05; +var TURN_RATE = 0.15; +var PITCH_RATE = 0.20; +var PITCH_RANGE = 30.0; + +var firstPosition = { x: getRandomFloat(X_MIN, X_MAX), y: Y_PELVIS, z: getRandomFloat(Z_MIN, Z_MAX) }; +var targetPosition = { x: 0, y: 0, z: 0 }; +var targetDirection = { x: 0, y: 0, z: 0, w: 0 }; +var currentDirection = { x: 0, y: 0, z: 0, w: 0 }; +var targetHeadPitch = 0.0; + +var cumulativeTime = 0.0; + +var basePelvisHeight = 0.0; +var pelvisOscillatorPosition = 0.0; +var pelvisOscillatorVelocity = 0.0; + +function clamp(val, min, max){ + return Math.max(min, Math.min(max, val)) +} + +// pick an integer between 1 and 100 that is not 28 for the face model for this bot +botNumber = 28; + +while (botNumber == 28) { + botNumber = getRandomInt(1, 100); +} + +if (botNumber <= 20) { + newFaceFilePrefix = "ron"; + newBodyFilePrefix = "defaultAvatar_body" +} else { + if (botNumber <= 40) { + newFaceFilePrefix = "superhero"; + } else if (botNumber <= 60) { + newFaceFilePrefix = "amber"; + } else if (botNumber <= 80) { + newFaceFilePrefix = "ron"; + } else { + newFaceFilePrefix = "angie"; + } + + newBodyFilePrefix = "bot" + botNumber; +} + + newFaceFilePrefix = "ron"; + newBodyFilePrefix = "bot" + 63; + +// set the face model fst using the bot number +// there is no need to change the body model - we're using the default +Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix + ".fst"; +Avatar.skeletonModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + "_a.fst"; +Avatar.billboardURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/billboards/bot" + botNumber + ".png"; + +Agent.isAvatar = true; +Agent.isListeningToAudioStream = true; + +// change the avatar's position to the random one +Avatar.position = firstPosition; +basePelvisHeight = firstPosition.y; +printVector("New dancer, position = ", Avatar.position); + +function loadSounds() { + var sound_filenames = ["AB1.raw", "Anchorman2.raw", "B1.raw", "B1.raw", "Bale1.raw", "Bandcamp.raw", + "Big1.raw", "Big2.raw", "Brian1.raw", "Buster1.raw", "CES1.raw", "CES2.raw", "CES3.raw", "CES4.raw", + "Carrie1.raw", "Carrie3.raw", "Charlotte1.raw", "EN1.raw", "EN2.raw", "EN3.raw", "Eugene1.raw", "Francesco1.raw", + "Italian1.raw", "Japanese1.raw", "Leigh1.raw", "Lucille1.raw", "Lucille2.raw", "MeanGirls.raw", "Murray2.raw", + "Nigel1.raw", "PennyLane.raw", "Pitt1.raw", "Ricardo.raw", "SN.raw", "Sake1.raw", "Samantha1.raw", "Samantha2.raw", + "Spicoli1.raw", "Supernatural.raw", "Swearengen1.raw", "TheDude.raw", "Tony.raw", "Triumph1.raw", "Uma1.raw", + "Walken1.raw", "Walken2.raw", "Z1.raw", "Z2.raw" + ]; + + var SOUND_BASE_URL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/"; + + for (var i = 0; i < sound_filenames.length; i++) { + sounds.push(new Sound(SOUND_BASE_URL + sound_filenames[i])); + } +} + +var sounds = []; +loadSounds(); + +function loadAnimations() { + + var animation_filenames = []; + var ANIMATION_BASE_URL = "http://highfidelity-dev.s3.amazonaws.com/animations/"; + + if (botNumber < 20) { + animation_filenames = ["robot/wave_hip_hop_dance.fbx", "robot/robot_hip_hop_dance.fbx"]; + } else if (botNumber <= 40) { + animation_filenames = ["superhero/house_dancing_2.fbx", "superhero/house_dancing_3.fbx", "superhero/house_dancing_4.fbx"]; + } else if (botNumber <= 60) { + animation_filenames = ["amber/house_dancing.fbx"] + } else if (botNumber <= 80) { + animation_filenames = ["ron/hip_hop_dancing.fbx", "ron/gangnam_style.fbx"]; + } else { + animation_filenames = ["angie/hip_hop_dancing_6.fbx"]; + } + + for (var i = 0; i < animation_filenames.length; i++) { + animations.push(AnimationCache.getAnimation(ANIMATION_BASE_URL + animation_filenames[i])); + } +} + +var animations = []; +loadAnimations(); + +function playRandomSound() { + if (!Agent.isPlayingAvatarSound) { + var whichSound = Math.floor((Math.random() * sounds.length) % sounds.length); + Agent.playAvatarSound(sounds[whichSound]); + } +} + +function stopWaving() { + isWaving = false; + Avatar.clearJointData(SHOULDER_JOINT_NUMBER); + Avatar.clearJointData(ELBOW_JOINT_NUMBER); + Avatar.clearJointData(JOINT_SPINE); +} + +//Animation KeyFrame constructor. rightJoints and leftJoints must be the same size +function WalkKeyFrame(rightJoints, leftJoints, singleJoints) { + this.rotations = []; + + for (var i = 0; i < rightJoints.length; i++) { + this.rotations[this.rotations.length] = rightJoints[i]; + this.rotations[this.rotations.length] = leftJoints[i]; + } + for (var i = 0; i < singleJoints.length; i++) { + this.rotations[this.rotations.length] = singleJoints[i]; + } +} + +//Procedural walk animation using two keyframes +//We use a separate array for front and back joints +var frontKeyFrames = []; +var backKeyFrames = []; +//for non mirrored joints such as the spine +var singleKeyFrames = []; +//Pitch, yaw, and roll for the joints +var frontAngles = []; +var backAngles = []; +//for non mirrored joints such as the spine +var singleAngles = []; + + + +//Actual joint mappings +var SHOULDER_JOINT_NUMBER = 15; +var ELBOW_JOINT_NUMBER = 16; +var JOINT_R_HIP = 1; +var JOINT_R_KNEE = 2; +var JOINT_L_HIP = 6; +var JOINT_L_KNEE = 7; +var JOINT_R_ARM = 15; +var JOINT_R_FOREARM = 16; +var JOINT_L_ARM = 39; +var JOINT_L_FOREARM = 40; +var JOINT_SPINE = 11; + +// ******************************* Animation Is Defined Below ************************************* + +var NUM_FRAMES = 2; +for (var i = 0; i < NUM_FRAMES; i++) { + frontAngles[i] = []; + backAngles[i] = []; + singleAngles[i] = []; + frontKeyFrames[i] = []; + backKeyFrames[i] = []; + singleKeyFrames[i] = []; +} +//Joint order for actual joint mappings, should be interleaved R,L,R,L,...S,S,S for R = right, L = left, S = single +var JOINT_ORDER = [JOINT_R_HIP, JOINT_L_HIP, JOINT_R_KNEE, JOINT_L_KNEE, JOINT_R_ARM, JOINT_L_ARM, JOINT_R_FOREARM, JOINT_L_FOREARM, JOINT_SPINE]; + +//Joint indices for joints that are duplicated, such as arms, It must match JOINT_ORDER +var HIP = 0; +var KNEE = 1; +var ARM = 2; +var FOREARM = 3; +//Joint indices for single joints +var SPINE = 0; + +//Symmetry multipliers for dthe left half [pitch, roll, yaw]. -1 means reflect, 1 means no reflect +var SYMMETRY = []; +SYMMETRY[HIP] = [1, -1, -1]; +SYMMETRY[KNEE] = [1, -1, -1]; +SYMMETRY[ARM] = [1, -1, -1]; +SYMMETRY[FOREARM] = [1, -1, -1]; + +//We have to store the angles so we can invert yaw and roll when making the animation +//symmetrical + + +//Front refers to leg, not arm. +//Legs Extending +frontAngles[0][HIP] = [30.0, 0.0, 8.0]; +frontAngles[0][KNEE] = [-15.0, 0.0, 0.0]; +frontAngles[0][ARM] = [85.0, -25.0, 0.0]; +frontAngles[0][FOREARM] = [0.0, 0.0, -15.0]; + +backAngles[0][HIP] = [-15, 0.0, 8.0]; +backAngles[0][KNEE] = [-28, 0.0, 0.0]; +backAngles[0][ARM] = [85.0, 20.0, 0.0]; +backAngles[0][FOREARM] = [10.0, 0.0, -25.0]; + +singleAngles[0][SPINE] = [-0.0, 0.0, 0.0]; + +//Legs Passing +frontAngles[1][HIP] = [6.0, 0.0, 8.0]; +frontAngles[1][KNEE] = [-12.0, 0.0, 0.0]; +frontAngles[1][ARM] = [85.0, 0.0, 0.0]; +frontAngles[1][FOREARM] = [0.0, 0.0, -15.0]; + +backAngles[1][HIP] = [10.0, 0.0, 8.0]; +backAngles[1][KNEE] = [-55.0, 0.0, 0.0]; +backAngles[1][ARM] = [85.0, 0.0, 0.0]; +backAngles[1][FOREARM] = [0.0, 0.0, -15.0]; + +singleAngles[1][SPINE] = [0.0, 0.0, 0.0]; + +// ******************************* Animation Is Defined Above ************************************* + +//Actual keyframes for the animation +var walkKeyFrames = []; +//Generate quaternions from the angles +for (var i = 0; i < frontAngles.length; i++) { + for (var j = 0; j < frontAngles[i].length; j++) { + frontKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(frontAngles[i][j][0], frontAngles[i][j][1], frontAngles[i][j][2]); + backKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(SYMMETRY[j][0] * backAngles[i][j][0], SYMMETRY[j][1] * backAngles[i][j][1], SYMMETRY[j][2] * backAngles[i][j][2]); + } +} +for (var i = 0; i < singleAngles.length; i++) { + for (var j = 0; j < singleAngles[i].length; j++) { + singleKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(singleAngles[i][j][0], singleAngles[i][j][1], singleAngles[i][j][2]); + } +} +walkKeyFrames[0] = new WalkKeyFrame(frontKeyFrames[0], backKeyFrames[0], singleKeyFrames[0]); +walkKeyFrames[1] = new WalkKeyFrame(frontKeyFrames[1], backKeyFrames[1], singleKeyFrames[1]); + +//Generate mirrored quaternions for the other half of the body +for (var i = 0; i < frontAngles.length; i++) { + for (var j = 0; j < frontAngles[i].length; j++) { + frontKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(SYMMETRY[j][0] * frontAngles[i][j][0], SYMMETRY[j][1] * frontAngles[i][j][1], SYMMETRY[j][2] * frontAngles[i][j][2]); + backKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(backAngles[i][j][0], backAngles[i][j][1], backAngles[i][j][2]); + } +} +for (var i = 0; i < singleAngles.length; i++) { + for (var j = 0; j < singleAngles[i].length; j++) { + singleKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(-singleAngles[i][j][0], -singleAngles[i][j][1], -singleAngles[i][j][2]); + } +} +walkKeyFrames[2] = new WalkKeyFrame(backKeyFrames[0], frontKeyFrames[0], singleKeyFrames[0]); +walkKeyFrames[3] = new WalkKeyFrame(backKeyFrames[1], frontKeyFrames[1], singleKeyFrames[1]); + +//Hook up pointers to the next keyframe +for (var i = 0; i < walkKeyFrames.length - 1; i++) { + walkKeyFrames[i].nextFrame = walkKeyFrames[i+1]; +} +walkKeyFrames[walkKeyFrames.length-1].nextFrame = walkKeyFrames[0]; + +//Set up the bezier curve control points using technique described at +//https://www.cs.tcd.ie/publications/tech-reports/reports.94/TCD-CS-94-18.pdf +//Set up all C1 +for (var i = 0; i < walkKeyFrames.length; i++) { + walkKeyFrames[i].nextFrame.controlPoints = []; + for (var j = 0; j < walkKeyFrames[i].rotations.length; j++) { + walkKeyFrames[i].nextFrame.controlPoints[j] = []; + var R = Quat.slerp(walkKeyFrames[i].rotations[j], walkKeyFrames[i].nextFrame.rotations[j], 2.0); + var T = Quat.slerp(R, walkKeyFrames[i].nextFrame.nextFrame.rotations[j], 0.5); + walkKeyFrames[i].nextFrame.controlPoints[j][0] = Quat.slerp(walkKeyFrames[i].nextFrame.rotations[j], T, 0.33333); + } +} +//Set up all C2 +for (var i = 0; i < walkKeyFrames.length; i++) { + for (var j = 0; j < walkKeyFrames[i].rotations.length; j++) { + walkKeyFrames[i].controlPoints[j][1] = Quat.slerp(walkKeyFrames[i].nextFrame.rotations[j], walkKeyFrames[i].nextFrame.controlPoints[j][0], -1.0); + } +} +//DeCasteljau evaluation to evaluate the bezier curve +function deCasteljau(k1, k2, c1, c2, f) { + var a = Quat.slerp(k1, c1, f); + var b = Quat.slerp(c1, c2, f); + var c = Quat.slerp(c2, k2, f); + var d = Quat.slerp(a, b, f); + var e = Quat.slerp(b, c, f); + return Quat.slerp(d, e, f); +} + +var currentFrame = 0; + +var walkTime = 0.0; +var walkFrequency = 3.0; + +function keepWalking(deltaTime) { + + walkTime += walkFrequency * deltaTime; + if (walkTime > 1.0) { + walkTime = 0.0; + currentFrame++; + if (currentFrame > 3) { + currentFrame = 0; + } + } + + var frame = walkKeyFrames[currentFrame]; + + for (var i = 0; i < JOINT_ORDER.length; i++) { + Avatar.setJointData(JOINT_ORDER[i], deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], walkTime)); + } +} + +function stopWalking() { + Avatar.clearJointData(JOINT_R_HIP); + Avatar.clearJointData(JOINT_R_KNEE); + Avatar.clearJointData(JOINT_L_HIP); + Avatar.clearJointData(JOINT_L_KNEE); +} + +var trailingAverageLoudness = 0; +var MAX_SAMPLE = 32767; +var DB_METER_BASE = Math.log(MAX_SAMPLE); + +var RAND_RATIO_LAST = getRandomFloat(0.1, 0.3); +var RAND_TRAILING = 1 - RAND_RATIO_LAST; + +function jumpWithLoudness(deltaTime) { + // potentially change pelvis height depending on trailing average loudness + + pelvisOscillatorVelocity += deltaTime * Agent.lastReceivedAudioLoudness * 700.0 ; + + pelvisOscillatorVelocity -= pelvisOscillatorPosition * 0.75; + pelvisOscillatorVelocity *= 0.97; + pelvisOscillatorPosition += deltaTime * pelvisOscillatorVelocity; + Avatar.headPitch = pelvisOscillatorPosition * 60.0; + + var pelvisPosition = Avatar.position; + pelvisPosition.y = (Y_PELVIS - 0.35) + pelvisOscillatorPosition; + + if (pelvisPosition.y < Y_PELVIS) { + pelvisPosition.y = Y_PELVIS; + } else if (pelvisPosition.y > Y_PELVIS + 1.0) { + pelvisPosition.y = Y_PELVIS + 1.0; + } + + Avatar.position = pelvisPosition; +} + +var jointMapping = null; +var frameIndex = 0.0; +var isPlayingDanceAnimation = false; +var randomAnimation = null; +var animationLoops = 1; +var forcedMove = false; + +var FRAME_RATE = 30.0; + +var wasMovingLastFrame = false; +var wasDancing = false; + +function danceAnimation(deltaTime) { + + var flooredFrame = Math.floor(frameIndex); + + if (jointMapping === null || flooredFrame >= randomAnimation.frames.length * animationLoops) { + // we've run our animation for our number of loops, start a new one + frameIndex = 0.0; + jointMapping = null; + randomAnimation = null; + } + + if (isMoving || (!wasMovingLastFrame && frameIndex === 0)) { + if (!isMoving) { + forcedMove = true; + possiblyStopDancing(); + } + + wasMovingLastFrame = true; + handleWalking(); + } else { + if (jointMapping === null) { + // pick a random animation + var whichAnimation = Math.floor((Math.random() * animations.length) % animations.length); + randomAnimation = animations[whichAnimation]; + + var avatarJointNames = Avatar.jointNames; + var animationJointNames = randomAnimation.jointNames; + if (avatarJointNames === 0 || animationJointNames.length === 0) { + return; + } + jointMapping = new Array(avatarJointNames.length); + for (var i = 0; i < avatarJointNames.length; i++) { + jointMapping[i] = animationJointNames.indexOf(avatarJointNames[i]); + } + } + + frameIndex += deltaTime * FRAME_RATE; + var frames = randomAnimation.frames; + var rotations = frames[flooredFrame % frames.length].rotations; + for (var j = 0; j < jointMapping.length; j++) { + var rotationIndex = jointMapping[j]; + if (rotationIndex != -1) { + Avatar.setJointData(j, rotations[rotationIndex]); + } + } + + wasMovingLastFrame = false; + wasDancing = true; + } +} + +function handleHeadTurn() { + if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) { + targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE); + isTurningHead = true; + } else { + Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * PITCH_RATE; + if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE) { + isTurningHead = false; + } + } +} + +var currentShoulderQuat = Avatar.getJointRotation(SHOULDER_JOINT_NUMBER); +var targetShoulderQuat = currentShoulderQuat; +var idleShoulderQuat = currentShoulderQuat; +var currentSpineQuat = Avatar.getJointRotation(JOINT_SPINE); +var targetSpineQuat = currentSpineQuat; +var idleSpineQuat = currentSpineQuat; +var currentElbowQuat = Avatar.getJointRotation(ELBOW_JOINT_NUMBER); +var targetElbowQuat = currentElbowQuat; +var idleElbowQuat = currentElbowQuat; + +function handleWalking(deltaTime) { + if (forcedMove || (!isMoving && Math.random() < CHANCE_OF_MOVING)) { + // Set new target location + targetDirection = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 })); + var front = Quat.getFront(targetDirection); + + targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL))); + + targetPosition.x = clamp(targetPosition.x, X_MIN, X_MAX); + targetPosition.z = clamp(targetPosition.z, Z_MIN, Z_MAX); + targetPosition.y = Y_PELVIS; + + wasMovingLastFrame = true; + isMoving = true; + forcedMove = false; + } else if (isMoving) { + keepWalking(deltaTime); + // Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE)); + Avatar.orientation = Quat.slerp(Avatar.orientation, targetDirection, TURN_RATE); + var diff = Vec3.subtract(Avatar.position, targetPosition); + diff.y = 0.0; + + wasMovingLastFrame = true; + + if (Vec3.length(diff) < STOP_TOLERANCE) { + isMoving = false; + stopWalking(); + } + } +} + +function handleTalking() { + if (Math.random() < CHANCE_OF_SOUND) { + playRandomSound(); + } +} + +function changePelvisHeight(newHeight) { + var newPosition = Avatar.position; + newPosition.y = newHeight; + Avatar.position = newPosition; +} + +function possiblyStopDancing() { + if (wasDancing) { + for (var j = 0; j < Avatar.jointNames.length; j++) { + Avatar.clearJointData(j); + } + + changePelvisHeight(Y_PELVIS); + } +} + +function updateBehavior(deltaTime) { + cumulativeTime += deltaTime; + + if (AvatarList.containsAvatarWithDisplayName("mrdj")) { + if (wasMovingLastFrame && !wasDancing) { + isMoving = false; + } + + // we have a DJ, shouldn't we be dancing? + jumpWithLoudness(deltaTime); + danceAnimation(deltaTime); + } else { + // make sure we're not dancing anymore + possiblyStopDancing(); + + wasDancing = false; + + // no DJ, let's just chill on the dancefloor - randomly walking and talking + handleHeadTurn(); + handleWalking(deltaTime); + handleTalking(); + } +} + +Script.update.connect(updateBehavior); \ No newline at end of file diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 8308536f97..66281883f0 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -76,6 +76,10 @@ glm::quat Quat::squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& return glm::squad(q1, q2, s1, s2, h); } +float Quat::dot(const glm::quat& q1, const glm::quat& q2) { + return glm::dot(q1, q2); +} + void Quat::print(const QString& lable, const glm::quat& q) { qDebug() << qPrintable(lable) << q.x << "," << q.y << "," << q.z << "," << q.w; } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 190c823118..faae636f02 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -38,6 +38,7 @@ public slots: glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha); glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha); glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h); + float dot(const glm::quat& q1, const glm::quat& q2); void print(const QString& lable, const glm::quat& q); }; From ef5db5321d7051fdcc4375b5ec1c511c4fe7c65b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Jul 2014 09:46:59 -0700 Subject: [PATCH 19/56] make voxel charges affect agents --- domain-server/resources/web/settings/describe.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index 0ab647734b..beb02411c9 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -19,7 +19,7 @@ }, "voxels": { "label": "Voxels", - "assignment-types": [3], + "assignment-types": [2,3], "settings": { "voxel-wallet": { "label": "Destination Wallet ID", From 9a7267a10c33506cd39e6ebe4bb404e23fefb54f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Jul 2014 10:52:14 -0700 Subject: [PATCH 20/56] remove an unecessary reference to loop --- assignment-client/src/audio/AudioMixer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 91ecee76e2..e2d9636118 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -415,7 +415,7 @@ void AudioMixer::run() { // block until we get the settingsRequestComplete signal QEventLoop loop; - loop.connect(&domainHandler, &DomainHandler::settingsRequestComplete, &loop, &QEventLoop::quit); + connect(&domainHandler, &DomainHandler::settingsRequestComplete, &loop, &QEventLoop::quit); domainHandler.requestDomainSettings(); loop.exec(); From 85b8449e83832d4d479da9f814c4a7b10c249b94 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Jul 2014 11:17:27 -0700 Subject: [PATCH 21/56] use two different signals for settings success and failure --- assignment-client/src/audio/AudioMixer.cpp | 3 ++- libraries/networking/src/DomainHandler.cpp | 4 ++-- libraries/networking/src/DomainHandler.h | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index e2d9636118..4c3d951716 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -415,7 +415,8 @@ void AudioMixer::run() { // block until we get the settingsRequestComplete signal QEventLoop loop; - connect(&domainHandler, &DomainHandler::settingsRequestComplete, &loop, &QEventLoop::quit); + connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); + connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); domainHandler.requestDomainSettings(); loop.exec(); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 0f37160512..e316ea2cc5 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -157,7 +157,7 @@ void DomainHandler::settingsRequestFinished() { _settingsObject = QJsonDocument::fromJson(settingsReply->readAll()).object(); qDebug() << "Received domain settings."; - emit settingsRequestComplete(true); + emit settingsReceived(); // reset failed settings requests to 0, we got them _failedSettingsRequests = 0; @@ -170,7 +170,7 @@ void DomainHandler::settingsRequestFinished() { qDebug() << "Failed to retreive domain-server settings" << MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS << "times. Re-setting connection to domain."; clearSettings(); clearConnectionInfo(); - emit settingsRequestComplete(false); + emit settingsReceiveFail(); } else { requestDomainSettings(); } diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 67edd64172..81745713e7 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -69,7 +69,8 @@ signals: void hostnameChanged(const QString& hostname); void connectedToDomain(const QString& hostname); - void settingsRequestComplete(bool wasSuccessful); + void settingsReceived(); + void settingsReceiveFail(); private: void reset(); From 39e6d7d31b99483a01e3d293d215bd3af4254b92 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Jul 2014 11:40:07 -0700 Subject: [PATCH 22/56] octree packet methods can take a cost --- libraries/octree/src/OctreeEditPacketSender.cpp | 9 ++++++--- libraries/octree/src/OctreeEditPacketSender.h | 6 +++--- libraries/voxels/src/VoxelsScriptingInterface.cpp | 1 - 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 2ed8f6c2a0..5f6fb1274b 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -87,7 +87,8 @@ bool OctreeEditPacketSender::serversExist() const { // This method is called when the edit packet layer has determined that it has a fully formed packet destined for // a known nodeID. -void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer, ssize_t length) { +void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer, + ssize_t length, qint64 satoshiCost) { NodeList* nodeList = NodeList::getInstance(); foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { @@ -157,7 +158,8 @@ void OctreeEditPacketSender::processPreServerExistsPackets() { } } -void OctreeEditPacketSender::queuePendingPacketToNodes(PacketType type, unsigned char* buffer, ssize_t length) { +void OctreeEditPacketSender::queuePendingPacketToNodes(PacketType type, unsigned char* buffer, + ssize_t length, qint64 satoshiCost) { // If we're asked to save messages while waiting for voxel servers to arrive, then do so... if (_maxPendingMessages > 0) { @@ -210,7 +212,8 @@ void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t l // NOTE: codeColorBuffer - is JUST the octcode/color and does not contain the packet header! -void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, unsigned char* codeColorBuffer, ssize_t length) { +void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, unsigned char* codeColorBuffer, + ssize_t length, qint64 satoshiCost) { if (!_shouldSend) { return; // bail early diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index cdcfc21d4a..584f74fd39 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -39,7 +39,7 @@ public: /// Queues a single edit message. Will potentially send a pending multi-command packet. Determines which server /// node or nodes the packet should be sent to. Can be called even before servers are known, in which case up to /// MaxPendingMessages will be buffered and processed when servers are known. - void queueOctreeEditMessage(PacketType type, unsigned char* buffer, ssize_t length); + void queueOctreeEditMessage(PacketType type, unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0); /// Releases all queued messages even if those messages haven't filled an MTU packet. This will move the packed message /// packets onto the send queue. If running in threaded mode, the caller does not need to do any further processing to @@ -100,8 +100,8 @@ public: protected: bool _shouldSend; - void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, ssize_t length); - void queuePendingPacketToNodes(PacketType type, unsigned char* buffer, ssize_t length); + void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0); + void queuePendingPacketToNodes(PacketType type, unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0); void queuePacketToNodes(unsigned char* buffer, ssize_t length); void initializePacket(EditPacketBuffer& packetBuffer, PacketType type); void releaseQueuedPacket(EditPacketBuffer& packetBuffer); // releases specific queued packet diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index e877f99760..05be0fe350 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -71,7 +71,6 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale, VoxelDetail addVoxelDetail = {x / (float)TREE_SCALE, y / (float)TREE_SCALE, z / (float)TREE_SCALE, scale / (float)TREE_SCALE, red, green, blue}; - // handle the local tree also... if (_tree) { if (_undoStack) { From 6be62842e3e1ede19769946c806ff84b7937c617 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 29 Jul 2014 15:45:12 -0700 Subject: [PATCH 23/56] Added walkWheel for syncinc animation speed, plus other improvements. --- examples/proceduralAnimation.js | 0 examples/{dancer.js => proceduralBot.js} | 214 +++++++++-------------- 2 files changed, 81 insertions(+), 133 deletions(-) create mode 100644 examples/proceduralAnimation.js rename examples/{dancer.js => proceduralBot.js} (74%) diff --git a/examples/proceduralAnimation.js b/examples/proceduralAnimation.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/dancer.js b/examples/proceduralBot.js similarity index 74% rename from examples/dancer.js rename to examples/proceduralBot.js index e7a75f7596..ce88f596b6 100644 --- a/examples/dancer.js +++ b/examples/proceduralBot.js @@ -1,15 +1,18 @@ // -// dancer.js +// proceduralBot.js // hifi // -// Created by Stephen Birarda on 2/20/14. -// Modified by Philip on 3/3/14 +// Created by Ben Arnold on 7/29/2013 +// // Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // // This is an example script that demonstrates an NPC avatar. // // +//For procedural walk animation +Script.include("proceduralAnimation.js"); + function getRandomFloat(min, max) { return Math.random() * (max - min) + min; } @@ -22,41 +25,41 @@ function printVector(string, vector) { print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); } -var CHANCE_OF_MOVING = 0.01; +var CHANCE_OF_MOVING = 0.1; var CHANCE_OF_SOUND = 0; var CHANCE_OF_HEAD_TURNING = 0.05; var CHANCE_OF_BIG_MOVE = 0.1; var CHANCE_OF_WAVING = 0.009; -var isMoving = true; +var isMoving = false; var isTurningHead = false; -var isPlay -ingAudio = false; +var isPlayingAudio = false; var isWaving = false; var waveFrequency = 0.0; var waveAmplitude = 0.0; -var X_MIN = 5.50; -var X_MAX = 5.60; -var Z_MIN = 5.00; -var Z_MAX = 5.10; +var X_MIN = 0.50; +var X_MAX = 15.60; +var Z_MIN = 0.50; +var Z_MAX = 15.10; var Y_PELVIS = 1.0; var MAX_PELVIS_DELTA = 2.5; var AVATAR_PELVIS_HEIGHT = 0.75; -var MOVE_RANGE_SMALL = 1.0; +var MOVE_RANGE_SMALL = 10.0; var TURN_RANGE = 70.0; var STOP_TOLERANCE = 0.05; var MOVE_RATE = 0.05; -var TURN_RATE = 0.15; -var PITCH_RATE = 0.20; -var PITCH_RANGE = 30.0; +var TURN_RATE = 0.2; +var PITCH_RATE = 0.10; +var PITCH_RANGE = 20.0; -var firstPosition = { x: getRandomFloat(X_MIN, X_MAX), y: Y_PELVIS, z: getRandomFloat(Z_MIN, Z_MAX) }; +//var firstPosition = { x: getRandomFloat(X_MIN, X_MAX), y: Y_PELVIS, z: getRandomFloat(Z_MIN, Z_MAX) }; +var firstPosition = { x: 0.5, y: Y_PELVIS, z: 0.5 }; var targetPosition = { x: 0, y: 0, z: 0 }; -var targetDirection = { x: 0, y: 0, z: 0, w: 0 }; -var currentDirection = { x: 0, y: 0, z: 0, w: 0 }; +var targetOrientation = { x: 0, y: 0, z: 0, w: 0 }; +var currentOrientation = { x: 0, y: 0, z: 0, w: 0 }; var targetHeadPitch = 0.0; var cumulativeTime = 0.0; @@ -130,30 +133,6 @@ function loadSounds() { var sounds = []; loadSounds(); -function loadAnimations() { - - var animation_filenames = []; - var ANIMATION_BASE_URL = "http://highfidelity-dev.s3.amazonaws.com/animations/"; - - if (botNumber < 20) { - animation_filenames = ["robot/wave_hip_hop_dance.fbx", "robot/robot_hip_hop_dance.fbx"]; - } else if (botNumber <= 40) { - animation_filenames = ["superhero/house_dancing_2.fbx", "superhero/house_dancing_3.fbx", "superhero/house_dancing_4.fbx"]; - } else if (botNumber <= 60) { - animation_filenames = ["amber/house_dancing.fbx"] - } else if (botNumber <= 80) { - animation_filenames = ["ron/hip_hop_dancing.fbx", "ron/gangnam_style.fbx"]; - } else { - animation_filenames = ["angie/hip_hop_dancing_6.fbx"]; - } - - for (var i = 0; i < animation_filenames.length; i++) { - animations.push(AnimationCache.getAnimation(ANIMATION_BASE_URL + animation_filenames[i])); - } -} - -var animations = []; -loadAnimations(); function playRandomSound() { if (!Agent.isPlayingAvatarSound) { @@ -162,13 +141,6 @@ function playRandomSound() { } } -function stopWaving() { - isWaving = false; - Avatar.clearJointData(SHOULDER_JOINT_NUMBER); - Avatar.clearJointData(ELBOW_JOINT_NUMBER); - Avatar.clearJointData(JOINT_SPINE); -} - //Animation KeyFrame constructor. rightJoints and leftJoints must be the same size function WalkKeyFrame(rightJoints, leftJoints, singleJoints) { this.rotations = []; @@ -231,13 +203,6 @@ var FOREARM = 3; //Joint indices for single joints var SPINE = 0; -//Symmetry multipliers for dthe left half [pitch, roll, yaw]. -1 means reflect, 1 means no reflect -var SYMMETRY = []; -SYMMETRY[HIP] = [1, -1, -1]; -SYMMETRY[KNEE] = [1, -1, -1]; -SYMMETRY[ARM] = [1, -1, -1]; -SYMMETRY[FOREARM] = [1, -1, -1]; - //We have to store the angles so we can invert yaw and roll when making the animation //symmetrical @@ -254,7 +219,7 @@ backAngles[0][KNEE] = [-28, 0.0, 0.0]; backAngles[0][ARM] = [85.0, 20.0, 0.0]; backAngles[0][FOREARM] = [10.0, 0.0, -25.0]; -singleAngles[0][SPINE] = [-0.0, 0.0, 0.0]; +singleAngles[0][SPINE] = [0.0, -15.0, 5.0]; //Legs Passing frontAngles[1][HIP] = [6.0, 0.0, 8.0]; @@ -277,7 +242,7 @@ var walkKeyFrames = []; for (var i = 0; i < frontAngles.length; i++) { for (var j = 0; j < frontAngles[i].length; j++) { frontKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(frontAngles[i][j][0], frontAngles[i][j][1], frontAngles[i][j][2]); - backKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(SYMMETRY[j][0] * backAngles[i][j][0], SYMMETRY[j][1] * backAngles[i][j][1], SYMMETRY[j][2] * backAngles[i][j][2]); + backKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(backAngles[i][j][0], -backAngles[i][j][1], -backAngles[i][j][2]); } } for (var i = 0; i < singleAngles.length; i++) { @@ -291,7 +256,7 @@ walkKeyFrames[1] = new WalkKeyFrame(frontKeyFrames[1], backKeyFrames[1], singleK //Generate mirrored quaternions for the other half of the body for (var i = 0; i < frontAngles.length; i++) { for (var j = 0; j < frontAngles[i].length; j++) { - frontKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(SYMMETRY[j][0] * frontAngles[i][j][0], SYMMETRY[j][1] * frontAngles[i][j][1], SYMMETRY[j][2] * frontAngles[i][j][2]); + frontKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(frontAngles[i][j][0], -frontAngles[i][j][1], -frontAngles[i][j][2]); backKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(backAngles[i][j][0], backAngles[i][j][1], backAngles[i][j][2]); } } @@ -340,12 +305,18 @@ function deCasteljau(k1, k2, c1, c2, f) { var currentFrame = 0; var walkTime = 0.0; -var walkFrequency = 3.0; + +var walkWheelRadius = 0.5; +var walkWheelRate = 2.0 * 3.141592 * walkWheelRadius / 8.0; + +var avatarAcceleration = 0.75; +var avatarVelocity = 0.0; +var avatarMaxVelocity = 1.4; function keepWalking(deltaTime) { - walkTime += walkFrequency * deltaTime; - if (walkTime > 1.0) { + walkTime += avatarVelocity * deltaTime; + if (walkTime > walkWheelRate) { walkTime = 0.0; currentFrame++; if (currentFrame > 3) { @@ -355,18 +326,13 @@ function keepWalking(deltaTime) { var frame = walkKeyFrames[currentFrame]; + var interp = walkTime / walkWheelRate; + for (var i = 0; i < JOINT_ORDER.length; i++) { - Avatar.setJointData(JOINT_ORDER[i], deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], walkTime)); + Avatar.setJointData(JOINT_ORDER[i], deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], interp)); } } -function stopWalking() { - Avatar.clearJointData(JOINT_R_HIP); - Avatar.clearJointData(JOINT_R_KNEE); - Avatar.clearJointData(JOINT_L_HIP); - Avatar.clearJointData(JOINT_L_KNEE); -} - var trailingAverageLoudness = 0; var MAX_SAMPLE = 32767; var DB_METER_BASE = Math.log(MAX_SAMPLE); @@ -408,56 +374,6 @@ var FRAME_RATE = 30.0; var wasMovingLastFrame = false; var wasDancing = false; -function danceAnimation(deltaTime) { - - var flooredFrame = Math.floor(frameIndex); - - if (jointMapping === null || flooredFrame >= randomAnimation.frames.length * animationLoops) { - // we've run our animation for our number of loops, start a new one - frameIndex = 0.0; - jointMapping = null; - randomAnimation = null; - } - - if (isMoving || (!wasMovingLastFrame && frameIndex === 0)) { - if (!isMoving) { - forcedMove = true; - possiblyStopDancing(); - } - - wasMovingLastFrame = true; - handleWalking(); - } else { - if (jointMapping === null) { - // pick a random animation - var whichAnimation = Math.floor((Math.random() * animations.length) % animations.length); - randomAnimation = animations[whichAnimation]; - - var avatarJointNames = Avatar.jointNames; - var animationJointNames = randomAnimation.jointNames; - if (avatarJointNames === 0 || animationJointNames.length === 0) { - return; - } - jointMapping = new Array(avatarJointNames.length); - for (var i = 0; i < avatarJointNames.length; i++) { - jointMapping[i] = animationJointNames.indexOf(avatarJointNames[i]); - } - } - - frameIndex += deltaTime * FRAME_RATE; - var frames = randomAnimation.frames; - var rotations = frames[flooredFrame % frames.length].rotations; - for (var j = 0; j < jointMapping.length; j++) { - var rotationIndex = jointMapping[j]; - if (rotationIndex != -1) { - Avatar.setJointData(j, rotations[rotationIndex]); - } - } - - wasMovingLastFrame = false; - wasDancing = true; - } -} function handleHeadTurn() { if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) { @@ -481,14 +397,32 @@ var currentElbowQuat = Avatar.getJointRotation(ELBOW_JOINT_NUMBER); var targetElbowQuat = currentElbowQuat; var idleElbowQuat = currentElbowQuat; +function stopWalking() { + Avatar.clearJointData(JOINT_R_HIP); + Avatar.clearJointData(JOINT_R_KNEE); + Avatar.clearJointData(JOINT_L_HIP); + Avatar.clearJointData(JOINT_L_KNEE); + avatarVelocity = 0.0; + isMoving = false; +} + +var MAX_ATTEMPTS = 40; function handleWalking(deltaTime) { + if (forcedMove || (!isMoving && Math.random() < CHANCE_OF_MOVING)) { // Set new target location - targetDirection = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 })); - var front = Quat.getFront(targetDirection); - - targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL))); + //Keep trying new orientations if the desired target location is out of bounds + var attempts = 0; + do { + targetOrientation = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 })); + var front = Quat.getFront(targetOrientation); + + targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL))); + } + while ((targetPosition.x < X_MIN || targetPosition.x > X_MAX || targetPosition.z < Z_MIN || targetPosition.z > Z_MAX) + && attempts < MAX_ATEMPTS); + targetPosition.x = clamp(targetPosition.x, X_MIN, X_MAX); targetPosition.z = clamp(targetPosition.z, Z_MIN, Z_MAX); targetPosition.y = Y_PELVIS; @@ -498,16 +432,31 @@ function handleWalking(deltaTime) { forcedMove = false; } else if (isMoving) { keepWalking(deltaTime); - // Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(Vec3.subtract(targetPosition, Avatar.position), MOVE_RATE)); - Avatar.orientation = Quat.slerp(Avatar.orientation, targetDirection, TURN_RATE); - var diff = Vec3.subtract(Avatar.position, targetPosition); - diff.y = 0.0; - wasMovingLastFrame = true; + var targetVector = Vec3.subtract(targetPosition, Avatar.position); + var distance = Vec3.length(targetVector); + if (distance <= avatarVelocity * deltaTime) { + Avatar.position = targetPosition; + stopWalking(); + } else { + var direction = Vec3.normalize(targetVector); + //Figure out if we should be slowing down + var t = avatarVelocity / avatarAcceleration; + var d = (avatarVelocity / 2.0) * t; + if (distance < d) { + avatarVelocity -= avatarAcceleration * deltaTime; + if (avatarVelocity <= 0) { + stopWalking(); + } + } else { + avatarVelocity += avatarAcceleration * deltaTime; + if (avatarVelocity > avatarMaxVelocity) avatarVelocity = avatarMaxVelocity; + } + Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(direction, avatarVelocity * deltaTime)); + Avatar.orientation = Quat.mix(Avatar.orientation, targetOrientation, TURN_RATE); + + wasMovingLastFrame = true; - if (Vec3.length(diff) < STOP_TOLERANCE) { - isMoving = false; - stopWalking(); } } } @@ -544,7 +493,6 @@ function updateBehavior(deltaTime) { // we have a DJ, shouldn't we be dancing? jumpWithLoudness(deltaTime); - danceAnimation(deltaTime); } else { // make sure we're not dancing anymore possiblyStopDancing(); From 5fd7a11c6b41020168789b7c34d9b1d6aea658a9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Jul 2014 16:38:07 -0700 Subject: [PATCH 24/56] rename the voxel cost settings --- domain-server/resources/web/settings/describe.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index beb02411c9..2cb558bf0c 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -27,15 +27,15 @@ "placeholder": "00000000-0000-0000-0000-000000000000", "default": "" }, - "per-voxel-cost": { + "per-voxel-credits": { "label": "Per Voxel Cost", "help": "Credit cost to change each voxel", "placeholder": "0.0", "default": "0.0", "input_addon": "₵" }, - "volume-cost": { - "label": "Volume Cost", + "per-meter-cubed-credits": { + "label": "Per Meter Cubed Cost", "help": "Credit cost to change each cubed meter of voxel space", "placeholder": "0.0", "default": "0.0", From 40e08d50088de709035fed7ee2cd586be4b17795 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Jul 2014 16:39:06 -0700 Subject: [PATCH 25/56] add voxel cost amounts to VoxelEditPacketSender --- .../voxels/src/VoxelEditPacketSender.cpp | 23 +++++++++++++++++++ libraries/voxels/src/VoxelEditPacketSender.h | 16 +++++++++++++ 2 files changed, 39 insertions(+) diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index 1832d5436e..b4de63b302 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -142,3 +142,26 @@ void VoxelEditPacketSender::queueVoxelEditMessages(PacketType type, int numberOf } } } + +void VoxelEditPacketSender::updateVoxelCosts(const QJsonObject& domainSettingsObject) { + + // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed + const QString VOXEL_SETTINGS_KEY = "voxels"; + const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; + const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; + + if (!domainSettingsObject.isEmpty()) { + float perVoxelCredits = (float) domainSettingsObject[VOXEL_SETTINGS_KEY].toObject()[PER_VOXEL_COST_KEY].toDouble(); + float perMeterCubedCredits = (float) domainSettingsObject[VOXEL_SETTINGS_KEY].toObject()[PER_METER_CUBED_COST_KEY].toDouble(); + + qDebug() << "PV: " << perVoxelCredits << "PMC: " << perMeterCubedCredits; + } else { + qDebug() << "CALLED WITH EMPTY SETTINGS!"; + _satoshisPerVoxel = 0; + _satoshisPerMeterCubed = 0; + } +} + +qint64 VoxelEditPacketSender::satoshiCostForMessage(PacketType type, int numberOfDetails, VoxelDetail *details) { + return 0; +} diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h index 3ef257de6c..33382d7faf 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -15,6 +15,7 @@ #define hifi_VoxelEditPacketSender_h #include + #include "VoxelDetail.h" /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. @@ -49,5 +50,20 @@ public: // My server type is the voxel server virtual char getMyNodeType() const { return NodeType::VoxelServer; } + + qint64 satoshiCostForMessage(PacketType type, int numberOfDetails, VoxelDetail* details); + + void setSatoshisPerVoxel(qint64 satoshisPerVoxel) { _satoshisPerVoxel = satoshisPerVoxel; } + qint64 getSatoshisPerVoxel() const { return _satoshisPerVoxel; } + + void setSatoshisPerMeterCubed(qint64 satoshisPerMeterCubed) { _satoshisPerMeterCubed = satoshisPerMeterCubed; } + qint64 getSatoshisPerMeterCubed() const { return _satoshisPerMeterCubed; } + +public slots: + void updateVoxelCosts(const QJsonObject& domainSettingsObject); + +private: + qint64 _satoshisPerVoxel; + qint64 _satoshisPerMeterCubed; }; #endif // hifi_VoxelEditPacketSender_h From 9be9f1417acad39c8ca5d1d5d0be24dc28562347 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 29 Jul 2014 16:51:57 -0700 Subject: [PATCH 26/56] Made some API changes for procedural animation --- examples/proceduralAnimation.js | 134 ++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/examples/proceduralAnimation.js b/examples/proceduralAnimation.js index e69de29bb2..7eb0873994 100644 --- a/examples/proceduralAnimation.js +++ b/examples/proceduralAnimation.js @@ -0,0 +1,134 @@ +// +// proceduralAnimation.js +// hifi +// +// Created by Ben Arnold on 7/29/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is a Procedural Animation API for creating procedural animations in JS. +// To include it in your JS files, simply use the following line at the top: +// Script.include("proceduralAnimation.js"); + +// You can see a usage example in proceduralBot.js + +ProcAnimAPI = function() { + + // generateKeyFrames(rightAngles, leftAngles, middleAngles, numFrames) + // + // Parameters: + // rightAngles - An array of tuples. The angles in degrees for the joints + // on the right side of the body + // leftAngles - An array of tuples. The angles in degrees for the joints + // on the left side of the body + // middleAngles - An array of tuples. The angles in degrees for the joints + // on the left side of the body + // numFrames - The number of frames in the animation, before mirroring. + // for a 4 frame walk animation, simply supply 2 frames + // and generateKeyFrames will return 4 frames. + // + // Return Value: + // Returns an array of KeyFrames. Each KeyFrame has an array of quaternions + // for each of the joints, generated from the input angles. They will be ordered + // R,L,R,L,...M,M,M,M where R ~ rightAngles, L ~ leftAngles, M ~ middlesAngles. + // The size of the returned array will be numFrames * 2 + this.generateKeyframes = function(rightAngles, leftAngles, middleAngles, numFrames) { + + if (rightAngles.length != leftAngles.length) { + print("ERROR: generateKeyFrames(...) rightAngles and leftAngles must have equal length."); + } + + //for mirrored joints, such as the arms or legs + var rightQuats = []; + var leftQuats = []; + //for non mirrored joints such as the spine + var middleQuats = []; + + for (var i = 0; i < numFrames; i++) { + rightQuats[i] = []; + leftQuats[i] = []; + middleQuats[i] = []; + } + + var finalKeyFrames = []; + //Generate quaternions + for (var i = 0; i < rightAngles.length; i++) { + for (var j = 0; j < rightAngles[i].length; j++) { + rightQuats[i][j] = Quat.fromPitchYawRollDegrees(rightAngles[i][j][0], rightAngles[i][j][1], rightAngles[i][j][2]); + leftQuats[i][j] = Quat.fromPitchYawRollDegrees(leftAngles[i][j][0], -leftAngles[i][j][1], -leftAngles[i][j][2]); + } + } + for (var i = 0; i < middleAngles.length; i++) { + for (var j = 0; j < middleAngles[i].length; j++) { + middleQuats[i][j] = Quat.fromPitchYawRollDegrees(middleAngles[i][j][0], middleAngles[i][j][1], middleAngles[i][j][2]); + } + } + finalKeyFrames[0] = new KeyFrame(rightQuats[0], leftQuats[0], middleQuats[0]); + finalKeyFrames[1] = new KeyFrame(rightQuats[1], leftQuats[1], middleQuats[1]); + + //Generate mirrored quaternions for the other half of the animation + for (var i = 0; i < rightAngles.length; i++) { + for (var j = 0; j < rightAngles[i].length; j++) { + rightQuats[i][j] = Quat.fromPitchYawRollDegrees(rightAngles[i][j][0], -rightAngles[i][j][1], -rightAngles[i][j][2]); + leftQuats[i][j] = Quat.fromPitchYawRollDegrees(leftAngles[i][j][0], leftAngles[i][j][1], leftAngles[i][j][2]); + } + } + for (var i = 0; i < middleAngles.length; i++) { + for (var j = 0; j < middleAngles[i].length; j++) { + middleQuats[i][j] = Quat.fromPitchYawRollDegrees(-middleAngles[i][j][0], -middleAngles[i][j][1], -middleAngles[i][j][2]); + } + } + finalKeyFrames[2] = new KeyFrame(leftQuats[0], rightQuats[0], middleQuats[0]); + finalKeyFrames[3] = new KeyFrame(leftQuats[1], rightQuats[1], middleQuats[1]); + + //Hook up pointers to the next keyframe + for (var i = 0; i < finalKeyFrames.length - 1; i++) { + finalKeyFrames[i].nextFrame = finalKeyFrames[i+1]; + } + finalKeyFrames[finalKeyFrames.length-1].nextFrame = finalKeyFrames[0]; + + //Set up the bezier curve control points using technique described at + //https://www.cs.tcd.ie/publications/tech-reports/reports.94/TCD-CS-94-18.pdf + //Set up all C1 + for (var i = 0; i < finalKeyFrames.length; i++) { + finalKeyFrames[i].nextFrame.controlPoints = []; + for (var j = 0; j < finalKeyFrames[i].rotations.length; j++) { + finalKeyFrames[i].nextFrame.controlPoints[j] = []; + var R = Quat.slerp(finalKeyFrames[i].rotations[j], finalKeyFrames[i].nextFrame.rotations[j], 2.0); + var T = Quat.slerp(R, finalKeyFrames[i].nextFrame.nextFrame.rotations[j], 0.5); + finalKeyFrames[i].nextFrame.controlPoints[j][0] = Quat.slerp(finalKeyFrames[i].nextFrame.rotations[j], T, 0.33333); + } + } + //Set up all C2 + for (var i = 0; i < finalKeyFrames.length; i++) { + for (var j = 0; j < finalKeyFrames[i].rotations.length; j++) { + finalKeyFrames[i].controlPoints[j][1] = Quat.slerp(finalKeyFrames[i].nextFrame.rotations[j], finalKeyFrames[i].nextFrame.controlPoints[j][0], -1.0); + } + } + + return finalKeyFrames; + } + + // Animation KeyFrame constructor. rightJoints and leftJoints must be the same size + this.KeyFrame = function(rightJoints, leftJoints, middleJoints) { + this.rotations = []; + + for (var i = 0; i < rightJoints.length; i++) { + this.rotations[this.rotations.length] = rightJoints[i]; + this.rotations[this.rotations.length] = leftJoints[i]; + } + for (var i = 0; i < middleJoints.length; i++) { + this.rotations[this.rotations.length] = middleJoints[i]; + } + } + + // DeCasteljau evaluation to evaluate the bezier curve. + // This is a very natural looking interpolation + this.deCasteljau = function(k1, k2, c1, c2, f) { + var a = Quat.slerp(k1, c1, f); + var b = Quat.slerp(c1, c2, f); + var c = Quat.slerp(c2, k2, f); + var d = Quat.slerp(a, b, f); + var e = Quat.slerp(b, c, f); + return Quat.slerp(d, e, f); + } +} \ No newline at end of file From 9dbe74b02f85bb70f6d747678fda898dbb079677 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Jul 2014 17:22:17 -0700 Subject: [PATCH 27/56] parse voxel costs to the DomainHandler object --- .../resources/web/settings/describe.json | 2 ++ .../src/DomainServerSettingsManager.cpp | 9 +++++++- libraries/networking/src/DomainHandler.cpp | 23 +++++++++++++++++++ libraries/networking/src/DomainHandler.h | 8 +++++++ .../networking/src/UserActivityLogger.cpp | 2 +- .../voxels/src/VoxelEditPacketSender.cpp | 19 --------------- libraries/voxels/src/VoxelEditPacketSender.h | 13 ----------- 7 files changed, 42 insertions(+), 34 deletions(-) diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index 2cb558bf0c..bf1a9cc23c 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -28,6 +28,7 @@ "default": "" }, "per-voxel-credits": { + "type": "double", "label": "Per Voxel Cost", "help": "Credit cost to change each voxel", "placeholder": "0.0", @@ -35,6 +36,7 @@ "input_addon": "₵" }, "per-meter-cubed-credits": { + "type": "double", "label": "Per Meter Cubed Cost", "help": "Credit cost to change each cubed meter of voxel space", "placeholder": "0.0", diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 4ec88996a6..9c741b2a3b 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -141,6 +141,8 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection return false; } +const QString SETTING_DESCRIPTION_TYPE_KEY = "type"; + void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant, QJsonObject descriptionObject) { @@ -155,7 +157,12 @@ void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ // this is an empty value, clear it in settings variant so the default is sent settingsVariant.remove(key); } else { - settingsVariant[key] = rootValue.toString(); + if (descriptionObject[key].toObject().contains(SETTING_DESCRIPTION_TYPE_KEY)) { + // for now this means that this is a double, so set it as a double + settingsVariant[key] = rootValue.toString().toDouble(); + } else { + settingsVariant[key] = rootValue.toString(); + } } } else if (rootValue.isBool()) { settingsVariant[key] = rootValue.toBool(); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index e316ea2cc5..a50f735542 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include "Assignment.h" @@ -159,6 +161,8 @@ void DomainHandler::settingsRequestFinished() { qDebug() << "Received domain settings."; emit settingsReceived(); + updateVoxelCosts(); + // reset failed settings requests to 0, we got them _failedSettingsRequests = 0; } else { @@ -177,6 +181,25 @@ void DomainHandler::settingsRequestFinished() { } } +void DomainHandler::updateVoxelCosts() { + + // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed + const QString VOXEL_SETTINGS_KEY = "voxels"; + const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; + const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; + + if (!_settingsObject.isEmpty()) { + float perVoxelCredits = (float) _settingsObject[VOXEL_SETTINGS_KEY].toObject()[PER_VOXEL_COST_KEY].toDouble(); + float perMeterCubedCredits = (float) _settingsObject[VOXEL_SETTINGS_KEY].toObject()[PER_METER_CUBED_COST_KEY].toDouble(); + + _satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT); + _satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT); + } else { + _satoshisPerVoxel = 0; + _satoshisPerMeterCubed = 0; + } +} + void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) { // figure out the port that the DS wants us to use for us to talk to them with DTLS int numBytesPacketHeader = numBytesForPacketHeader(dtlsRequirementPacket); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 81745713e7..7b7a80b49a 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -62,6 +62,9 @@ public: void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket); + qint64 getSatoshisPerVoxel() const { return _satoshisPerVoxel; } + qint64 getSatoshisPerMeterCubed() const { return _satoshisPerMeterCubed; } + private slots: void completedHostnameLookup(const QHostInfo& hostInfo); void settingsRequestFinished(); @@ -75,6 +78,8 @@ signals: private: void reset(); + void updateVoxelCosts(); + QUuid _uuid; QString _hostname; HifiSockAddr _sockAddr; @@ -83,6 +88,9 @@ private: QTimer* _handshakeTimer; QJsonObject _settingsObject; int _failedSettingsRequests; + + qint64 _satoshisPerVoxel; + qint64 _satoshisPerMeterCubed; }; #endif // hifi_DomainHandler_h diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 90b9da07dc..e2d3434867 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -70,7 +70,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall } void UserActivityLogger::requestFinished(const QJsonObject& object) { - qDebug() << object; + // qDebug() << object; } void UserActivityLogger::requestError(QNetworkReply::NetworkError error,const QString& string) { diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index b4de63b302..40a0f853f6 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -143,25 +143,6 @@ void VoxelEditPacketSender::queueVoxelEditMessages(PacketType type, int numberOf } } -void VoxelEditPacketSender::updateVoxelCosts(const QJsonObject& domainSettingsObject) { - - // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed - const QString VOXEL_SETTINGS_KEY = "voxels"; - const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; - const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; - - if (!domainSettingsObject.isEmpty()) { - float perVoxelCredits = (float) domainSettingsObject[VOXEL_SETTINGS_KEY].toObject()[PER_VOXEL_COST_KEY].toDouble(); - float perMeterCubedCredits = (float) domainSettingsObject[VOXEL_SETTINGS_KEY].toObject()[PER_METER_CUBED_COST_KEY].toDouble(); - - qDebug() << "PV: " << perVoxelCredits << "PMC: " << perMeterCubedCredits; - } else { - qDebug() << "CALLED WITH EMPTY SETTINGS!"; - _satoshisPerVoxel = 0; - _satoshisPerMeterCubed = 0; - } -} - qint64 VoxelEditPacketSender::satoshiCostForMessage(PacketType type, int numberOfDetails, VoxelDetail *details) { return 0; } diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h index 33382d7faf..4fb8f718cc 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -52,18 +52,5 @@ public: virtual char getMyNodeType() const { return NodeType::VoxelServer; } qint64 satoshiCostForMessage(PacketType type, int numberOfDetails, VoxelDetail* details); - - void setSatoshisPerVoxel(qint64 satoshisPerVoxel) { _satoshisPerVoxel = satoshisPerVoxel; } - qint64 getSatoshisPerVoxel() const { return _satoshisPerVoxel; } - - void setSatoshisPerMeterCubed(qint64 satoshisPerMeterCubed) { _satoshisPerMeterCubed = satoshisPerMeterCubed; } - qint64 getSatoshisPerMeterCubed() const { return _satoshisPerMeterCubed; } - -public slots: - void updateVoxelCosts(const QJsonObject& domainSettingsObject); - -private: - qint64 _satoshisPerVoxel; - qint64 _satoshisPerMeterCubed; }; #endif // hifi_VoxelEditPacketSender_h From 6a534a6ff1911df5e3ae495e914c2e4a639d3149 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Jul 2014 17:31:12 -0700 Subject: [PATCH 28/56] send along satoshi costs for voxel additions from VoxelEditPacketSender --- .../octree/src/OctreeEditPacketSender.cpp | 2 +- libraries/octree/src/OctreeEditPacketSender.h | 2 +- .../voxels/src/VoxelEditPacketSender.cpp | 19 ++++++++++++++----- libraries/voxels/src/VoxelEditPacketSender.h | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 5f6fb1274b..1d0c40d659 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -177,7 +177,7 @@ void OctreeEditPacketSender::queuePendingPacketToNodes(PacketType type, unsigned } } -void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t length) { +void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t length, qint64 satoshiCost) { if (!_shouldSend) { return; // bail early } diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index 584f74fd39..ad2526b866 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -102,7 +102,7 @@ protected: bool _shouldSend; void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0); void queuePendingPacketToNodes(PacketType type, unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0); - void queuePacketToNodes(unsigned char* buffer, ssize_t length); + void queuePacketToNodes(unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0); void initializePacket(EditPacketBuffer& packetBuffer, PacketType type); void releaseQueuedPacket(EditPacketBuffer& packetBuffer); // releases specific queued packet diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index 40a0f853f6..0913b10272 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -117,9 +117,9 @@ void VoxelEditPacketSender::sendVoxelEditMessage(PacketType type, const VoxelDet // If we don't have voxel jurisdictions, then we will simply queue up these packets and wait till we have // jurisdictions for processing if (!voxelServersExist()) { - queuePendingPacketToNodes(type, bufferOut, sizeOut); + queuePendingPacketToNodes(type, bufferOut, sizeOut, satoshiCostForMessage(detail)); } else { - queuePacketToNodes(bufferOut, sizeOut); + queuePacketToNodes(bufferOut, sizeOut, satoshiCostForMessage(detail)); } // either way, clean up the created buffer @@ -138,11 +138,20 @@ void VoxelEditPacketSender::queueVoxelEditMessages(PacketType type, int numberOf int sizeOut = 0; if (encodeVoxelEditMessageDetails(type, 1, &details[i], &bufferOut[0], _maxPacketSize, sizeOut)) { - queueOctreeEditMessage(type, bufferOut, sizeOut); + queueOctreeEditMessage(type, bufferOut, sizeOut, satoshiCostForMessage(details[i])); } } } -qint64 VoxelEditPacketSender::satoshiCostForMessage(PacketType type, int numberOfDetails, VoxelDetail *details) { - return 0; +qint64 VoxelEditPacketSender::satoshiCostForMessage(const VoxelDetail& details) { + const DomainHandler& domainHandler = NodeList::getInstance()->getDomainHandler(); + + qint64 totalSatoshiCost = domainHandler.getSatoshisPerVoxel(); + + qint64 costPerMeterCubed = domainHandler.getSatoshisPerMeterCubed(); + float totalVolume = details.s * details.s * details.s; + + totalSatoshiCost += floorf(totalVolume * costPerMeterCubed); + + return costPerMeterCubed; } diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h index 4fb8f718cc..e761812629 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -51,6 +51,6 @@ public: // My server type is the voxel server virtual char getMyNodeType() const { return NodeType::VoxelServer; } - qint64 satoshiCostForMessage(PacketType type, int numberOfDetails, VoxelDetail* details); + qint64 satoshiCostForMessage(const VoxelDetail& details); }; #endif // hifi_VoxelEditPacketSender_h From 1c6834cdd6c93b19d8f676c26d8e3e9a13cbc624 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 29 Jul 2014 17:49:33 -0700 Subject: [PATCH 29/56] Improved the API and cleaned up the code --- ...Animation.js => proceduralAnimationAPI.js} | 16 +- examples/proceduralBot.js | 198 ++++-------------- 2 files changed, 44 insertions(+), 170 deletions(-) rename examples/{proceduralAnimation.js => proceduralAnimationAPI.js} (91%) diff --git a/examples/proceduralAnimation.js b/examples/proceduralAnimationAPI.js similarity index 91% rename from examples/proceduralAnimation.js rename to examples/proceduralAnimationAPI.js index 7eb0873994..ae6b5c3d31 100644 --- a/examples/proceduralAnimation.js +++ b/examples/proceduralAnimationAPI.js @@ -10,6 +10,8 @@ // Script.include("proceduralAnimation.js"); // You can see a usage example in proceduralBot.js +// The current implementation is quite simple. If you would like a feature +// to be added or expanded, you can contact Ben at brb555@vols.utk.edu ProcAnimAPI = function() { @@ -62,8 +64,8 @@ ProcAnimAPI = function() { middleQuats[i][j] = Quat.fromPitchYawRollDegrees(middleAngles[i][j][0], middleAngles[i][j][1], middleAngles[i][j][2]); } } - finalKeyFrames[0] = new KeyFrame(rightQuats[0], leftQuats[0], middleQuats[0]); - finalKeyFrames[1] = new KeyFrame(rightQuats[1], leftQuats[1], middleQuats[1]); + finalKeyFrames[0] = new this.KeyFrame(rightQuats[0], leftQuats[0], middleQuats[0]); + finalKeyFrames[1] = new this.KeyFrame(rightQuats[1], leftQuats[1], middleQuats[1]); //Generate mirrored quaternions for the other half of the animation for (var i = 0; i < rightAngles.length; i++) { @@ -77,8 +79,8 @@ ProcAnimAPI = function() { middleQuats[i][j] = Quat.fromPitchYawRollDegrees(-middleAngles[i][j][0], -middleAngles[i][j][1], -middleAngles[i][j][2]); } } - finalKeyFrames[2] = new KeyFrame(leftQuats[0], rightQuats[0], middleQuats[0]); - finalKeyFrames[3] = new KeyFrame(leftQuats[1], rightQuats[1], middleQuats[1]); + finalKeyFrames[2] = new this.KeyFrame(leftQuats[0], rightQuats[0], middleQuats[0]); + finalKeyFrames[3] = new this.KeyFrame(leftQuats[1], rightQuats[1], middleQuats[1]); //Hook up pointers to the next keyframe for (var i = 0; i < finalKeyFrames.length - 1; i++) { @@ -106,7 +108,7 @@ ProcAnimAPI = function() { } return finalKeyFrames; - } + }; // Animation KeyFrame constructor. rightJoints and leftJoints must be the same size this.KeyFrame = function(rightJoints, leftJoints, middleJoints) { @@ -119,7 +121,7 @@ ProcAnimAPI = function() { for (var i = 0; i < middleJoints.length; i++) { this.rotations[this.rotations.length] = middleJoints[i]; } - } + }; // DeCasteljau evaluation to evaluate the bezier curve. // This is a very natural looking interpolation @@ -130,5 +132,5 @@ ProcAnimAPI = function() { var d = Quat.slerp(a, b, f); var e = Quat.slerp(b, c, f); return Quat.slerp(d, e, f); - } + }; } \ No newline at end of file diff --git a/examples/proceduralBot.js b/examples/proceduralBot.js index ce88f596b6..05838da095 100644 --- a/examples/proceduralBot.js +++ b/examples/proceduralBot.js @@ -11,32 +11,30 @@ // //For procedural walk animation -Script.include("proceduralAnimation.js"); +Script.include("http://s3-us-west-1.amazonaws.com/highfidelity-public/scripts/proceduralAnimationAPI.js"); + +var procAnimAPI = new ProcAnimAPI(); function getRandomFloat(min, max) { -return Math.random() * (max - min) + min; + return Math.random() * (max - min) + min; } function getRandomInt (min, max) { -return Math.floor(Math.random() * (max - min + 1)) + min; + return Math.floor(Math.random() * (max - min + 1)) + min; } function printVector(string, vector) { -print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); + print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); } var CHANCE_OF_MOVING = 0.1; var CHANCE_OF_SOUND = 0; var CHANCE_OF_HEAD_TURNING = 0.05; var CHANCE_OF_BIG_MOVE = 0.1; -var CHANCE_OF_WAVING = 0.009; var isMoving = false; var isTurningHead = false; var isPlayingAudio = false; -var isWaving = false; -var waveFrequency = 0.0; -var waveAmplitude = 0.0; var X_MIN = 0.50; var X_MAX = 15.60; @@ -141,32 +139,13 @@ function playRandomSound() { } } -//Animation KeyFrame constructor. rightJoints and leftJoints must be the same size -function WalkKeyFrame(rightJoints, leftJoints, singleJoints) { - this.rotations = []; - - for (var i = 0; i < rightJoints.length; i++) { - this.rotations[this.rotations.length] = rightJoints[i]; - this.rotations[this.rotations.length] = leftJoints[i]; - } - for (var i = 0; i < singleJoints.length; i++) { - this.rotations[this.rotations.length] = singleJoints[i]; - } -} - //Procedural walk animation using two keyframes //We use a separate array for front and back joints -var frontKeyFrames = []; -var backKeyFrames = []; -//for non mirrored joints such as the spine -var singleKeyFrames = []; //Pitch, yaw, and roll for the joints -var frontAngles = []; -var backAngles = []; +var rightAngles = []; +var leftAngles = []; //for non mirrored joints such as the spine -var singleAngles = []; - - +var middleAngles = []; //Actual joint mappings var SHOULDER_JOINT_NUMBER = 15; @@ -185,12 +164,9 @@ var JOINT_SPINE = 11; var NUM_FRAMES = 2; for (var i = 0; i < NUM_FRAMES; i++) { - frontAngles[i] = []; - backAngles[i] = []; - singleAngles[i] = []; - frontKeyFrames[i] = []; - backKeyFrames[i] = []; - singleKeyFrames[i] = []; + rightAngles[i] = []; + leftAngles[i] = []; + middleAngles[i] = []; } //Joint order for actual joint mappings, should be interleaved R,L,R,L,...S,S,S for R = right, L = left, S = single var JOINT_ORDER = [JOINT_R_HIP, JOINT_L_HIP, JOINT_R_KNEE, JOINT_L_KNEE, JOINT_R_ARM, JOINT_L_ARM, JOINT_R_FOREARM, JOINT_L_FOREARM, JOINT_SPINE]; @@ -206,101 +182,37 @@ var SPINE = 0; //We have to store the angles so we can invert yaw and roll when making the animation //symmetrical - //Front refers to leg, not arm. //Legs Extending -frontAngles[0][HIP] = [30.0, 0.0, 8.0]; -frontAngles[0][KNEE] = [-15.0, 0.0, 0.0]; -frontAngles[0][ARM] = [85.0, -25.0, 0.0]; -frontAngles[0][FOREARM] = [0.0, 0.0, -15.0]; +rightAngles[0][HIP] = [30.0, 0.0, 8.0]; +rightAngles[0][KNEE] = [-15.0, 0.0, 0.0]; +rightAngles[0][ARM] = [85.0, -25.0, 0.0]; +rightAngles[0][FOREARM] = [0.0, 0.0, -15.0]; -backAngles[0][HIP] = [-15, 0.0, 8.0]; -backAngles[0][KNEE] = [-28, 0.0, 0.0]; -backAngles[0][ARM] = [85.0, 20.0, 0.0]; -backAngles[0][FOREARM] = [10.0, 0.0, -25.0]; +leftAngles[0][HIP] = [-15, 0.0, 8.0]; +leftAngles[0][KNEE] = [-28, 0.0, 0.0]; +leftAngles[0][ARM] = [85.0, 20.0, 0.0]; +leftAngles[0][FOREARM] = [10.0, 0.0, -25.0]; -singleAngles[0][SPINE] = [0.0, -15.0, 5.0]; +middleAngles[0][SPINE] = [0.0, -15.0, 5.0]; //Legs Passing -frontAngles[1][HIP] = [6.0, 0.0, 8.0]; -frontAngles[1][KNEE] = [-12.0, 0.0, 0.0]; -frontAngles[1][ARM] = [85.0, 0.0, 0.0]; -frontAngles[1][FOREARM] = [0.0, 0.0, -15.0]; +rightAngles[1][HIP] = [6.0, 0.0, 8.0]; +rightAngles[1][KNEE] = [-12.0, 0.0, 0.0]; +rightAngles[1][ARM] = [85.0, 0.0, 0.0]; +rightAngles[1][FOREARM] = [0.0, 0.0, -15.0]; -backAngles[1][HIP] = [10.0, 0.0, 8.0]; -backAngles[1][KNEE] = [-55.0, 0.0, 0.0]; -backAngles[1][ARM] = [85.0, 0.0, 0.0]; -backAngles[1][FOREARM] = [0.0, 0.0, -15.0]; +leftAngles[1][HIP] = [10.0, 0.0, 8.0]; +leftAngles[1][KNEE] = [-55.0, 0.0, 0.0]; +leftAngles[1][ARM] = [85.0, 0.0, 0.0]; +leftAngles[1][FOREARM] = [0.0, 0.0, -15.0]; -singleAngles[1][SPINE] = [0.0, 0.0, 0.0]; +middleAngles[1][SPINE] = [0.0, 0.0, 0.0]; // ******************************* Animation Is Defined Above ************************************* //Actual keyframes for the animation -var walkKeyFrames = []; -//Generate quaternions from the angles -for (var i = 0; i < frontAngles.length; i++) { - for (var j = 0; j < frontAngles[i].length; j++) { - frontKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(frontAngles[i][j][0], frontAngles[i][j][1], frontAngles[i][j][2]); - backKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(backAngles[i][j][0], -backAngles[i][j][1], -backAngles[i][j][2]); - } -} -for (var i = 0; i < singleAngles.length; i++) { - for (var j = 0; j < singleAngles[i].length; j++) { - singleKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(singleAngles[i][j][0], singleAngles[i][j][1], singleAngles[i][j][2]); - } -} -walkKeyFrames[0] = new WalkKeyFrame(frontKeyFrames[0], backKeyFrames[0], singleKeyFrames[0]); -walkKeyFrames[1] = new WalkKeyFrame(frontKeyFrames[1], backKeyFrames[1], singleKeyFrames[1]); - -//Generate mirrored quaternions for the other half of the body -for (var i = 0; i < frontAngles.length; i++) { - for (var j = 0; j < frontAngles[i].length; j++) { - frontKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(frontAngles[i][j][0], -frontAngles[i][j][1], -frontAngles[i][j][2]); - backKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(backAngles[i][j][0], backAngles[i][j][1], backAngles[i][j][2]); - } -} -for (var i = 0; i < singleAngles.length; i++) { - for (var j = 0; j < singleAngles[i].length; j++) { - singleKeyFrames[i][j] = Quat.fromPitchYawRollDegrees(-singleAngles[i][j][0], -singleAngles[i][j][1], -singleAngles[i][j][2]); - } -} -walkKeyFrames[2] = new WalkKeyFrame(backKeyFrames[0], frontKeyFrames[0], singleKeyFrames[0]); -walkKeyFrames[3] = new WalkKeyFrame(backKeyFrames[1], frontKeyFrames[1], singleKeyFrames[1]); - -//Hook up pointers to the next keyframe -for (var i = 0; i < walkKeyFrames.length - 1; i++) { - walkKeyFrames[i].nextFrame = walkKeyFrames[i+1]; -} -walkKeyFrames[walkKeyFrames.length-1].nextFrame = walkKeyFrames[0]; - -//Set up the bezier curve control points using technique described at -//https://www.cs.tcd.ie/publications/tech-reports/reports.94/TCD-CS-94-18.pdf -//Set up all C1 -for (var i = 0; i < walkKeyFrames.length; i++) { - walkKeyFrames[i].nextFrame.controlPoints = []; - for (var j = 0; j < walkKeyFrames[i].rotations.length; j++) { - walkKeyFrames[i].nextFrame.controlPoints[j] = []; - var R = Quat.slerp(walkKeyFrames[i].rotations[j], walkKeyFrames[i].nextFrame.rotations[j], 2.0); - var T = Quat.slerp(R, walkKeyFrames[i].nextFrame.nextFrame.rotations[j], 0.5); - walkKeyFrames[i].nextFrame.controlPoints[j][0] = Quat.slerp(walkKeyFrames[i].nextFrame.rotations[j], T, 0.33333); - } -} -//Set up all C2 -for (var i = 0; i < walkKeyFrames.length; i++) { - for (var j = 0; j < walkKeyFrames[i].rotations.length; j++) { - walkKeyFrames[i].controlPoints[j][1] = Quat.slerp(walkKeyFrames[i].nextFrame.rotations[j], walkKeyFrames[i].nextFrame.controlPoints[j][0], -1.0); - } -} -//DeCasteljau evaluation to evaluate the bezier curve -function deCasteljau(k1, k2, c1, c2, f) { - var a = Quat.slerp(k1, c1, f); - var b = Quat.slerp(c1, c2, f); - var c = Quat.slerp(c2, k2, f); - var d = Quat.slerp(a, b, f); - var e = Quat.slerp(b, c, f); - return Quat.slerp(d, e, f); -} +var walkKeyFrames = procAnimAPI.generateKeyframes(rightAngles, leftAngles, middleAngles, NUM_FRAMES); var currentFrame = 0; @@ -314,7 +226,7 @@ var avatarVelocity = 0.0; var avatarMaxVelocity = 1.4; function keepWalking(deltaTime) { - + walkTime += avatarVelocity * deltaTime; if (walkTime > walkWheelRate) { walkTime = 0.0; @@ -329,17 +241,10 @@ function keepWalking(deltaTime) { var interp = walkTime / walkWheelRate; for (var i = 0; i < JOINT_ORDER.length; i++) { - Avatar.setJointData(JOINT_ORDER[i], deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], interp)); + Avatar.setJointData(JOINT_ORDER[i], procAnimAPI.deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], interp)); } } -var trailingAverageLoudness = 0; -var MAX_SAMPLE = 32767; -var DB_METER_BASE = Math.log(MAX_SAMPLE); - -var RAND_RATIO_LAST = getRandomFloat(0.1, 0.3); -var RAND_TRAILING = 1 - RAND_RATIO_LAST; - function jumpWithLoudness(deltaTime) { // potentially change pelvis height depending on trailing average loudness @@ -362,18 +267,9 @@ function jumpWithLoudness(deltaTime) { Avatar.position = pelvisPosition; } -var jointMapping = null; -var frameIndex = 0.0; -var isPlayingDanceAnimation = false; -var randomAnimation = null; -var animationLoops = 1; var forcedMove = false; -var FRAME_RATE = 30.0; - var wasMovingLastFrame = false; -var wasDancing = false; - function handleHeadTurn() { if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) { @@ -387,16 +283,6 @@ function handleHeadTurn() { } } -var currentShoulderQuat = Avatar.getJointRotation(SHOULDER_JOINT_NUMBER); -var targetShoulderQuat = currentShoulderQuat; -var idleShoulderQuat = currentShoulderQuat; -var currentSpineQuat = Avatar.getJointRotation(JOINT_SPINE); -var targetSpineQuat = currentSpineQuat; -var idleSpineQuat = currentSpineQuat; -var currentElbowQuat = Avatar.getJointRotation(ELBOW_JOINT_NUMBER); -var targetElbowQuat = currentElbowQuat; -var idleElbowQuat = currentElbowQuat; - function stopWalking() { Avatar.clearJointData(JOINT_R_HIP); Avatar.clearJointData(JOINT_R_KNEE); @@ -421,7 +307,7 @@ function handleWalking(deltaTime) { targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL))); } while ((targetPosition.x < X_MIN || targetPosition.x > X_MAX || targetPosition.z < Z_MIN || targetPosition.z > Z_MAX) - && attempts < MAX_ATEMPTS); + && attempts < MAX_ATTEMPTS); targetPosition.x = clamp(targetPosition.x, X_MIN, X_MAX); targetPosition.z = clamp(targetPosition.z, Z_MIN, Z_MAX); @@ -473,31 +359,17 @@ function changePelvisHeight(newHeight) { Avatar.position = newPosition; } -function possiblyStopDancing() { - if (wasDancing) { - for (var j = 0; j < Avatar.jointNames.length; j++) { - Avatar.clearJointData(j); - } - - changePelvisHeight(Y_PELVIS); - } -} - function updateBehavior(deltaTime) { cumulativeTime += deltaTime; if (AvatarList.containsAvatarWithDisplayName("mrdj")) { - if (wasMovingLastFrame && !wasDancing) { + if (wasMovingLastFrame) { isMoving = false; } // we have a DJ, shouldn't we be dancing? jumpWithLoudness(deltaTime); } else { - // make sure we're not dancing anymore - possiblyStopDancing(); - - wasDancing = false; // no DJ, let's just chill on the dancefloor - randomly walking and talking handleHeadTurn(); From f944c27cafac1a5b434fa4c07d32defecd7afb73 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 09:24:31 -0700 Subject: [PATCH 30/56] don't calculate satoshi cost if the costs are non-existent --- libraries/voxels/src/VoxelEditPacketSender.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index 0913b10272..87bba950c7 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -149,9 +149,12 @@ qint64 VoxelEditPacketSender::satoshiCostForMessage(const VoxelDetail& details) qint64 totalSatoshiCost = domainHandler.getSatoshisPerVoxel(); qint64 costPerMeterCubed = domainHandler.getSatoshisPerMeterCubed(); - float totalVolume = details.s * details.s * details.s; - totalSatoshiCost += floorf(totalVolume * costPerMeterCubed); - - return costPerMeterCubed; + if (totalSatoshiCost == 0 && costPerMeterCubed == 0) { + float totalVolume = details.s * details.s * details.s; + + return totalSatoshiCost + floorf(totalVolume * costPerMeterCubed); + } else { + return 0; + } } From 2c8808e922ec88ec24aff1da5eda4314184552a9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 09:45:33 -0700 Subject: [PATCH 31/56] repairs to cost calculations for voxels --- libraries/networking/src/DomainHandler.cpp | 3 ++- libraries/voxels/src/VoxelEditPacketSender.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index a50f735542..5f20bebd50 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -171,7 +171,8 @@ void DomainHandler::settingsRequestFinished() { qDebug() << "Error getting domain settings -" << settingsReply->errorString() << "- retrying"; if (++_failedSettingsRequests >= MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS) { - qDebug() << "Failed to retreive domain-server settings" << MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS << "times. Re-setting connection to domain."; + qDebug() << "Failed to retreive domain-server settings" << MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS + << "times. Re-setting connection to domain."; clearSettings(); clearConnectionInfo(); emit settingsReceiveFail(); diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index 87bba950c7..ee189f492d 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -147,14 +147,14 @@ qint64 VoxelEditPacketSender::satoshiCostForMessage(const VoxelDetail& details) const DomainHandler& domainHandler = NodeList::getInstance()->getDomainHandler(); qint64 totalSatoshiCost = domainHandler.getSatoshisPerVoxel(); - qint64 costPerMeterCubed = domainHandler.getSatoshisPerMeterCubed(); if (totalSatoshiCost == 0 && costPerMeterCubed == 0) { - float totalVolume = details.s * details.s * details.s; - - return totalSatoshiCost + floorf(totalVolume * costPerMeterCubed); - } else { return 0; + } else { + float meterScale = details.s * TREE_SCALE; + float totalVolume = meterScale * meterScale * meterScale; + + return totalSatoshiCost + (qint64) floorf(totalVolume * costPerMeterCubed); } } From 66592466adfc6b885aac13bbca4add57f6c383dc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 10:05:21 -0700 Subject: [PATCH 32/56] pipe satoshi cost down to queuePacketToNodes --- libraries/octree/src/EditPacketBuffer.cpp | 30 ++++++++++++++++ libraries/octree/src/EditPacketBuffer.h | 34 +++++++++++++++++++ .../octree/src/OctreeEditPacketSender.cpp | 18 ++++------ libraries/octree/src/OctreeEditPacketSender.h | 13 ++----- 4 files changed, 72 insertions(+), 23 deletions(-) create mode 100644 libraries/octree/src/EditPacketBuffer.cpp create mode 100644 libraries/octree/src/EditPacketBuffer.h diff --git a/libraries/octree/src/EditPacketBuffer.cpp b/libraries/octree/src/EditPacketBuffer.cpp new file mode 100644 index 0000000000..d6e7bf0183 --- /dev/null +++ b/libraries/octree/src/EditPacketBuffer.cpp @@ -0,0 +1,30 @@ +// +// EditPacketBuffer.cpp +// libraries/octree/src +// +// Created by Stephen Birarda on 2014-07-30. +// 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 "EditPacketBuffer.h" + +EditPacketBuffer::EditPacketBuffer() : + _nodeUUID(), + _currentType(PacketTypeUnknown), + _currentSize(0), + _satoshiCost(0) +{ + +} + +EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, ssize_t length, qint64 satoshiCost, QUuid nodeUUID) : + _nodeUUID(nodeUUID), + _currentType(type), + _currentSize(length), + _satoshiCost(satoshiCost) +{ + memcpy(_currentBuffer, buffer, length); +}; \ No newline at end of file diff --git a/libraries/octree/src/EditPacketBuffer.h b/libraries/octree/src/EditPacketBuffer.h new file mode 100644 index 0000000000..9b52cbc5e4 --- /dev/null +++ b/libraries/octree/src/EditPacketBuffer.h @@ -0,0 +1,34 @@ +// +// EditPacketBuffer.h +// libraries/octree/src +// +// Created by Stephen Birarda on 2014-07-30. +// 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_EditPacketBuffer_h +#define hifi_EditPacketBuffer_h + +#include + +#include +#include + +/// Used for construction of edit packets +class EditPacketBuffer { +public: + EditPacketBuffer(); + EditPacketBuffer(PacketType type, unsigned char* codeColorBuffer, ssize_t length, + qint64 satoshiCost = 0, const QUuid nodeUUID = QUuid()); + + QUuid _nodeUUID; + PacketType _currentType; + unsigned char _currentBuffer[MAX_PACKET_SIZE]; + ssize_t _currentSize; + qint64 _satoshiCost; +}; + +#endif // hifi_EditPacketBuffer_h \ No newline at end of file diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 1d0c40d659..b9891f0628 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -17,14 +17,6 @@ #include #include "OctreeEditPacketSender.h" -EditPacketBuffer::EditPacketBuffer(PacketType type, unsigned char* buffer, ssize_t length, QUuid nodeUUID) : - _nodeUUID(nodeUUID), - _currentType(type), - _currentSize(length) -{ - memcpy(_currentBuffer, buffer, length); -}; - const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::DEFAULT_PACKETS_PER_SECOND; @@ -136,7 +128,7 @@ void OctreeEditPacketSender::processPreServerExistsPackets() { _pendingPacketsLock.lock(); while (!_preServerSingleMessagePackets.empty()) { EditPacketBuffer* packet = _preServerSingleMessagePackets.front(); - queuePacketToNodes(&packet->_currentBuffer[0], packet->_currentSize); + queuePacketToNodes(&packet->_currentBuffer[0], packet->_currentSize, packet->_satoshiCost); delete packet; _preServerSingleMessagePackets.erase(_preServerSingleMessagePackets.begin()); } @@ -163,7 +155,7 @@ void OctreeEditPacketSender::queuePendingPacketToNodes(PacketType type, unsigned // If we're asked to save messages while waiting for voxel servers to arrive, then do so... if (_maxPendingMessages > 0) { - EditPacketBuffer* packet = new EditPacketBuffer(type, buffer, length); + EditPacketBuffer* packet = new EditPacketBuffer(type, buffer, length, satoshiCost); _pendingPacketsLock.lock(); _preServerSingleMessagePackets.push_back(packet); // if we've saved MORE than our max, then clear out the oldest packet... @@ -204,7 +196,7 @@ void OctreeEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t l isMyJurisdiction = (map.isMyJurisdiction(octCode, CHECK_NODE_ONLY) == JurisdictionMap::WITHIN); _serverJurisdictions->unlock(); if (isMyJurisdiction) { - queuePacketToNode(nodeUUID, buffer, length); + queuePacketToNode(nodeUUID, buffer, length, satoshiCost); } } } @@ -288,6 +280,7 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, unsigned ch memcpy(&packetBuffer._currentBuffer[packetBuffer._currentSize], codeColorBuffer, length); packetBuffer._currentSize += length; + packetBuffer._satoshiCost += satoshiCost; } } } @@ -309,7 +302,8 @@ void OctreeEditPacketSender::releaseQueuedMessages() { void OctreeEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer) { _releaseQueuedPacketMutex.lock(); if (packetBuffer._currentSize > 0 && packetBuffer._currentType != PacketTypeUnknown) { - queuePacketToNode(packetBuffer._nodeUUID, &packetBuffer._currentBuffer[0], packetBuffer._currentSize); + queuePacketToNode(packetBuffer._nodeUUID, &packetBuffer._currentBuffer[0], + packetBuffer._currentSize, packetBuffer._satoshiCost); packetBuffer._currentSize = 0; packetBuffer._currentType = PacketTypeUnknown; } diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index ad2526b866..d11aa55963 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -15,20 +15,11 @@ #include #include #include + +#include "EditPacketBuffer.h" #include "JurisdictionMap.h" #include "SentPacketHistory.h" -/// Used for construction of edit packets -class EditPacketBuffer { -public: - EditPacketBuffer() : _nodeUUID(), _currentType(PacketTypeUnknown), _currentSize(0) { } - EditPacketBuffer(PacketType type, unsigned char* codeColorBuffer, ssize_t length, const QUuid nodeUUID = QUuid()); - QUuid _nodeUUID; - PacketType _currentType; - unsigned char _currentBuffer[MAX_PACKET_SIZE]; - ssize_t _currentSize; -}; - /// Utility for processing, packing, queueing and sending of outbound edit messages. class OctreeEditPacketSender : public PacketSender { Q_OBJECT From 7a128e5f779dbde7f690fb48dd018b39d16bd673 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 10:14:51 -0700 Subject: [PATCH 33/56] add satoshi cost to voxel packet debug --- libraries/octree/src/OctreeEditPacketSender.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index b9891f0628..8c9bee2be0 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -94,7 +94,7 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c unsigned char* sequenceAt = buffer + numBytesPacketHeader; quint16 sequence = _outgoingSequenceNumbers[nodeUUID]++; memcpy(sequenceAt, &sequence, sizeof(quint16)); - + // send packet QByteArray packet(reinterpret_cast(buffer), length); queuePacketForSending(node, packet); @@ -103,7 +103,7 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c _sentPacketHistories[nodeUUID].packetSent(sequence, packet); // debugging output... - bool wantDebugging = false; + bool wantDebugging = true; if (wantDebugging) { int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast(buffer)); unsigned short int sequence = (*((unsigned short int*)(buffer + numBytesPacketHeader))); @@ -113,6 +113,7 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c qDebug() << "OctreeEditPacketSender::queuePacketToNode() queued " << buffer[0] << " - command to node bytes=" << length << + " satoshiCost=" << satoshiCost << " sequence=" << sequence << " transitTimeSoFar=" << transitTime << " usecs"; } From 303f4bf4f1f106f704819525a81f3608de876b5b Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 30 Jul 2014 12:08:07 -0700 Subject: [PATCH 34/56] Improved API, added footstep sounds --- examples/proceduralAnimationAPI.js | 54 +++--- examples/proceduralBot.js | 264 ++++++++++++++++------------- 2 files changed, 176 insertions(+), 142 deletions(-) diff --git a/examples/proceduralAnimationAPI.js b/examples/proceduralAnimationAPI.js index ae6b5c3d31..61397ec180 100644 --- a/examples/proceduralAnimationAPI.js +++ b/examples/proceduralAnimationAPI.js @@ -64,8 +64,10 @@ ProcAnimAPI = function() { middleQuats[i][j] = Quat.fromPitchYawRollDegrees(middleAngles[i][j][0], middleAngles[i][j][1], middleAngles[i][j][2]); } } - finalKeyFrames[0] = new this.KeyFrame(rightQuats[0], leftQuats[0], middleQuats[0]); - finalKeyFrames[1] = new this.KeyFrame(rightQuats[1], leftQuats[1], middleQuats[1]); + + for (var i = 0; i < numFrames; i++) { + finalKeyFrames[i] = new this.KeyFrame(rightQuats[i], leftQuats[i], middleQuats[i]); + } //Generate mirrored quaternions for the other half of the animation for (var i = 0; i < rightAngles.length; i++) { @@ -79,35 +81,41 @@ ProcAnimAPI = function() { middleQuats[i][j] = Quat.fromPitchYawRollDegrees(-middleAngles[i][j][0], -middleAngles[i][j][1], -middleAngles[i][j][2]); } } - finalKeyFrames[2] = new this.KeyFrame(leftQuats[0], rightQuats[0], middleQuats[0]); - finalKeyFrames[3] = new this.KeyFrame(leftQuats[1], rightQuats[1], middleQuats[1]); - - //Hook up pointers to the next keyframe - for (var i = 0; i < finalKeyFrames.length - 1; i++) { - finalKeyFrames[i].nextFrame = finalKeyFrames[i+1]; + for (var i = 0; i < numFrames; i++) { + finalKeyFrames[numFrames + i] = new this.KeyFrame(leftQuats[i], rightQuats[i], middleQuats[i]); } - finalKeyFrames[finalKeyFrames.length-1].nextFrame = finalKeyFrames[0]; + + //Generate control points + this.computeBezierControlPoints(finalKeyFrames); + + return finalKeyFrames; + }; + + //Computes 2 controlPoints to each keyframe to be used in the bezier evaluation. + //Technique is described at: //https://www.cs.tcd.ie/publications/tech-reports/reports.94/TCD-CS-94-18.pdf + this.computeBezierControlPoints = function(keyFrames) { + //Hook up pointers to the next keyframe + for (var i = 0; i < keyFrames.length - 1; i++) { + keyFrames[i].nextFrame = keyFrames[i+1]; + } + keyFrames[keyFrames.length-1].nextFrame = keyFrames[0]; - //Set up the bezier curve control points using technique described at - //https://www.cs.tcd.ie/publications/tech-reports/reports.94/TCD-CS-94-18.pdf //Set up all C1 - for (var i = 0; i < finalKeyFrames.length; i++) { - finalKeyFrames[i].nextFrame.controlPoints = []; - for (var j = 0; j < finalKeyFrames[i].rotations.length; j++) { - finalKeyFrames[i].nextFrame.controlPoints[j] = []; - var R = Quat.slerp(finalKeyFrames[i].rotations[j], finalKeyFrames[i].nextFrame.rotations[j], 2.0); - var T = Quat.slerp(R, finalKeyFrames[i].nextFrame.nextFrame.rotations[j], 0.5); - finalKeyFrames[i].nextFrame.controlPoints[j][0] = Quat.slerp(finalKeyFrames[i].nextFrame.rotations[j], T, 0.33333); + for (var i = 0; i < keyFrames.length; i++) { + keyFrames[i].nextFrame.controlPoints = []; + for (var j = 0; j < keyFrames[i].rotations.length; j++) { + keyFrames[i].nextFrame.controlPoints[j] = []; + var R = Quat.slerp(keyFrames[i].rotations[j], keyFrames[i].nextFrame.rotations[j], 2.0); + var T = Quat.slerp(R, keyFrames[i].nextFrame.nextFrame.rotations[j], 0.5); + keyFrames[i].nextFrame.controlPoints[j][0] = Quat.slerp(keyFrames[i].nextFrame.rotations[j], T, 0.33333); } } //Set up all C2 - for (var i = 0; i < finalKeyFrames.length; i++) { - for (var j = 0; j < finalKeyFrames[i].rotations.length; j++) { - finalKeyFrames[i].controlPoints[j][1] = Quat.slerp(finalKeyFrames[i].nextFrame.rotations[j], finalKeyFrames[i].nextFrame.controlPoints[j][0], -1.0); + for (var i = 0; i < keyFrames.length; i++) { + for (var j = 0; j < keyFrames[i].rotations.length; j++) { + keyFrames[i].controlPoints[j][1] = Quat.slerp(keyFrames[i].nextFrame.rotations[j], keyFrames[i].nextFrame.controlPoints[j][0], -1.0); } } - - return finalKeyFrames; }; // Animation KeyFrame constructor. rightJoints and leftJoints must be the same size diff --git a/examples/proceduralBot.js b/examples/proceduralBot.js index 05838da095..d8218c8341 100644 --- a/examples/proceduralBot.js +++ b/examples/proceduralBot.js @@ -27,7 +27,7 @@ function printVector(string, vector) { print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); } -var CHANCE_OF_MOVING = 0.1; +var CHANCE_OF_MOVING = 0.01; var CHANCE_OF_SOUND = 0; var CHANCE_OF_HEAD_TURNING = 0.05; var CHANCE_OF_BIG_MOVE = 0.1; @@ -60,8 +60,6 @@ var targetOrientation = { x: 0, y: 0, z: 0, w: 0 }; var currentOrientation = { x: 0, y: 0, z: 0, w: 0 }; var targetHeadPitch = 0.0; -var cumulativeTime = 0.0; - var basePelvisHeight = 0.0; var pelvisOscillatorPosition = 0.0; var pelvisOscillatorVelocity = 0.0; @@ -209,11 +207,34 @@ leftAngles[1][FOREARM] = [0.0, 0.0, -15.0]; middleAngles[1][SPINE] = [0.0, 0.0, 0.0]; -// ******************************* Animation Is Defined Above ************************************* - //Actual keyframes for the animation var walkKeyFrames = procAnimAPI.generateKeyframes(rightAngles, leftAngles, middleAngles, NUM_FRAMES); +// ******************************* Animation Is Defined Above ************************************* + +// ********************************** Standing Key Frame ****************************************** +//We don't have to do any mirroring or anything, since this is just a single pose. +var rightQuats = []; +var leftQuats = []; +var middleQuats = []; + +rightQuats[HIP] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 7.0); +rightQuats[KNEE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); +rightQuats[ARM] = Quat.fromPitchYawRollDegrees(85.0, 0.0, 0.0); +rightQuats[FOREARM] = Quat.fromPitchYawRollDegrees(0.0, 0.0, -10.0); + +leftQuats[HIP] = Quat.fromPitchYawRollDegrees(0, 0.0, -7.0); +leftQuats[KNEE] = Quat.fromPitchYawRollDegrees(0, 0.0, 0.0); +leftQuats[ARM] = Quat.fromPitchYawRollDegrees(85.0, 0.0, 0.0); +leftQuats[FOREARM] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 10.0); + +middleQuats[SPINE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); + +var standingKeyFrame = new procAnimAPI.KeyFrame(rightQuats, leftQuats, middleQuats); + +// ************************************************************************************************ + + var currentFrame = 0; var walkTime = 0.0; @@ -225,46 +246,56 @@ var avatarAcceleration = 0.75; var avatarVelocity = 0.0; var avatarMaxVelocity = 1.4; -function keepWalking(deltaTime) { - - walkTime += avatarVelocity * deltaTime; - if (walkTime > walkWheelRate) { - walkTime = 0.0; - currentFrame++; - if (currentFrame > 3) { - currentFrame = 0; - } - } +function handleAnimation(deltaTime) { - var frame = walkKeyFrames[currentFrame]; + if (avatarVelocity == 0.0) { + walkTime = 0.0; + currentFrame = 0; + } else { + walkTime += avatarVelocity * deltaTime; + if (walkTime > walkWheelRate) { + walkTime = 0.0; + currentFrame++; + if (currentFrame > 3) { + currentFrame = 0; + } + } + } + + var frame = walkKeyFrames[currentFrame]; - var interp = walkTime / walkWheelRate; + var walkInterp = walkTime / walkWheelRate; + var animInterp = avatarVelocity / (avatarMaxVelocity / 2.0); + if (animInterp > 1.0) animInterp = 1.0; - for (var i = 0; i < JOINT_ORDER.length; i++) { - Avatar.setJointData(JOINT_ORDER[i], procAnimAPI.deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], interp)); - } + for (var i = 0; i < JOINT_ORDER.length; i++) { + var walkJoint = procAnimAPI.deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], walkInterp); + var standJoint = standingKeyFrame.rotations[i]; + var finalJoint = Quat.mix(standJoint, walkJoint, animInterp); + Avatar.setJointData(JOINT_ORDER[i], finalJoint); + } } function jumpWithLoudness(deltaTime) { - // potentially change pelvis height depending on trailing average loudness - - pelvisOscillatorVelocity += deltaTime * Agent.lastReceivedAudioLoudness * 700.0 ; + // potentially change pelvis height depending on trailing average loudness - pelvisOscillatorVelocity -= pelvisOscillatorPosition * 0.75; - pelvisOscillatorVelocity *= 0.97; - pelvisOscillatorPosition += deltaTime * pelvisOscillatorVelocity; - Avatar.headPitch = pelvisOscillatorPosition * 60.0; + pelvisOscillatorVelocity += deltaTime * Agent.lastReceivedAudioLoudness * 700.0 ; - var pelvisPosition = Avatar.position; - pelvisPosition.y = (Y_PELVIS - 0.35) + pelvisOscillatorPosition; - - if (pelvisPosition.y < Y_PELVIS) { - pelvisPosition.y = Y_PELVIS; - } else if (pelvisPosition.y > Y_PELVIS + 1.0) { - pelvisPosition.y = Y_PELVIS + 1.0; - } - - Avatar.position = pelvisPosition; + pelvisOscillatorVelocity -= pelvisOscillatorPosition * 0.75; + pelvisOscillatorVelocity *= 0.97; + pelvisOscillatorPosition += deltaTime * pelvisOscillatorVelocity; + Avatar.headPitch = pelvisOscillatorPosition * 60.0; + + var pelvisPosition = Avatar.position; + pelvisPosition.y = (Y_PELVIS - 0.35) + pelvisOscillatorPosition; + + if (pelvisPosition.y < Y_PELVIS) { + pelvisPosition.y = Y_PELVIS; + } else if (pelvisPosition.y > Y_PELVIS + 1.0) { + pelvisPosition.y = Y_PELVIS + 1.0; + } + + Avatar.position = pelvisPosition; } var forcedMove = false; @@ -272,110 +303,105 @@ var forcedMove = false; var wasMovingLastFrame = false; function handleHeadTurn() { - if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) { - targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE); - isTurningHead = true; - } else { - Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * PITCH_RATE; - if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE) { - isTurningHead = false; + if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) { + targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE); + isTurningHead = true; + } else { + Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * PITCH_RATE; + if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE) { + isTurningHead = false; + } } - } } function stopWalking() { - Avatar.clearJointData(JOINT_R_HIP); - Avatar.clearJointData(JOINT_R_KNEE); - Avatar.clearJointData(JOINT_L_HIP); - Avatar.clearJointData(JOINT_L_KNEE); - avatarVelocity = 0.0; - isMoving = false; + avatarVelocity = 0.0; + isMoving = false; } var MAX_ATTEMPTS = 40; function handleWalking(deltaTime) { - if (forcedMove || (!isMoving && Math.random() < CHANCE_OF_MOVING)) { - // Set new target location - - //Keep trying new orientations if the desired target location is out of bounds - var attempts = 0; - do { - targetOrientation = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 })); - var front = Quat.getFront(targetOrientation); - - targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL))); - } - while ((targetPosition.x < X_MIN || targetPosition.x > X_MAX || targetPosition.z < Z_MIN || targetPosition.z > Z_MAX) - && attempts < MAX_ATTEMPTS); - - targetPosition.x = clamp(targetPosition.x, X_MIN, X_MAX); - targetPosition.z = clamp(targetPosition.z, Z_MIN, Z_MAX); - targetPosition.y = Y_PELVIS; - - wasMovingLastFrame = true; - isMoving = true; - forcedMove = false; - } else if (isMoving) { - keepWalking(deltaTime); - - var targetVector = Vec3.subtract(targetPosition, Avatar.position); - var distance = Vec3.length(targetVector); - if (distance <= avatarVelocity * deltaTime) { - Avatar.position = targetPosition; - stopWalking(); - } else { - var direction = Vec3.normalize(targetVector); - //Figure out if we should be slowing down - var t = avatarVelocity / avatarAcceleration; - var d = (avatarVelocity / 2.0) * t; - if (distance < d) { - avatarVelocity -= avatarAcceleration * deltaTime; - if (avatarVelocity <= 0) { - stopWalking(); - } - } else { - avatarVelocity += avatarAcceleration * deltaTime; - if (avatarVelocity > avatarMaxVelocity) avatarVelocity = avatarMaxVelocity; + if (forcedMove || (!isMoving && Math.random() < CHANCE_OF_MOVING)) { + // Set new target location + + //Keep trying new orientations if the desired target location is out of bounds + var attempts = 0; + do { + targetOrientation = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 })); + var front = Quat.getFront(targetOrientation); + + targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL))); + } + while ((targetPosition.x < X_MIN || targetPosition.x > X_MAX || targetPosition.z < Z_MIN || targetPosition.z > Z_MAX) + && attempts < MAX_ATTEMPTS); + + targetPosition.x = clamp(targetPosition.x, X_MIN, X_MAX); + targetPosition.z = clamp(targetPosition.z, Z_MIN, Z_MAX); + targetPosition.y = Y_PELVIS; + + wasMovingLastFrame = true; + isMoving = true; + forcedMove = false; + } else if (isMoving) { + + var targetVector = Vec3.subtract(targetPosition, Avatar.position); + var distance = Vec3.length(targetVector); + if (distance <= avatarVelocity * deltaTime) { + Avatar.position = targetPosition; + stopWalking(); + } else { + var direction = Vec3.normalize(targetVector); + //Figure out if we should be slowing down + var t = avatarVelocity / avatarAcceleration; + var d = (avatarVelocity / 2.0) * t; + if (distance < d) { + avatarVelocity -= avatarAcceleration * deltaTime; + if (avatarVelocity <= 0) { + stopWalking(); + } + } else { + avatarVelocity += avatarAcceleration * deltaTime; + if (avatarVelocity > avatarMaxVelocity) avatarVelocity = avatarMaxVelocity; + } + Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(direction, avatarVelocity * deltaTime)); + Avatar.orientation = Quat.mix(Avatar.orientation, targetOrientation, TURN_RATE); + + wasMovingLastFrame = true; + } - Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(direction, avatarVelocity * deltaTime)); - Avatar.orientation = Quat.mix(Avatar.orientation, targetOrientation, TURN_RATE); - - wasMovingLastFrame = true; - } - } } function handleTalking() { - if (Math.random() < CHANCE_OF_SOUND) { - playRandomSound(); - } + if (Math.random() < CHANCE_OF_SOUND) { + playRandomSound(); + } } function changePelvisHeight(newHeight) { - var newPosition = Avatar.position; - newPosition.y = newHeight; - Avatar.position = newPosition; + var newPosition = Avatar.position; + newPosition.y = newHeight; + Avatar.position = newPosition; } function updateBehavior(deltaTime) { - cumulativeTime += deltaTime; - if (AvatarList.containsAvatarWithDisplayName("mrdj")) { - if (wasMovingLastFrame) { - isMoving = false; + if (AvatarList.containsAvatarWithDisplayName("mrdj")) { + if (wasMovingLastFrame) { + isMoving = false; + } + + // we have a DJ, shouldn't we be dancing? + jumpWithLoudness(deltaTime); + } else { + + // no DJ, let's just chill on the dancefloor - randomly walking and talking + handleHeadTurn(); + handleAnimation(deltaTime); + handleWalking(deltaTime); + handleTalking(); } - - // we have a DJ, shouldn't we be dancing? - jumpWithLoudness(deltaTime); - } else { - - // no DJ, let's just chill on the dancefloor - randomly walking and talking - handleHeadTurn(); - handleWalking(deltaTime); - handleTalking(); - } } Script.update.connect(updateBehavior); \ No newline at end of file From ab1be38fd5eba19ed91f098e17e25b2777764b4e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 14:02:46 -0700 Subject: [PATCH 35/56] reset the cost of the packet back to 0 so it doesn't keep accumulating --- libraries/octree/src/OctreeEditPacketSender.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 8c9bee2be0..634a0bb260 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -324,6 +324,9 @@ void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, Pa packetBuffer._currentSize += sizeof(quint64); // nudge past timestamp packetBuffer._currentType = type; + + // reset cost for packet to 0 + packetBuffer._satoshiCost = 0; } bool OctreeEditPacketSender::process() { From 055a97fc2d634db805a5730c22ae783a71fe8cef Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 30 Jul 2014 14:18:38 -0700 Subject: [PATCH 36/56] Added facial animation to the procedural bot --- examples/proceduralBot.js | 288 ++++++++++++++++++++++++++++++++++---- 1 file changed, 263 insertions(+), 25 deletions(-) diff --git a/examples/proceduralBot.js b/examples/proceduralBot.js index d8218c8341..f02ca934dc 100644 --- a/examples/proceduralBot.js +++ b/examples/proceduralBot.js @@ -27,10 +27,10 @@ function printVector(string, vector) { print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); } -var CHANCE_OF_MOVING = 0.01; -var CHANCE_OF_SOUND = 0; +var CHANCE_OF_MOVING = 0.00; +var CHANCE_OF_SOUND = 0.005; var CHANCE_OF_HEAD_TURNING = 0.05; -var CHANCE_OF_BIG_MOVE = 0.1; +var CHANCE_OF_BIG_MOVE = 1.0; var isMoving = false; var isTurningHead = false; @@ -45,12 +45,13 @@ var MAX_PELVIS_DELTA = 2.5; var AVATAR_PELVIS_HEIGHT = 0.75; -var MOVE_RANGE_SMALL = 10.0; +var MOVE_RANGE_SMALL = 3.0; +var MOVE_RANGE_BIG = 10.0; var TURN_RANGE = 70.0; var STOP_TOLERANCE = 0.05; var MOVE_RATE = 0.05; var TURN_RATE = 0.2; -var PITCH_RATE = 0.10; +var PITCH_RATE = 0.05; var PITCH_RANGE = 20.0; //var firstPosition = { x: getRandomFloat(X_MIN, X_MAX), y: Y_PELVIS, z: getRandomFloat(Z_MIN, Z_MAX) }; @@ -110,33 +111,254 @@ basePelvisHeight = firstPosition.y; printVector("New dancer, position = ", Avatar.position); function loadSounds() { - var sound_filenames = ["AB1.raw", "Anchorman2.raw", "B1.raw", "B1.raw", "Bale1.raw", "Bandcamp.raw", + var sound_filenames = ["AB1.raw", "Anchorman2.raw", "B1.raw", "B1.raw", "Bale1.raw", "Bandcamp.raw", "Big1.raw", "Big2.raw", "Brian1.raw", "Buster1.raw", "CES1.raw", "CES2.raw", "CES3.raw", "CES4.raw", "Carrie1.raw", "Carrie3.raw", "Charlotte1.raw", "EN1.raw", "EN2.raw", "EN3.raw", "Eugene1.raw", "Francesco1.raw", "Italian1.raw", "Japanese1.raw", "Leigh1.raw", "Lucille1.raw", "Lucille2.raw", "MeanGirls.raw", "Murray2.raw", "Nigel1.raw", "PennyLane.raw", "Pitt1.raw", "Ricardo.raw", "SN.raw", "Sake1.raw", "Samantha1.raw", "Samantha2.raw", "Spicoli1.raw", "Supernatural.raw", "Swearengen1.raw", "TheDude.raw", "Tony.raw", "Triumph1.raw", "Uma1.raw", "Walken1.raw", "Walken2.raw", "Z1.raw", "Z2.raw" - ]; - - var SOUND_BASE_URL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/"; - - for (var i = 0; i < sound_filenames.length; i++) { - sounds.push(new Sound(SOUND_BASE_URL + sound_filenames[i])); - } + ]; + + var footstep_filenames = ["FootstepW2Left-12db.wav", "FootstepW2Right-12db.wav", "FootstepW3Left-12db.wav", "FootstepW3Right-12db.wav", + "FootstepW5Left-12db.wav", "FootstepW5Right-12db.wav"]; + + var SOUND_BASE_URL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/"; + + var FOOTSTEP_BASE_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Footsteps/"; + + for (var i = 0; i < sound_filenames.length; i++) { + sounds.push(new Sound(SOUND_BASE_URL + sound_filenames[i])); + } + + for (var i = 0; i < footstep_filenames.length; i++) { + footstepSounds.push(new Sound(FOOTSTEP_BASE_URL + footstep_filenames[i])); + } } var sounds = []; +var footstepSounds = []; loadSounds(); function playRandomSound() { - if (!Agent.isPlayingAvatarSound) { - var whichSound = Math.floor((Math.random() * sounds.length) % sounds.length); - Agent.playAvatarSound(sounds[whichSound]); - } + if (!Agent.isPlayingAvatarSound) { + var whichSound = Math.floor((Math.random() * sounds.length)); + Agent.playAvatarSound(sounds[whichSound]); + } } +function playRandomFootstepSound() { + + var whichSound = Math.floor((Math.random() * footstepSounds.length)); + var options = new AudioInjectionOptions(); + options.position = Avatar.position; + options.volume = 1.0; + Audio.playSound(footstepSounds[whichSound], options); + +} + +// ************************************ Facial Animation ********************************** +var allBlendShapes = []; +var targetBlendCoefficient = []; +var currentBlendCoefficient = []; + +//Blendshape constructor +function addBlendshapeToPose(pose, shapeIndex, val) { + var index = pose.blendShapes.length; + pose.blendShapes[index] = {shapeIndex: shapeIndex, val: val }; +} +//The mood of the avatar, determines face. 0 = happy, 1 = angry, 2 = sad. +var avatarMood = 0; +var currentExpression = -1; +//Face pose constructor +var happyPoses = []; + +happyPoses[0] = {blendShapes: []}; +addBlendshapeToPose(happyPoses[0], 28, 0.7); //MouthSmile_L +addBlendshapeToPose(happyPoses[0], 29, 0.7); //MouthSmile_R + +happyPoses[1] = {blendShapes: []}; +addBlendshapeToPose(happyPoses[1], 28, 1.0); //MouthSmile_L +addBlendshapeToPose(happyPoses[1], 29, 1.0); //MouthSmile_R +addBlendshapeToPose(happyPoses[1], 21, 0.2); //JawOpen + +happyPoses[2] = {blendShapes: []}; +addBlendshapeToPose(happyPoses[2], 28, 1.0); //MouthSmile_L +addBlendshapeToPose(happyPoses[2], 29, 1.0); //MouthSmile_R +addBlendshapeToPose(happyPoses[2], 21, 0.5); //JawOpen +addBlendshapeToPose(happyPoses[2], 46, 1.0); //CheekSquint_L +addBlendshapeToPose(happyPoses[2], 47, 1.0); //CheekSquint_R +addBlendshapeToPose(happyPoses[2], 17, 1.0); //BrowsU_L +addBlendshapeToPose(happyPoses[2], 18, 1.0); //BrowsU_R + +var angryPoses = []; + +angryPoses[0] = {blendShapes: []}; +addBlendshapeToPose(angryPoses[0], 26, 0.6); //MouthFrown_L +addBlendshapeToPose(angryPoses[0], 27, 0.6); //MouthFrown_R +addBlendshapeToPose(angryPoses[0], 14, 0.6); //BrowsD_L +addBlendshapeToPose(angryPoses[0], 15, 0.6); //BrowsD_R + +angryPoses[1] = {blendShapes: []}; +addBlendshapeToPose(angryPoses[1], 26, 0.9); //MouthFrown_L +addBlendshapeToPose(angryPoses[1], 27, 0.9); //MouthFrown_R +addBlendshapeToPose(angryPoses[1], 14, 0.9); //BrowsD_L +addBlendshapeToPose(angryPoses[1], 15, 0.9); //BrowsD_R + +angryPoses[2] = {blendShapes: []}; +addBlendshapeToPose(angryPoses[2], 26, 1.0); //MouthFrown_L +addBlendshapeToPose(angryPoses[2], 27, 1.0); //MouthFrown_R +addBlendshapeToPose(angryPoses[2], 14, 1.0); //BrowsD_L +addBlendshapeToPose(angryPoses[2], 15, 1.0); //BrowsD_R +addBlendshapeToPose(angryPoses[2], 21, 0.5); //JawOpen +addBlendshapeToPose(angryPoses[2], 46, 1.0); //CheekSquint_L +addBlendshapeToPose(angryPoses[2], 47, 1.0); //CheekSquint_R + +var sadPoses = []; + +sadPoses[0] = {blendShapes: []}; +addBlendshapeToPose(sadPoses[0], 26, 0.6); //MouthFrown_L +addBlendshapeToPose(sadPoses[0], 27, 0.6); //MouthFrown_R +addBlendshapeToPose(sadPoses[0], 16, 0.2); //BrowsU_C +addBlendshapeToPose(sadPoses[0], 2, 0.6); //EyeSquint_L +addBlendshapeToPose(sadPoses[0], 3, 0.6); //EyeSquint_R + +sadPoses[1] = {blendShapes: []}; +addBlendshapeToPose(sadPoses[1], 26, 0.9); //MouthFrown_L +addBlendshapeToPose(sadPoses[1], 27, 0.9); //MouthFrown_R +addBlendshapeToPose(sadPoses[1], 16, 0.6); //BrowsU_C +addBlendshapeToPose(sadPoses[1], 2, 0.9); //EyeSquint_L +addBlendshapeToPose(sadPoses[1], 3, 0.9); //EyeSquint_R + +sadPoses[2] = {blendShapes: []}; +addBlendshapeToPose(sadPoses[2], 26, 1.0); //MouthFrown_L +addBlendshapeToPose(sadPoses[2], 27, 1.0); //MouthFrown_R +addBlendshapeToPose(sadPoses[2], 16, 0.1); //BrowsU_C +addBlendshapeToPose(sadPoses[2], 2, 1.0); //EyeSquint_L +addBlendshapeToPose(sadPoses[2], 3, 1.0); //EyeSquint_R +addBlendshapeToPose(sadPoses[2], 21, 0.3); //JawOpen + +var facePoses = []; +facePoses[0] = happyPoses; +facePoses[1] = angryPoses; +facePoses[2] = sadPoses; + + +function addBlendShape(s) { + allBlendShapes[allBlendShapes.length] = s; +} + +//It is imperative that the following blendshapes are all present and are in the correct order +addBlendShape("EyeBlink_L"); //0 +addBlendShape("EyeBlink_R"); //1 +addBlendShape("EyeSquint_L"); //2 +addBlendShape("EyeSquint_R"); //3 +addBlendShape("EyeDown_L"); //4 +addBlendShape("EyeDown_R"); //5 +addBlendShape("EyeIn_L"); //6 +addBlendShape("EyeIn_R"); //7 +addBlendShape("EyeOpen_L"); //8 +addBlendShape("EyeOpen_R"); //9 +addBlendShape("EyeOut_L"); //10 +addBlendShape("EyeOut_R"); //11 +addBlendShape("EyeUp_L"); //12 +addBlendShape("EyeUp_R"); //13 +addBlendShape("BrowsD_L"); //14 +addBlendShape("BrowsD_R"); //15 +addBlendShape("BrowsU_C"); //16 +addBlendShape("BrowsU_L"); //17 +addBlendShape("BrowsU_R"); //18 +addBlendShape("JawFwd"); //19 +addBlendShape("JawLeft"); //20 +addBlendShape("JawOpen"); //21 +addBlendShape("JawChew"); //22 +addBlendShape("JawRight"); //23 +addBlendShape("MouthLeft"); //24 +addBlendShape("MouthRight"); //25 +addBlendShape("MouthFrown_L"); //26 +addBlendShape("MouthFrown_R"); //27 +addBlendShape("MouthSmile_L"); //28 +addBlendShape("MouthSmile_R"); //29 +addBlendShape("MouthDimple_L"); //30 +addBlendShape("MouthDimple_R"); //31 +addBlendShape("LipsStretch_L"); //32 +addBlendShape("LipsStretch_R"); //33 +addBlendShape("LipsUpperClose"); //34 +addBlendShape("LipsLowerClose"); //35 +addBlendShape("LipsUpperUp"); //36 +addBlendShape("LipsLowerDown"); //37 +addBlendShape("LipsUpperOpen"); //38 +addBlendShape("LipsLowerOpen"); //39 +addBlendShape("LipsFunnel"); //40 +addBlendShape("LipsPucker"); //41 +addBlendShape("ChinLowerRaise"); //42 +addBlendShape("ChinUpperRaise"); //43 +addBlendShape("Sneer"); //44 +addBlendShape("Puff"); //45 +addBlendShape("CheekSquint_L"); //46 +addBlendShape("CheekSquint_R"); //47 + +for (var i = 0; i < allBlendShapes.length; i++) { + targetBlendCoefficient[i] = 0; + currentBlendCoefficient[i] = 0; +} + +function setRandomExpression() { + + //Clear all expression data for current expression + if (currentExpression != -1) { + var expression = facePoses[avatarMood][currentExpression]; + for (var i = 0; i < expression.blendShapes.length; i++) { + targetBlendCoefficient[expression.blendShapes[i].shapeIndex] = 0.0; + } + } + //Get a new current expression + currentExpression = Math.floor(Math.random() * facePoses[avatarMood].length); + var expression = facePoses[avatarMood][currentExpression]; + for (var i = 0; i < expression.blendShapes.length; i++) { + targetBlendCoefficient[expression.blendShapes[i].shapeIndex] = expression.blendShapes[i].val; + } +} + +var expressionChangeSpeed = 0.1; +function updateBlendShapes(deltaTime) { + + for (var i = 0; i < allBlendShapes.length; i++) { + currentBlendCoefficient[i] += (targetBlendCoefficient[i] - currentBlendCoefficient[i]) * expressionChangeSpeed; + Avatar.setBlendshape(allBlendShapes[i], currentBlendCoefficient[i]); + } +} + +var BLINK_SPEED = 0.15; +var CHANCE_TO_BLINK = 0.0025; +var MAX_BLINK = 0.85; +var blink = 0.0; +var isBlinking = false; +function updateBlinking(deltaTime) { + if (isBlinking == false) { + if (Math.random() < CHANCE_TO_BLINK) { + isBlinking = true; + } else { + blink -= BLINK_SPEED; + if (blink < 0.0) blink = 0.0; + } + } else { + blink += BLINK_SPEED; + if (blink > MAX_BLINK) { + blink = MAX_BLINK; + isBlinking = false; + } + } + + currentBlendCoefficient[0] = blink; + currentBlendCoefficient[1] = blink; + targetBlendCoefficient[0] = blink; + targetBlendCoefficient[1] = blink; +} + +// ************************************************************************************* + //Procedural walk animation using two keyframes //We use a separate array for front and back joints //Pitch, yaw, and roll for the joints @@ -248,24 +470,33 @@ var avatarMaxVelocity = 1.4; function handleAnimation(deltaTime) { + updateBlinking(deltaTime); + updateBlendShapes(deltaTime); + + if (Math.random() < 0.01) { + setRandomExpression(); + } + if (avatarVelocity == 0.0) { walkTime = 0.0; currentFrame = 0; } else { - walkTime += avatarVelocity * deltaTime; - if (walkTime > walkWheelRate) { - walkTime = 0.0; - currentFrame++; - if (currentFrame > 3) { + walkTime += avatarVelocity * deltaTime; + if (walkTime > walkWheelRate) { + walkTime = 0.0; + currentFrame++; + if (currentFrame % 2 == 1) { + playRandomFootstepSound(); + } + if (currentFrame > 3) { currentFrame = 0; } } } - var frame = walkKeyFrames[currentFrame]; var walkInterp = walkTime / walkWheelRate; - var animInterp = avatarVelocity / (avatarMaxVelocity / 2.0); + var animInterp = avatarVelocity / (avatarMaxVelocity / 1.3); if (animInterp > 1.0) animInterp = 1.0; for (var i = 0; i < JOINT_ORDER.length; i++) { @@ -325,13 +556,20 @@ function handleWalking(deltaTime) { if (forcedMove || (!isMoving && Math.random() < CHANCE_OF_MOVING)) { // Set new target location + var moveRange; + if (Math.random() < CHANCE_OF_BIG_MOVE) { + moveRange = MOVE_RANGE_BIG; + } else { + moveRange = MOVE_RANGE_SMALL; + } + //Keep trying new orientations if the desired target location is out of bounds var attempts = 0; do { targetOrientation = Quat.multiply(Avatar.orientation, Quat.angleAxis(getRandomFloat(-TURN_RANGE, TURN_RANGE), { x:0, y:1, z:0 })); var front = Quat.getFront(targetOrientation); - targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, MOVE_RANGE_SMALL))); + targetPosition = Vec3.sum(Avatar.position, Vec3.multiply(front, getRandomFloat(0.0, moveRange))); } while ((targetPosition.x < X_MIN || targetPosition.x > X_MAX || targetPosition.z < Z_MIN || targetPosition.z > Z_MAX) && attempts < MAX_ATTEMPTS); From 2796f714198c07324b43d95696f63af114557cd3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 15:06:15 -0700 Subject: [PATCH 37/56] library cleanup to suppress warnings on cmake 3.0 --- CMakeLists.txt | 7 +++++-- animation-server/CMakeLists.txt | 2 -- assignment-client/CMakeLists.txt | 6 ------ cmake/macros/SetupHifiProject.cmake | 6 +++--- domain-server/CMakeLists.txt | 2 -- interface/CMakeLists.txt | 6 ------ libraries/animation/CMakeLists.txt | 6 ------ libraries/audio/CMakeLists.txt | 6 ------ libraries/avatars/CMakeLists.txt | 6 ------ libraries/embedded-webserver/CMakeLists.txt | 6 ------ libraries/fbx/CMakeLists.txt | 6 ------ libraries/metavoxels/CMakeLists.txt | 6 ------ libraries/models/CMakeLists.txt | 6 ------ libraries/networking/CMakeLists.txt | 6 ------ libraries/octree/CMakeLists.txt | 6 ------ libraries/particles/CMakeLists.txt | 6 ------ libraries/script-engine/CMakeLists.txt | 6 ------ libraries/shared/CMakeLists.txt | 6 ------ libraries/voxels/CMakeLists.txt | 7 ------- tests/CMakeLists.txt | 5 +---- tests/audio/CMakeLists.txt | 6 ------ tests/metavoxels/CMakeLists.txt | 6 ------ tests/networking/CMakeLists.txt | 6 ------ tests/octree/CMakeLists.txt | 6 ------ tests/physics/CMakeLists.txt | 6 ------ tests/shared/CMakeLists.txt | 6 ------ tools/CMakeLists.txt | 1 - tools/bitstream2json/CMakeLists.txt | 6 ------ tools/json2bitstream/CMakeLists.txt | 6 ------ tools/mtc/CMakeLists.txt | 10 +--------- voxel-edit/CMakeLists.txt | 6 ------ 31 files changed, 10 insertions(+), 168 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b7c55740a0..91290c6a96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,12 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 2.8.12.2) if (WIN32) - cmake_policy (SET CMP0020 NEW) + cmake_policy(SET CMP0020 NEW) endif (WIN32) +cmake_policy(SET CMP0028 OLD) +cmake_policy(SET CMP0043 OLD) + project(hifi) add_definitions(-DGLM_FORCE_RADIANS) diff --git a/animation-server/CMakeLists.txt b/animation-server/CMakeLists.txt index 31ed5d98df..116ee0e942 100644 --- a/animation-server/CMakeLists.txt +++ b/animation-server/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - if (WIN32) cmake_policy (SET CMP0020 NEW) endif (WIN32) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 873ae761ca..5ca021b175 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME assignment-client) set(ROOT_DIR ..) diff --git a/cmake/macros/SetupHifiProject.cmake b/cmake/macros/SetupHifiProject.cmake index 50cd3a4150..15c70cc2a4 100644 --- a/cmake/macros/SetupHifiProject.cmake +++ b/cmake/macros/SetupHifiProject.cmake @@ -7,7 +7,7 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -macro(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) +macro(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) project(${TARGET}) # grab the implemenation and header files @@ -25,10 +25,10 @@ macro(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) # add the executable, include additional optional sources add_executable(${TARGET} ${TARGET_SRCS} ${ARGN}) - if (${INCLUDE_QT}) + if (INCLUDE_QT) find_package(Qt5Core REQUIRED) qt5_use_modules(${TARGET} Core) endif () - + target_link_libraries(${TARGET} ${QT_LIBRARIES}) endmacro() \ No newline at end of file diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index f9bbeb31fc..6ee794f7c6 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - if (WIN32) cmake_policy (SET CMP0020 NEW) endif (WIN32) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c549440334..7336b55852 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt index 1e33f50659..b1dc59fff8 100644 --- a/libraries/animation/CMakeLists.txt +++ b/libraries/animation/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/audio/CMakeLists.txt b/libraries/audio/CMakeLists.txt index fafdfc7e6c..2ad8a4b0bc 100644 --- a/libraries/audio/CMakeLists.txt +++ b/libraries/audio/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index fc398fc33d..ca4a2630b4 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/embedded-webserver/CMakeLists.txt b/libraries/embedded-webserver/CMakeLists.txt index 3dc9074332..1386bcc392 100644 --- a/libraries/embedded-webserver/CMakeLists.txt +++ b/libraries/embedded-webserver/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index bc5ac3b3c4..45d1051dc6 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/metavoxels/CMakeLists.txt b/libraries/metavoxels/CMakeLists.txt index 79c07c6c47..c79631ce06 100644 --- a/libraries/metavoxels/CMakeLists.txt +++ b/libraries/metavoxels/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/models/CMakeLists.txt b/libraries/models/CMakeLists.txt index 06578371cc..8056d215da 100644 --- a/libraries/models/CMakeLists.txt +++ b/libraries/models/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index fdd2d5830a..9a13374a4f 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/octree/CMakeLists.txt b/libraries/octree/CMakeLists.txt index 5988b4b058..031f7ef69a 100644 --- a/libraries/octree/CMakeLists.txt +++ b/libraries/octree/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/particles/CMakeLists.txt b/libraries/particles/CMakeLists.txt index dbf2995293..76b3373466 100644 --- a/libraries/particles/CMakeLists.txt +++ b/libraries/particles/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index d0d05df286..2dd40c7ece 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 560546473c..f099f424e9 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/libraries/voxels/CMakeLists.txt b/libraries/voxels/CMakeLists.txt index 51b83bb650..2ae35da2c0 100644 --- a/libraries/voxels/CMakeLists.txt +++ b/libraries/voxels/CMakeLists.txt @@ -1,10 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - - set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2f56d337fa..dd2e6e396e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,10 +1,7 @@ -cmake_minimum_required(VERSION 2.8) - # add the test directories file(GLOB TEST_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*") foreach(DIR ${TEST_SUBDIRS}) if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}") add_subdirectory(${DIR}) endif() -endforeach() - +endforeach() \ No newline at end of file diff --git a/tests/audio/CMakeLists.txt b/tests/audio/CMakeLists.txt index 5c5178cb71..b7375a0086 100644 --- a/tests/audio/CMakeLists.txt +++ b/tests/audio/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME audio-tests) set(ROOT_DIR ../..) diff --git a/tests/metavoxels/CMakeLists.txt b/tests/metavoxels/CMakeLists.txt index ca141c5137..f4c0695362 100644 --- a/tests/metavoxels/CMakeLists.txt +++ b/tests/metavoxels/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME metavoxel-tests) set(ROOT_DIR ../..) diff --git a/tests/networking/CMakeLists.txt b/tests/networking/CMakeLists.txt index 2e094d2ce7..64b5f273d1 100644 --- a/tests/networking/CMakeLists.txt +++ b/tests/networking/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME networking-tests) set(ROOT_DIR ../..) diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index 9c5e031d74..c7a6500b6d 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME octree-tests) set(ROOT_DIR ../..) diff --git a/tests/physics/CMakeLists.txt b/tests/physics/CMakeLists.txt index 9b336523f1..643d318f7d 100644 --- a/tests/physics/CMakeLists.txt +++ b/tests/physics/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME physics-tests) set(ROOT_DIR ../..) diff --git a/tests/shared/CMakeLists.txt b/tests/shared/CMakeLists.txt index b9513e3f26..0785314d36 100644 --- a/tests/shared/CMakeLists.txt +++ b/tests/shared/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME shared-tests) set(ROOT_DIR ../..) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 79db82e90f..c9c0690cc7 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 2.8) # add the tool directories add_subdirectory(bitstream2json) diff --git a/tools/bitstream2json/CMakeLists.txt b/tools/bitstream2json/CMakeLists.txt index d5b82adbd9..576406e787 100644 --- a/tools/bitstream2json/CMakeLists.txt +++ b/tools/bitstream2json/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME bitstream2json) set(ROOT_DIR ../..) diff --git a/tools/json2bitstream/CMakeLists.txt b/tools/json2bitstream/CMakeLists.txt index b93c57b582..5ff4673298 100644 --- a/tools/json2bitstream/CMakeLists.txt +++ b/tools/json2bitstream/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME json2bitstream) set(ROOT_DIR ../..) diff --git a/tools/mtc/CMakeLists.txt b/tools/mtc/CMakeLists.txt index a7ab97c7f2..582c5e3bfd 100644 --- a/tools/mtc/CMakeLists.txt +++ b/tools/mtc/CMakeLists.txt @@ -1,15 +1,7 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME mtc) set(ROOT_DIR ../..) set(MACRO_DIR "${ROOT_DIR}/cmake/macros") include(${MACRO_DIR}/SetupHifiProject.cmake) -setup_hifi_project(${TARGET_NAME} TRUE) - - +setup_hifi_project(${TARGET_NAME} TRUE) \ No newline at end of file diff --git a/voxel-edit/CMakeLists.txt b/voxel-edit/CMakeLists.txt index 6ebf23efba..006dfb0599 100644 --- a/voxel-edit/CMakeLists.txt +++ b/voxel-edit/CMakeLists.txt @@ -1,9 +1,3 @@ -cmake_minimum_required(VERSION 2.8) - -if (WIN32) - cmake_policy (SET CMP0020 NEW) -endif (WIN32) - set(TARGET_NAME voxel-edit) set(ROOT_DIR ..) From 21229f22a70a0f39e901d6b53a3e12f61842de15 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 15:12:07 -0700 Subject: [PATCH 38/56] only set the policy if it exists --- CMakeLists.txt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 91290c6a96..de23f52271 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,16 @@ if (WIN32) cmake_policy(SET CMP0020 NEW) endif (WIN32) -cmake_policy(SET CMP0028 OLD) -cmake_policy(SET CMP0043 OLD) +if (POLICY CMP0028) + cmake_policy(SET CMP0028 OLD) +endif () + +if (POLICY CMP0043) + cmake_policy(SET CMP0043 OLD) +endif () + + + project(hifi) add_definitions(-DGLM_FORCE_RADIANS) @@ -42,7 +50,7 @@ set(CMAKE_AUTOMOC ON) # targets not supported on windows if (NOT WIN32) add_subdirectory(animation-server) -endif (NOT WIN32) +endif () # targets on all platforms add_subdirectory(assignment-client) From 54390600a32b89aec66fa9d0d7b20a8415b8ef96 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 15:13:01 -0700 Subject: [PATCH 39/56] remove some extra spaces --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de23f52271..2dbc89b75f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,9 +12,6 @@ if (POLICY CMP0043) cmake_policy(SET CMP0043 OLD) endif () - - - project(hifi) add_definitions(-DGLM_FORCE_RADIANS) From 4c59c0ebfe47ff6ac565606cfc3fb399e0999aee Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 15:19:43 -0700 Subject: [PATCH 40/56] fix reference to INCLUDE_QT variable in SetupHifiProject macro --- cmake/macros/SetupHifiProject.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/macros/SetupHifiProject.cmake b/cmake/macros/SetupHifiProject.cmake index 15c70cc2a4..ec731859d4 100644 --- a/cmake/macros/SetupHifiProject.cmake +++ b/cmake/macros/SetupHifiProject.cmake @@ -25,7 +25,7 @@ macro(SETUP_HIFI_PROJECT TARGET INCLUDE_QT) # add the executable, include additional optional sources add_executable(${TARGET} ${TARGET_SRCS} ${ARGN}) - if (INCLUDE_QT) + if (${INCLUDE_QT}) find_package(Qt5Core REQUIRED) qt5_use_modules(${TARGET} Core) endif () From 66c1aba7f7de493965a2bf23a236f89533f726ef Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 30 Jul 2014 15:23:03 -0700 Subject: [PATCH 41/56] Animation improvements, exposed roll/yaw head control. --- examples/proceduralBot.js | 78 ++++++++++++++++++++++-------- libraries/avatars/src/AvatarData.h | 10 +++- 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/examples/proceduralBot.js b/examples/proceduralBot.js index f02ca934dc..9b10b28243 100644 --- a/examples/proceduralBot.js +++ b/examples/proceduralBot.js @@ -27,9 +27,9 @@ function printVector(string, vector) { print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); } -var CHANCE_OF_MOVING = 0.00; -var CHANCE_OF_SOUND = 0.005; -var CHANCE_OF_HEAD_TURNING = 0.05; +var CHANCE_OF_MOVING = 0.000; +var CHANCE_OF_SOUND = 0;//0.005; +var CHANCE_OF_HEAD_TURNING = 0.01; var CHANCE_OF_BIG_MOVE = 1.0; var isMoving = false; @@ -51,8 +51,9 @@ var TURN_RANGE = 70.0; var STOP_TOLERANCE = 0.05; var MOVE_RATE = 0.05; var TURN_RATE = 0.2; -var PITCH_RATE = 0.05; -var PITCH_RANGE = 20.0; +var HEAD_TURN_RATE = 0.05; +var PITCH_RANGE = 15.0; +var YAW_RANGE = 35.0; //var firstPosition = { x: getRandomFloat(X_MIN, X_MAX), y: Y_PELVIS, z: getRandomFloat(Z_MIN, Z_MAX) }; var firstPosition = { x: 0.5, y: Y_PELVIS, z: 0.5 }; @@ -60,6 +61,7 @@ var targetPosition = { x: 0, y: 0, z: 0 }; var targetOrientation = { x: 0, y: 0, z: 0, w: 0 }; var currentOrientation = { x: 0, y: 0, z: 0, w: 0 }; var targetHeadPitch = 0.0; +var targetHeadYaw = 0.0; var basePelvisHeight = 0.0; var pelvisOscillatorPosition = 0.0; @@ -93,8 +95,8 @@ if (botNumber <= 20) { newBodyFilePrefix = "bot" + botNumber; } - newFaceFilePrefix = "ron"; - newBodyFilePrefix = "bot" + 63; +// newFaceFilePrefix = "ron"; +// newBodyFilePrefix = "bot" + 63; // set the face model fst using the bot number // there is no need to change the body model - we're using the default @@ -379,6 +381,10 @@ var JOINT_R_FOREARM = 16; var JOINT_L_ARM = 39; var JOINT_L_FOREARM = 40; var JOINT_SPINE = 11; +var JOINT_R_FOOT = 3; +var JOINT_L_FOOT = 8; +var JOINT_R_TOE = 4; +var JOINT_L_TOE = 9; // ******************************* Animation Is Defined Below ************************************* @@ -389,15 +395,29 @@ for (var i = 0; i < NUM_FRAMES; i++) { middleAngles[i] = []; } //Joint order for actual joint mappings, should be interleaved R,L,R,L,...S,S,S for R = right, L = left, S = single -var JOINT_ORDER = [JOINT_R_HIP, JOINT_L_HIP, JOINT_R_KNEE, JOINT_L_KNEE, JOINT_R_ARM, JOINT_L_ARM, JOINT_R_FOREARM, JOINT_L_FOREARM, JOINT_SPINE]; - -//Joint indices for joints that are duplicated, such as arms, It must match JOINT_ORDER +var JOINT_ORDER = []; +//*** right / left joints *** var HIP = 0; +JOINT_ORDER.push(JOINT_R_HIP); +JOINT_ORDER.push(JOINT_L_HIP); var KNEE = 1; +JOINT_ORDER.push(JOINT_R_KNEE); +JOINT_ORDER.push(JOINT_L_KNEE); var ARM = 2; +JOINT_ORDER.push(JOINT_R_ARM); +JOINT_ORDER.push(JOINT_L_ARM); var FOREARM = 3; -//Joint indices for single joints +JOINT_ORDER.push(JOINT_R_FOREARM); +JOINT_ORDER.push(JOINT_L_FOREARM); +var FOOT = 4; +JOINT_ORDER.push(JOINT_R_FOOT); +JOINT_ORDER.push(JOINT_L_FOOT); +var TOE = 5; +JOINT_ORDER.push(JOINT_R_TOE); +JOINT_ORDER.push(JOINT_L_TOE); +//*** middle joints *** var SPINE = 0; +JOINT_ORDER.push(JOINT_SPINE); //We have to store the angles so we can invert yaw and roll when making the animation //symmetrical @@ -408,11 +428,15 @@ rightAngles[0][HIP] = [30.0, 0.0, 8.0]; rightAngles[0][KNEE] = [-15.0, 0.0, 0.0]; rightAngles[0][ARM] = [85.0, -25.0, 0.0]; rightAngles[0][FOREARM] = [0.0, 0.0, -15.0]; +rightAngles[0][FOOT] = [0.0, 0.0, 0.0]; +rightAngles[0][TOE] = [0.0, 0.0, 0.0]; leftAngles[0][HIP] = [-15, 0.0, 8.0]; -leftAngles[0][KNEE] = [-28, 0.0, 0.0]; +leftAngles[0][KNEE] = [-26, 0.0, 0.0]; leftAngles[0][ARM] = [85.0, 20.0, 0.0]; leftAngles[0][FOREARM] = [10.0, 0.0, -25.0]; +leftAngles[0][FOOT] = [-13.0, 0.0, 0.0]; +leftAngles[0][TOE] = [34.0, 0.0, 0.0]; middleAngles[0][SPINE] = [0.0, -15.0, 5.0]; @@ -421,11 +445,15 @@ rightAngles[1][HIP] = [6.0, 0.0, 8.0]; rightAngles[1][KNEE] = [-12.0, 0.0, 0.0]; rightAngles[1][ARM] = [85.0, 0.0, 0.0]; rightAngles[1][FOREARM] = [0.0, 0.0, -15.0]; +rightAngles[1][FOOT] = [6.0, -8.0, 0.0]; +rightAngles[1][TOE] = [0.0, 0.0, 0.0]; leftAngles[1][HIP] = [10.0, 0.0, 8.0]; -leftAngles[1][KNEE] = [-55.0, 0.0, 0.0]; +leftAngles[1][KNEE] = [-60.0, 0.0, 0.0]; leftAngles[1][ARM] = [85.0, 0.0, 0.0]; leftAngles[1][FOREARM] = [0.0, 0.0, -15.0]; +leftAngles[1][FOOT] = [0.0, 0.0, 0.0]; +leftAngles[1][TOE] = [0.0, 0.0, 0.0]; middleAngles[1][SPINE] = [0.0, 0.0, 0.0]; @@ -444,11 +472,15 @@ rightQuats[HIP] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 7.0); rightQuats[KNEE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); rightQuats[ARM] = Quat.fromPitchYawRollDegrees(85.0, 0.0, 0.0); rightQuats[FOREARM] = Quat.fromPitchYawRollDegrees(0.0, 0.0, -10.0); +rightQuats[FOOT] = Quat.fromPitchYawRollDegrees(0.0, -8.0, 0.0); +rightQuats[TOE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); leftQuats[HIP] = Quat.fromPitchYawRollDegrees(0, 0.0, -7.0); leftQuats[KNEE] = Quat.fromPitchYawRollDegrees(0, 0.0, 0.0); leftQuats[ARM] = Quat.fromPitchYawRollDegrees(85.0, 0.0, 0.0); leftQuats[FOREARM] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 10.0); +leftQuats[FOOT] = Quat.fromPitchYawRollDegrees(0.0, 8.0, 0.0); +leftQuats[TOE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); middleQuats[SPINE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0); @@ -477,12 +509,12 @@ function handleAnimation(deltaTime) { setRandomExpression(); } - if (avatarVelocity == 0.0) { - walkTime = 0.0; - currentFrame = 0; - } else { - walkTime += avatarVelocity * deltaTime; - if (walkTime > walkWheelRate) { + if (avatarVelocity == 0.0) { + walkTime = 0.0; + currentFrame = 0; + } else { + walkTime += avatarVelocity * deltaTime; + if (walkTime > walkWheelRate) { walkTime = 0.0; currentFrame++; if (currentFrame % 2 == 1) { @@ -493,6 +525,7 @@ function handleAnimation(deltaTime) { } } } + var frame = walkKeyFrames[currentFrame]; var walkInterp = walkTime / walkWheelRate; @@ -536,10 +569,13 @@ var wasMovingLastFrame = false; function handleHeadTurn() { if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) { targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE); + targetHeadYaw = getRandomFloat(-YAW_RANGE, YAW_RANGE); isTurningHead = true; } else { - Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * PITCH_RATE; - if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE) { + Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * HEAD_TURN_RATE; + Avatar.headYaw = Avatar.headYaw + (targetHeadYaw - Avatar.headYaw) * HEAD_TURN_RATE; + if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE && + Math.abs(Avatar.headYaw - targetHeadYaw) < STOP_TOLERANCE) { isTurningHead = false; } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 008aecc817..8533b8b0e8 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -120,6 +120,8 @@ class AvatarData : public QObject { Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation) Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) + Q_PROPERTY(float headYaw READ getHeadYaw WRITE setHeadYaw) + Q_PROPERTY(float headRoll READ getHeadRoll WRITE setHeadRoll) Q_PROPERTY(float audioLoudness READ getAudioLoudness WRITE setAudioLoudness) Q_PROPERTY(float audioAverageLoudness READ getAudioAverageLoudness WRITE setAudioAverageLoudness) @@ -171,7 +173,13 @@ public: // access to Head().set/getMousePitch (degrees) float getHeadPitch() const { return _headData->getBasePitch(); } - void setHeadPitch(float value) { _headData->setBasePitch(value); }; + void setHeadPitch(float value) { _headData->setBasePitch(value); } + + float getHeadYaw() const { return _headData->getBaseYaw(); } + void setHeadYaw(float value) { _headData->setBaseYaw(value); } + + float getHeadRoll() const { return _headData->getBaseRoll(); } + void setHeadRoll(float value) { _headData->setBaseRoll(value); } // access to Head().set/getAverageLoudness float getAudioLoudness() const { return _headData->getAudioLoudness(); } From 8bb5c2b84c1b04070adf280d95ae5273ae8cc9b3 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 30 Jul 2014 16:59:15 -0700 Subject: [PATCH 42/56] Small improvements to script, renamed to bot_procedural --- .../{proceduralBot.js => bot_procedural.js.} | 39 ++++++------------- 1 file changed, 11 insertions(+), 28 deletions(-) rename examples/{proceduralBot.js => bot_procedural.js.} (96%) diff --git a/examples/proceduralBot.js b/examples/bot_procedural.js. similarity index 96% rename from examples/proceduralBot.js rename to examples/bot_procedural.js. index 9b10b28243..17e54007cc 100644 --- a/examples/proceduralBot.js +++ b/examples/bot_procedural.js. @@ -27,8 +27,8 @@ function printVector(string, vector) { print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); } -var CHANCE_OF_MOVING = 0.000; -var CHANCE_OF_SOUND = 0;//0.005; +var CHANCE_OF_MOVING = 0.005; +var CHANCE_OF_SOUND = 0.005; var CHANCE_OF_HEAD_TURNING = 0.01; var CHANCE_OF_BIG_MOVE = 1.0; @@ -40,11 +40,11 @@ var X_MIN = 0.50; var X_MAX = 15.60; var Z_MIN = 0.50; var Z_MAX = 15.10; -var Y_PELVIS = 1.0; +var Y_FEET = 0.0; +var AVATAR_PELVIS_HEIGHT = 0.84; +var Y_PELVIS = Y_FEET + AVATAR_PELVIS_HEIGHT; var MAX_PELVIS_DELTA = 2.5; -var AVATAR_PELVIS_HEIGHT = 0.75; - var MOVE_RANGE_SMALL = 3.0; var MOVE_RANGE_BIG = 10.0; var TURN_RANGE = 70.0; @@ -71,32 +71,15 @@ function clamp(val, min, max){ return Math.max(min, Math.min(max, val)) } -// pick an integer between 1 and 100 that is not 28 for the face model for this bot -botNumber = 28; +//Array of all valid bot numbers +var validBotNumbers = []; -while (botNumber == 28) { - botNumber = getRandomInt(1, 100); -} +// right now we only use bot 63, since many other bots have messed up skeletons and LOD issues +var botNumber = 63;//getRandomInt(0, 99); -if (botNumber <= 20) { - newFaceFilePrefix = "ron"; - newBodyFilePrefix = "defaultAvatar_body" -} else { - if (botNumber <= 40) { - newFaceFilePrefix = "superhero"; - } else if (botNumber <= 60) { - newFaceFilePrefix = "amber"; - } else if (botNumber <= 80) { - newFaceFilePrefix = "ron"; - } else { - newFaceFilePrefix = "angie"; - } +var newFaceFilePrefix = "ron"; - newBodyFilePrefix = "bot" + botNumber; -} - -// newFaceFilePrefix = "ron"; -// newBodyFilePrefix = "bot" + 63; +var newBodyFilePrefix = "bot" + botNumber; // set the face model fst using the bot number // there is no need to change the body model - we're using the default From eba92eb5173584d28ee11baddb6d33bc52075f79 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 17:21:04 -0700 Subject: [PATCH 43/56] have the OctreeEditPacketSender emit a signal when payment is required --- libraries/octree/src/OctreeEditPacketSender.cpp | 11 ++++++++++- libraries/octree/src/OctreeEditPacketSender.h | 14 +++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 634a0bb260..86208c9fd2 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -26,7 +26,10 @@ OctreeEditPacketSender::OctreeEditPacketSender() : _maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES), _releaseQueuedMessagesPending(false), _serverJurisdictions(NULL), - _maxPacketSize(MAX_PACKET_SIZE) { + _maxPacketSize(MAX_PACKET_SIZE), + _destinationWalletUUID() +{ + } OctreeEditPacketSender::~OctreeEditPacketSender() { @@ -98,6 +101,12 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c // send packet QByteArray packet(reinterpret_cast(buffer), length); queuePacketForSending(node, packet); + + if (hasDestinationWalletUUID() && satoshiCost > 0) { + // if we have a destination wallet UUID and a cost associated with this packet, signal that it + // needs to be sent + emit octreePaymentRequired(satoshiCost, nodeUUID, _destinationWalletUUID); + } // add packet to history _sentPacketHistories[nodeUUID].packetSent(sequence, packet); diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index d11aa55963..7b39a1d785 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -82,13 +82,19 @@ public: // you must override these... virtual char getMyNodeType() const = 0; virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { }; + + bool hasDestinationWalletUUID() const { return _destinationWalletUUID.isNull(); } + void setDestinationWalletUUID(const QUuid& destinationWalletUUID) { _destinationWalletUUID = destinationWalletUUID; } + const QUuid& getDestinationWalletUUID() { return _destinationWalletUUID; } + + void processNackPacket(const QByteArray& packet); public slots: void nodeKilled(SharedNodePointer node); -public: - void processNackPacket(const QByteArray& packet); - +signals: + void octreePaymentRequired(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID); + protected: bool _shouldSend; void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, ssize_t length, qint64 satoshiCost = 0); @@ -118,5 +124,7 @@ protected: // TODO: add locks for this and _pendingEditPackets QHash _sentPacketHistories; QHash _outgoingSequenceNumbers; + + QUuid _destinationWalletUUID; }; #endif // hifi_OctreeEditPacketSender_h From 363cef6d8a6890fb29be98657dbf1c7b794e63fb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 17:33:37 -0700 Subject: [PATCH 44/56] move satoshi costs to VoxelEditPacketSender --- interface/src/Application.cpp | 31 +++++++++++++++++++ interface/src/Application.h | 2 ++ libraries/networking/src/DomainHandler.cpp | 23 +------------- libraries/networking/src/DomainHandler.h | 10 +----- .../voxels/src/VoxelEditPacketSender.cpp | 7 ++--- libraries/voxels/src/VoxelEditPacketSender.h | 7 +++++ 6 files changed, 44 insertions(+), 36 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9b54b84351..480f215d40 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3788,6 +3788,37 @@ void Application::uploadAttachment() { uploadModel(ATTACHMENT_MODEL); } +void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) { + // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed + const QString VOXEL_SETTINGS_KEY = "voxels"; + const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; + const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; + const QString VOXEL_WALLET_UUID = "voxel-wallet"; + + const QJsonObject& voxelObject = domainSettingsObject[VOXEL_SETTINGS_KEY].toObject(); + + qint64 satoshisPerVoxel = 0; + qint64 satoshisPerMeterCubed = 0; + QUuid voxelWalletUUID; + + if (domainSettingsObject.isEmpty()) { + float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble(); + float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble(); + + satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT); + satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT); + + voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString()); + } + + qDebug() << "Voxel costs are" << satoshisPerVoxel << "per voxel and" << satoshisPerMeterCubed << "per meter cubed"; + qDebug() << "Destination wallet UUID for voxel payments is" << voxelWalletUUID; + + _voxelEditSender.setSatoshisPerVoxel(satoshisPerVoxel); + _voxelEditSender.setSatoshisPerMeterCubed(satoshisPerMeterCubed); + _voxelEditSender.setDestinationWalletUUID(voxelWalletUUID); +} + QString Application::getPreviousScriptLocation() { QString suggestedName; if (_previousScriptLocation.isEmpty()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index a356b26725..54fb25839a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -342,6 +342,8 @@ public slots: void uploadAttachment(); void bumpSettings() { ++_numChangedSettings; } + + void domainSettingsReceived(const QJsonObject& domainSettingsObject); private slots: void timer(); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 5f20bebd50..1bcf03f477 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -159,9 +159,7 @@ void DomainHandler::settingsRequestFinished() { _settingsObject = QJsonDocument::fromJson(settingsReply->readAll()).object(); qDebug() << "Received domain settings."; - emit settingsReceived(); - - updateVoxelCosts(); + emit settingsReceived(_settingsObject); // reset failed settings requests to 0, we got them _failedSettingsRequests = 0; @@ -182,25 +180,6 @@ void DomainHandler::settingsRequestFinished() { } } -void DomainHandler::updateVoxelCosts() { - - // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed - const QString VOXEL_SETTINGS_KEY = "voxels"; - const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; - const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits"; - - if (!_settingsObject.isEmpty()) { - float perVoxelCredits = (float) _settingsObject[VOXEL_SETTINGS_KEY].toObject()[PER_VOXEL_COST_KEY].toDouble(); - float perMeterCubedCredits = (float) _settingsObject[VOXEL_SETTINGS_KEY].toObject()[PER_METER_CUBED_COST_KEY].toDouble(); - - _satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT); - _satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT); - } else { - _satoshisPerVoxel = 0; - _satoshisPerMeterCubed = 0; - } -} - void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) { // figure out the port that the DS wants us to use for us to talk to them with DTLS int numBytesPacketHeader = numBytesForPacketHeader(dtlsRequirementPacket); diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 7b7a80b49a..a2b8e2e307 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -62,9 +62,6 @@ public: void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket); - qint64 getSatoshisPerVoxel() const { return _satoshisPerVoxel; } - qint64 getSatoshisPerMeterCubed() const { return _satoshisPerMeterCubed; } - private slots: void completedHostnameLookup(const QHostInfo& hostInfo); void settingsRequestFinished(); @@ -72,14 +69,12 @@ signals: void hostnameChanged(const QString& hostname); void connectedToDomain(const QString& hostname); - void settingsReceived(); + void settingsReceived(const QJsonObject& domainSettingsObject); void settingsReceiveFail(); private: void reset(); - void updateVoxelCosts(); - QUuid _uuid; QString _hostname; HifiSockAddr _sockAddr; @@ -88,9 +83,6 @@ private: QTimer* _handshakeTimer; QJsonObject _settingsObject; int _failedSettingsRequests; - - qint64 _satoshisPerVoxel; - qint64 _satoshisPerMeterCubed; }; #endif // hifi_DomainHandler_h diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index ee189f492d..3bb76d17fe 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -146,15 +146,12 @@ void VoxelEditPacketSender::queueVoxelEditMessages(PacketType type, int numberOf qint64 VoxelEditPacketSender::satoshiCostForMessage(const VoxelDetail& details) { const DomainHandler& domainHandler = NodeList::getInstance()->getDomainHandler(); - qint64 totalSatoshiCost = domainHandler.getSatoshisPerVoxel(); - qint64 costPerMeterCubed = domainHandler.getSatoshisPerMeterCubed(); - - if (totalSatoshiCost == 0 && costPerMeterCubed == 0) { + if (_satoshisPerVoxel == 0 && _satoshisPerMeterCubed == 0) { return 0; } else { float meterScale = details.s * TREE_SCALE; float totalVolume = meterScale * meterScale * meterScale; - return totalSatoshiCost + (qint64) floorf(totalVolume * costPerMeterCubed); + return _satoshisPerVoxel + (qint64) floorf(totalVolume * _satoshisPerMeterCubed); } } diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h index e761812629..560c585ed5 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -51,6 +51,13 @@ public: // My server type is the voxel server virtual char getMyNodeType() const { return NodeType::VoxelServer; } + void setSatoshisPerVoxel(qint64 satoshisPerVoxel) { _satoshisPerVoxel = satoshisPerVoxel; } + void setSatoshisPerMeterCubed(qint64 satoshisPerMeterCubed) { _satoshisPerMeterCubed = satoshisPerMeterCubed; } + qint64 satoshiCostForMessage(const VoxelDetail& details); + +private: + qint64 _satoshisPerVoxel; + qint64 _satoshisPerMeterCubed; }; #endif // hifi_VoxelEditPacketSender_h From 99daa062c017cdc691feae5310b686dd1e902855 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 17:35:33 -0700 Subject: [PATCH 45/56] trigger domainSettingsReceived slot when connected to domain --- interface/src/Application.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 480f215d40..be8e9c240e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -240,6 +240,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&nodeList->getDomainHandler(), SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); + connect(&nodeList->getDomainHandler(), &DomainHandler::settingsReceived, &Application::domainSettingsReceived); // update our location every 5 seconds in the data-server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; From 9c1dd7c4a4ce7b96fc6ebf01edf1f6a46a957873 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 17:35:49 -0700 Subject: [PATCH 46/56] fix call to connect in Application --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index be8e9c240e..d30cd63105 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -240,7 +240,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&nodeList->getDomainHandler(), SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); - connect(&nodeList->getDomainHandler(), &DomainHandler::settingsReceived, &Application::domainSettingsReceived); + connect(&nodeList->getDomainHandler(), &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); // update our location every 5 seconds in the data-server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; From b6c0b4fd218543a7356a6d4c49ba6aa2a2b81477 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 30 Jul 2014 17:39:24 -0700 Subject: [PATCH 47/56] Random mood in procedural bot --- examples/bot_procedural.js. | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/examples/bot_procedural.js. b/examples/bot_procedural.js. index 17e54007cc..00c2829924 100644 --- a/examples/bot_procedural.js. +++ b/examples/bot_procedural.js. @@ -1,5 +1,5 @@ // -// proceduralBot.js +// bot_procedural.js // hifi // // Created by Ben Arnold on 7/29/2013 @@ -154,7 +154,18 @@ function addBlendshapeToPose(pose, shapeIndex, val) { pose.blendShapes[index] = {shapeIndex: shapeIndex, val: val }; } //The mood of the avatar, determines face. 0 = happy, 1 = angry, 2 = sad. -var avatarMood = 0; + +//Randomly pick avatar mood. 80% happy, 10% mad 10% sad +var randMood = Math.floor(Math.random() * 11); +var avatarMood; +if (randMood == 0) { + avatarMood = 1; +} else if (randMood == 2) { + avatarMood = 2; +} else { + avatarMood = 0; +} + var currentExpression = -1; //Face pose constructor var happyPoses = []; From db25cd9d2c6139674e9066f81e54725f6223a0d7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 18:07:08 -0700 Subject: [PATCH 48/56] call PaymentManager when an OctreeEditPacketSender needs to pay --- interface/src/Application.cpp | 17 ++++++++++--- interface/src/PaymentManager.cpp | 24 ++++++++++++++++++ interface/src/PaymentManager.h | 25 +++++++++++++++++++ libraries/octree/src/OctreeEditPacketSender.h | 2 +- 4 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 interface/src/PaymentManager.cpp create mode 100644 interface/src/PaymentManager.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d30cd63105..a099231654 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -69,6 +69,7 @@ #include "InterfaceVersion.h" #include "Menu.h" #include "ModelUploader.h" +#include "PaymentManager.h" #include "Util.h" #include "devices/MIDIManager.h" #include "devices/OculusManager.h" @@ -237,10 +238,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(audioThread, SIGNAL(started()), &_audio, SLOT(start())); audioThread->start(); + + const DomainHandler& domainHandler = nodeList->getDomainHandler(); - connect(&nodeList->getDomainHandler(), SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); - connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); - connect(&nodeList->getDomainHandler(), &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); + connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); + connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); + + // hookup VoxelEditSender to PaymentManager so we can pay for octree edits + const PaymentManager& paymentManager = PaymentManager::getInstance(); + connect(&_voxelEditSender, &VoxelEditPacketSender::octreePaymentRequired, + &paymentManager, &PaymentManager::sendSignedPayment); // update our location every 5 seconds in the data-server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; @@ -3790,6 +3798,7 @@ void Application::uploadAttachment() { } void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) { + // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed const QString VOXEL_SETTINGS_KEY = "voxels"; const QString PER_VOXEL_COST_KEY = "per-voxel-credits"; @@ -3802,7 +3811,7 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject qint64 satoshisPerMeterCubed = 0; QUuid voxelWalletUUID; - if (domainSettingsObject.isEmpty()) { + if (!domainSettingsObject.isEmpty()) { float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble(); float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble(); diff --git a/interface/src/PaymentManager.cpp b/interface/src/PaymentManager.cpp new file mode 100644 index 0000000000..f4d23fca30 --- /dev/null +++ b/interface/src/PaymentManager.cpp @@ -0,0 +1,24 @@ +// +// PaymentManager.cpp +// interface/src +// +// Created by Stephen Birarda on 2014-07-30. +// 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 +#include + +#include "PaymentManager.h" + +PaymentManager& PaymentManager::getInstance() { + static PaymentManager sharedInstance; + return sharedInstance; +} + +void PaymentManager::sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID) { + qDebug() << "Paying" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID; +} \ No newline at end of file diff --git a/interface/src/PaymentManager.h b/interface/src/PaymentManager.h new file mode 100644 index 0000000000..67419a39a4 --- /dev/null +++ b/interface/src/PaymentManager.h @@ -0,0 +1,25 @@ +// +// PaymentManager.h +// interface/src +// +// Created by Stephen Birarda on 2014-07-30. +// 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_PaymentManager_h +#define hifi_PaymentManager_h + +#include + +class PaymentManager : public QObject { + Q_OBJECT +public: + static PaymentManager& getInstance(); +public slots: + void sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID); +}; + +#endif // hifi_PaymentManager_h \ No newline at end of file diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index 7b39a1d785..a11c626003 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -83,7 +83,7 @@ public: virtual char getMyNodeType() const = 0; virtual void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { }; - bool hasDestinationWalletUUID() const { return _destinationWalletUUID.isNull(); } + bool hasDestinationWalletUUID() const { return !_destinationWalletUUID.isNull(); } void setDestinationWalletUUID(const QUuid& destinationWalletUUID) { _destinationWalletUUID = destinationWalletUUID; } const QUuid& getDestinationWalletUUID() { return _destinationWalletUUID; } From ef58453fda85edb51eeb1db4929dd3f228e5b06f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 18:10:11 -0700 Subject: [PATCH 49/56] turn off verbose octree packet debug --- interface/src/PaymentManager.cpp | 2 ++ libraries/octree/src/OctreeEditPacketSender.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/PaymentManager.cpp b/interface/src/PaymentManager.cpp index f4d23fca30..4baf11386b 100644 --- a/interface/src/PaymentManager.cpp +++ b/interface/src/PaymentManager.cpp @@ -21,4 +21,6 @@ PaymentManager& PaymentManager::getInstance() { void PaymentManager::sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID) { qDebug() << "Paying" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID; + + } \ No newline at end of file diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 86208c9fd2..543f47273c 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -112,7 +112,7 @@ void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned c _sentPacketHistories[nodeUUID].packetSent(sequence, packet); // debugging output... - bool wantDebugging = true; + bool wantDebugging = false; if (wantDebugging) { int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast(buffer)); unsigned short int sequence = (*((unsigned short int*)(buffer + numBytesPacketHeader))); From 631977bac2c72b24662ea204c49e47a73087f5a2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Jul 2014 18:15:20 -0700 Subject: [PATCH 50/56] setup a SignedTransaction from PaymentManager --- interface/src/PaymentManager.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/interface/src/PaymentManager.cpp b/interface/src/PaymentManager.cpp index 4baf11386b..b8af43ec67 100644 --- a/interface/src/PaymentManager.cpp +++ b/interface/src/PaymentManager.cpp @@ -9,9 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include +#include "SignedWalletTransaction.h" + #include "PaymentManager.h" PaymentManager& PaymentManager::getInstance() { @@ -22,5 +25,11 @@ PaymentManager& PaymentManager::getInstance() { void PaymentManager::sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID) { qDebug() << "Paying" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID; + // setup a signed wallet transaction + const qint64 DEFAULT_TRANSACTION_EXPIRY_SECONDS = 60; + qint64 currentTimestamp = QDateTime::currentDateTimeUtc().toTime_t(); + SignedWalletTransaction newTransaction(destinationWalletUUID, satoshiAmount, + currentTimestamp, DEFAULT_TRANSACTION_EXPIRY_SECONDS); + // send the signed transaction to the redeeming node } \ No newline at end of file From a65f81c71aa26cfeb0f49a6721cf47d0707d09ed Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 30 Jul 2014 18:25:58 -0700 Subject: [PATCH 51/56] Random start position for bot --- examples/bot_procedural.js. | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/bot_procedural.js. b/examples/bot_procedural.js. index 00c2829924..265b887e0a 100644 --- a/examples/bot_procedural.js. +++ b/examples/bot_procedural.js. @@ -55,8 +55,7 @@ var HEAD_TURN_RATE = 0.05; var PITCH_RANGE = 15.0; var YAW_RANGE = 35.0; -//var firstPosition = { x: getRandomFloat(X_MIN, X_MAX), y: Y_PELVIS, z: getRandomFloat(Z_MIN, Z_MAX) }; -var firstPosition = { x: 0.5, y: Y_PELVIS, z: 0.5 }; +var firstPosition = { x: getRandomFloat(X_MIN, X_MAX), y: Y_PELVIS, z: getRandomFloat(Z_MIN, Z_MAX) }; var targetPosition = { x: 0, y: 0, z: 0 }; var targetOrientation = { x: 0, y: 0, z: 0, w: 0 }; var currentOrientation = { x: 0, y: 0, z: 0, w: 0 }; From 350c75961801fe6a0132ed86e064ecc1c7a73f5c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 31 Jul 2014 09:18:57 -0700 Subject: [PATCH 52/56] add a method to SignedWalletTransaction to return binary message --- interface/src/SignedWalletTransaction.cpp | 8 ++++++-- interface/src/SignedWalletTransaction.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/SignedWalletTransaction.cpp b/interface/src/SignedWalletTransaction.cpp index d29207e4f5..8a0d4e6fdb 100644 --- a/interface/src/SignedWalletTransaction.cpp +++ b/interface/src/SignedWalletTransaction.cpp @@ -32,7 +32,7 @@ SignedWalletTransaction::SignedWalletTransaction(const QUuid& destinationUUID, q } -QByteArray SignedWalletTransaction::hexMessage() { +QByteArray SignedWalletTransaction::binaryMessage() { // build the message using the components of this transaction // UUID, source UUID, destination UUID, message timestamp, expiry delta, amount @@ -49,7 +49,11 @@ QByteArray SignedWalletTransaction::hexMessage() { messageBinary.append(reinterpret_cast(&_amount), sizeof(_amount)); - return messageBinary.toHex(); + return messageBinary; +} + +QByteArray SignedWalletTransaction::hexMessage() { + return binaryMessage().toHex(); } QByteArray SignedWalletTransaction::messageDigest() { diff --git a/interface/src/SignedWalletTransaction.h b/interface/src/SignedWalletTransaction.h index 3b13f73335..6bc66a535e 100644 --- a/interface/src/SignedWalletTransaction.h +++ b/interface/src/SignedWalletTransaction.h @@ -19,6 +19,7 @@ class SignedWalletTransaction : public WalletTransaction { public: SignedWalletTransaction(const QUuid& destinationUUID, qint64 amount, qint64 messageTimestamp, qint64 expiryDelta); + QByteArray binaryMessage(); QByteArray hexMessage(); QByteArray messageDigest(); QByteArray signedMessageDigest(); From ce4336894a1fed5172dda18071334fa200023b75 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 31 Jul 2014 09:26:18 -0700 Subject: [PATCH 53/56] actually send signed transaction from PaymentManager --- interface/src/PaymentManager.cpp | 15 ++++++++++++++- interface/src/SignedWalletTransaction.h | 2 ++ libraries/networking/src/PacketHeaders.h | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/interface/src/PaymentManager.cpp b/interface/src/PaymentManager.cpp index b8af43ec67..84f5e96fc9 100644 --- a/interface/src/PaymentManager.cpp +++ b/interface/src/PaymentManager.cpp @@ -13,6 +13,9 @@ #include #include +#include +#include + #include "SignedWalletTransaction.h" #include "PaymentManager.h" @@ -23,7 +26,6 @@ PaymentManager& PaymentManager::getInstance() { } void PaymentManager::sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUUID, const QUuid& destinationWalletUUID) { - qDebug() << "Paying" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID; // setup a signed wallet transaction const qint64 DEFAULT_TRANSACTION_EXPIRY_SECONDS = 60; @@ -32,4 +34,15 @@ void PaymentManager::sendSignedPayment(qint64 satoshiAmount, const QUuid& nodeUU currentTimestamp, DEFAULT_TRANSACTION_EXPIRY_SECONDS); // send the signed transaction to the redeeming node + QByteArray transactionByteArray = byteArrayWithPopulatedHeader(PacketTypeSignedTransactionPayment); + + // append the binary message and the signed message digest + transactionByteArray.append(newTransaction.binaryMessage()); + transactionByteArray.append(newTransaction.signedMessageDigest()); + + qDebug() << "Paying" << satoshiAmount << "satoshis to" << destinationWalletUUID << "via" << nodeUUID; + + // use the NodeList to send that to the right node + NodeList* nodeList = NodeList::getInstance(); + nodeList->writeDatagram(transactionByteArray, nodeList->nodeWithUUID(nodeUUID)); } \ No newline at end of file diff --git a/interface/src/SignedWalletTransaction.h b/interface/src/SignedWalletTransaction.h index 6bc66a535e..b3b1620dac 100644 --- a/interface/src/SignedWalletTransaction.h +++ b/interface/src/SignedWalletTransaction.h @@ -14,6 +14,8 @@ #include +const int NUM_BYTES_SIGNED_TRANSACTION_MESSAGE = 72; + class SignedWalletTransaction : public WalletTransaction { Q_OBJECT public: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 83350a32d1..436c34e6eb 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -70,6 +70,7 @@ enum PacketType { PacketTypeVoxelEditNack, PacketTypeParticleEditNack, PacketTypeModelEditNack, + PacketTypeSignedTransactionPayment }; typedef char PacketVersion; From eadae8ed1ae224439e57586c69b7dcca8bbe8e2d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 31 Jul 2014 10:05:26 -0700 Subject: [PATCH 54/56] allow unauthenticated redemptions of signed transactions from octree server --- assignment-client/src/octree/OctreeServer.cpp | 52 ++++++ assignment-client/src/octree/OctreeServer.h | 3 + interface/src/SignedWalletTransaction.h | 2 - libraries/networking/src/AccountManager.cpp | 155 ++++++++++-------- libraries/networking/src/AccountManager.h | 10 +- 5 files changed, 154 insertions(+), 68 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 551bed795d..42dc9a047d 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -9,11 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include #include + +#include #include #include #include @@ -244,6 +247,10 @@ OctreeServer::OctreeServer(const QByteArray& packet) : _averageLoopTime.updateAverage(0); qDebug() << "Octree server starting... [" << this << "]"; + + // make sure the AccountManager has an Auth URL for payment redemptions + + AccountManager::getInstance().setAuthURL(DEFAULT_NODE_AUTH_URL); } OctreeServer::~OctreeServer() { @@ -857,6 +864,8 @@ void OctreeServer::readPendingDatagrams() { } } else if (packetType == PacketTypeJurisdictionRequest) { _jurisdictionSender->queueReceivedPacket(matchingNode, receivedPacket); + } else if (packetType == PacketTypeSignedTransactionPayment) { + handleSignedTransactionPayment(packetType, receivedPacket); } else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) { _octreeInboundPacketProcessor->queueReceivedPacket(matchingNode, receivedPacket); } else { @@ -1204,6 +1213,49 @@ QString OctreeServer::getStatusLink() { return result; } +void OctreeServer::handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram) { + // for now we're not verifying that this is actual payment for any octree edits + // just use the AccountManager to send it up to the data server and have it redeemed + AccountManager& accountManager = AccountManager::getInstance(); + + const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE = 72; + const int NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE = 256; + + int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(packetType); + + // pull out the transaction message in binary + QByteArray messageHex = datagram.mid(numBytesPacketHeader, NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE).toHex(); + // pull out the binary signed message digest + QByteArray signatureHex = datagram.mid(numBytesPacketHeader + NUM_BYTES_SIGNED_TRANSACTION_BINARY_MESSAGE, + NUM_BYTES_SIGNED_TRANSACTION_BINARY_SIGNATURE).toHex(); + + // setup the QJSONObject we are posting + QJsonObject postObject; + + const QString TRANSACTION_OBJECT_MESSAGE_KEY = "message"; + const QString TRANSACTION_OBJECT_SIGNATURE_KEY = "signature"; + const QString POST_OBJECT_TRANSACTION_KEY = "transaction"; + + QJsonObject transactionObject; + transactionObject.insert(TRANSACTION_OBJECT_MESSAGE_KEY, QString(messageHex)); + transactionObject.insert(TRANSACTION_OBJECT_SIGNATURE_KEY, QString(signatureHex)); + + postObject.insert(POST_OBJECT_TRANSACTION_KEY, transactionObject); + + // setup our callback params + JSONCallbackParameters callbackParameters; + callbackParameters.jsonCallbackReceiver = this; + callbackParameters.jsonCallbackMethod = "handleSignedTransactionPaymentResponse"; + + accountManager.unauthenticatedRequest("/api/v1/transactions/redeem", QNetworkAccessManager::PostOperation, + callbackParameters, QJsonDocument(postObject).toJson()); + +} + +void OctreeServer::handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject) { + qDebug() << "STP response:" << jsonObject; +} + void OctreeServer::sendStatsPacket() { // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and // send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 76b39c5771..d00af41d5e 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -137,6 +137,9 @@ protected: QString getConfiguration(); QString getStatusLink(); + void handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram); + void handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject); + int _argc; const char** _argv; char** _parsedArgV; diff --git a/interface/src/SignedWalletTransaction.h b/interface/src/SignedWalletTransaction.h index b3b1620dac..6bc66a535e 100644 --- a/interface/src/SignedWalletTransaction.h +++ b/interface/src/SignedWalletTransaction.h @@ -14,8 +14,6 @@ #include -const int NUM_BYTES_SIGNED_TRANSACTION_MESSAGE = 72; - class SignedWalletTransaction : public WalletTransaction { Q_OBJECT public: diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 563d735790..68af384007 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -15,15 +15,15 @@ #include #include #include +#include #include -#include #include "NodeList.h" #include "PacketHeaders.h" #include "AccountManager.h" -const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false; +const bool VERBOSE_HTTP_REQUEST_DEBUGGING = true; AccountManager& AccountManager::getInstance() { static AccountManager sharedInstance; @@ -62,6 +62,8 @@ AccountManager::AccountManager() : qRegisterMetaType("QNetworkAccessManager::Operation"); qRegisterMetaType("JSONCallbackParameters"); + + qRegisterMetaType("QHttpMultiPart*"); connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged); } @@ -142,90 +144,113 @@ void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessMan const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) { + QMetaObject::invokeMethod(this, "invokedRequest", Q_ARG(const QString&, path), + Q_ARG(bool, true), Q_ARG(QNetworkAccessManager::Operation, operation), Q_ARG(const JSONCallbackParameters&, callbackParams), Q_ARG(const QByteArray&, dataByteArray), Q_ARG(QHttpMultiPart*, dataMultiPart)); } -void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::Operation operation, +void AccountManager::unauthenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation, + const JSONCallbackParameters& callbackParams, + const QByteArray& dataByteArray, + QHttpMultiPart* dataMultiPart) { + + QMetaObject::invokeMethod(this, "invokedRequest", + Q_ARG(const QString&, path), + Q_ARG(bool, false), + Q_ARG(QNetworkAccessManager::Operation, operation), + Q_ARG(const JSONCallbackParameters&, callbackParams), + Q_ARG(const QByteArray&, dataByteArray), + Q_ARG(QHttpMultiPart*, dataMultiPart)); +} + +void AccountManager::invokedRequest(const QString& path, + bool requiresAuthentication, + QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) { NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - if (hasValidAccessToken()) { - QNetworkRequest authenticatedRequest; - - QUrl requestURL = _authURL; - - if (path.startsWith("/")) { - requestURL.setPath(path); + QNetworkRequest networkRequest; + + QUrl requestURL = _authURL; + + if (path.startsWith("/")) { + requestURL.setPath(path); + } else { + requestURL.setPath("/" + path); + } + + if (requiresAuthentication) { + if (hasValidAccessToken()) { + requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); } else { - requestURL.setPath("/" + path); + qDebug() << "No valid access token present. Bailing on authenticated invoked request."; + return; } - - requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); - - authenticatedRequest.setUrl(requestURL); - - if (VERBOSE_HTTP_REQUEST_DEBUGGING) { - qDebug() << "Making an authenticated request to" << qPrintable(requestURL.toString()); - - if (!dataByteArray.isEmpty()) { - qDebug() << "The POST/PUT body -" << QString(dataByteArray); - } + } + + networkRequest.setUrl(requestURL); + + if (VERBOSE_HTTP_REQUEST_DEBUGGING) { + qDebug() << "Making an authenticated request to" << qPrintable(requestURL.toString()); + + if (!dataByteArray.isEmpty()) { + qDebug() << "The POST/PUT body -" << QString(dataByteArray); } - - QNetworkReply* networkReply = NULL; - - switch (operation) { - case QNetworkAccessManager::GetOperation: - networkReply = networkAccessManager.get(authenticatedRequest); - break; - case QNetworkAccessManager::PostOperation: - case QNetworkAccessManager::PutOperation: - if (dataMultiPart) { - if (operation == QNetworkAccessManager::PostOperation) { - networkReply = networkAccessManager.post(authenticatedRequest, dataMultiPart); - } else { - networkReply = networkAccessManager.put(authenticatedRequest, dataMultiPart); - } - dataMultiPart->setParent(networkReply); + } + + QNetworkReply* networkReply = NULL; + + switch (operation) { + case QNetworkAccessManager::GetOperation: + networkReply = networkAccessManager.get(networkRequest); + break; + case QNetworkAccessManager::PostOperation: + case QNetworkAccessManager::PutOperation: + if (dataMultiPart) { + if (operation == QNetworkAccessManager::PostOperation) { + networkReply = networkAccessManager.post(networkRequest, dataMultiPart); } else { - authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - if (operation == QNetworkAccessManager::PostOperation) { - networkReply = networkAccessManager.post(authenticatedRequest, dataByteArray); - } else { - networkReply = networkAccessManager.put(authenticatedRequest, dataByteArray); - } + networkReply = networkAccessManager.put(networkRequest, dataMultiPart); } - - break; - case QNetworkAccessManager::DeleteOperation: - networkReply = networkAccessManager.sendCustomRequest(authenticatedRequest, "DELETE"); - break; - default: - // other methods not yet handled - break; - } - - if (networkReply) { - if (!callbackParams.isEmpty()) { - // if we have information for a callback, insert the callbackParams into our local map - _pendingCallbackMap.insert(networkReply, callbackParams); - - if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) { - callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)), - callbackParams.updateSlot.toStdString().c_str()); + dataMultiPart->setParent(networkReply); + } else { + networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + if (operation == QNetworkAccessManager::PostOperation) { + networkReply = networkAccessManager.post(networkRequest, dataByteArray); + } else { + networkReply = networkAccessManager.put(networkRequest, dataByteArray); } } - - // if we ended up firing of a request, hook up to it now - connect(networkReply, SIGNAL(finished()), SLOT(processReply())); + + break; + case QNetworkAccessManager::DeleteOperation: + networkReply = networkAccessManager.sendCustomRequest(networkRequest, "DELETE"); + break; + default: + // other methods not yet handled + break; + } + + if (networkReply) { + if (!callbackParams.isEmpty()) { + // if we have information for a callback, insert the callbackParams into our local map + _pendingCallbackMap.insert(networkReply, callbackParams); + + if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) { + callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)), + callbackParams.updateSlot.toStdString().c_str()); + } } + + // if we ended up firing of a request, hook up to it now + connect(networkReply, SIGNAL(finished()), SLOT(processReply())); } } diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 49a39c1a22..64d62cd1c2 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -47,6 +47,12 @@ public: const JSONCallbackParameters& callbackParams = JSONCallbackParameters(), const QByteArray& dataByteArray = QByteArray(), QHttpMultiPart* dataMultiPart = NULL); + + void unauthenticatedRequest(const QString& path, + QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, + const JSONCallbackParameters& callbackParams = JSONCallbackParameters(), + const QByteArray& dataByteArray = QByteArray(), + QHttpMultiPart* dataMultiPart = NULL); const QUrl& getAuthURL() const { return _authURL; } void setAuthURL(const QUrl& authURL); @@ -88,7 +94,9 @@ private: void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); - Q_INVOKABLE void invokedRequest(const QString& path, QNetworkAccessManager::Operation operation, + Q_INVOKABLE void invokedRequest(const QString& path, + bool requiresAuthentication, + QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart); From e1ba076a711cbe3dc1397f99054cdb2596d795fe Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 31 Jul 2014 11:03:12 -0700 Subject: [PATCH 55/56] complete redemption of signed transactions from octree server --- assignment-client/src/octree/OctreeServer.cpp | 4 +++- assignment-client/src/octree/OctreeServer.h | 3 ++- libraries/networking/src/AccountManager.cpp | 5 +++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 42dc9a047d..02fb26c09f 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1253,7 +1253,9 @@ void OctreeServer::handleSignedTransactionPayment(PacketType packetType, const Q } void OctreeServer::handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject) { - qDebug() << "STP response:" << jsonObject; + // pull the ID to debug the transaction + QString transactionIDString = jsonObject["data"].toObject()["transaction"].toObject()["id"].toString(); + qDebug() << "Redeemed transaction with ID" << transactionIDString << "successfully."; } void OctreeServer::sendStatsPacket() { diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index d00af41d5e..c904b3d86c 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -127,6 +127,8 @@ public slots: void nodeAdded(SharedNodePointer node); void nodeKilled(SharedNodePointer node); void sendStatsPacket(); + + void handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject); protected: void parsePayload(); @@ -138,7 +140,6 @@ protected: QString getStatusLink(); void handleSignedTransactionPayment(PacketType packetType, const QByteArray& datagram); - void handleSignedTransactionPaymentResponse(const QJsonObject& jsonObject); int _argc; const char** _argv; diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 68af384007..7fda9d74c9 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -23,7 +23,7 @@ #include "AccountManager.h" -const bool VERBOSE_HTTP_REQUEST_DEBUGGING = true; +const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false; AccountManager& AccountManager::getInstance() { static AccountManager sharedInstance; @@ -198,7 +198,7 @@ void AccountManager::invokedRequest(const QString& path, networkRequest.setUrl(requestURL); if (VERBOSE_HTTP_REQUEST_DEBUGGING) { - qDebug() << "Making an authenticated request to" << qPrintable(requestURL.toString()); + qDebug() << "Making a request to" << qPrintable(requestURL.toString()); if (!dataByteArray.isEmpty()) { qDebug() << "The POST/PUT body -" << QString(dataByteArray); @@ -301,6 +301,7 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "Received error response from data-server that has no matching callback."; qDebug() << "Error" << requestReply->error() << "-" << requestReply->errorString(); + qDebug() << requestReply->readAll(); } } } From 3c459d8460854b20b8c11ea6e7e61805c004ab8a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 31 Jul 2014 11:12:41 -0700 Subject: [PATCH 56/56] fix include of tgmath for windows --- libraries/networking/src/DomainHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 1bcf03f477..786726b745 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include