diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index dd3050ba4e..b37784cddc 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -46,8 +46,14 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : const QCommandLineOption helpOption = parser.addHelpOption(); - const QCommandLineOption clientTypeOption(ASSIGNMENT_TYPE_OVERRIDE_OPTION, - "run single assignment client of given type", "type"); + QString typeDescription = "run single assignment client of given type\n# | Type\n============================"; + for (Assignment::Type type = Assignment::FirstType; + type != Assignment::AllTypes; + type = static_cast(static_cast(type) + 1)) { + typeDescription.append(QStringLiteral("\n%1 | %2").arg(QString::number(type), Assignment::typeToString(type))); + } + const QCommandLineOption clientTypeOption(ASSIGNMENT_TYPE_OVERRIDE_OPTION, typeDescription, "type"); + parser.addOption(clientTypeOption); const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name"); diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 995a5bad27..ecdf14ebec 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -477,7 +477,7 @@ void EntityServer::startDynamicDomainVerification() { QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); QJsonObject request; request["certificate_id"] = i.key(); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c2fe3af7c1..290f4a7f53 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -94,7 +94,7 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection, root.insert(requestSubobjectKey, subobject); QJsonDocument doc { root }; - QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + metaversePath }; + QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + metaversePath }; QNetworkRequest req(url); req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); @@ -420,7 +420,7 @@ bool DomainServer::optionallySetupOAuth() { // if we don't have an oauth provider URL then we default to the default node auth url if (_oauthProviderURL.isEmpty()) { - _oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL; + _oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL(); } auto accountManager = DependencyManager::get(); @@ -945,7 +945,7 @@ void DomainServer::createStaticAssignmentsForType(Assignment::Type type, const Q void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet& excludedTypes) { // enumerate over all assignment types and see if we've already excluded it - for (Assignment::Type defaultedType = Assignment::AudioMixerType; + for (Assignment::Type defaultedType = Assignment::FirstType; defaultedType != Assignment::AllTypes; defaultedType = static_cast(static_cast(defaultedType) + 1)) { if (!excludedTypes.contains(defaultedType) && defaultedType != Assignment::AgentType) { @@ -2159,7 +2159,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url QJsonDocument doc(root); - QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/api/v1/places/" + place_id }; + QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/v1/places/" + place_id }; url.setQuery("access_token=" + accessTokenVariant->toString()); diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index f8bf1f62ae..3cf1e1450e 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -208,7 +208,7 @@ void IceServer::requestDomainPublicKey(const QUuid& domainID) { // send a request to the metaverse API for the public key for this domain auto& networkAccessManager = NetworkAccessManager::getInstance(); - QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL }; + QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL() }; QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID)); publicKeyURL.setPath(publicKeyPath); diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 300bcd46f0..0e2f2a5282 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -52,7 +52,11 @@ Item { targetHeight += hifi.dimensions.contentSpacing.y + additionalInformation.height } - parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)); + var newWidth = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)); + if(!isNaN(newWidth)) { + parent.width = root.width = newWidth; + } + parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y); } diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index a058f32994..c9e5d2aaec 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -59,6 +59,7 @@ Rectangle { } else if (walletStatus === 2) { if (root.activeView !== "passphraseModal") { root.activeView = "passphraseModal"; + UserActivityLogger.commercePassphraseEntry("marketplace checkout"); } } else if (walletStatus === 3) { authSuccessStep(); diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index ff9ce16e93..5366bda4d6 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -54,6 +54,7 @@ Rectangle { } else if (walletStatus === 2) { if (root.activeView !== "passphraseModal") { root.activeView = "passphraseModal"; + UserActivityLogger.commercePassphraseEntry("marketplace purchases"); } } else if (walletStatus === 3) { if ((Settings.getValue("isFirstUseOfPurchases", true) || root.isDebuggingFirstUseTutorial) && root.activeView !== "firstUseTutorial") { diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml index a75d511793..91d2ab9f7f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseChange.qml @@ -25,10 +25,6 @@ Item { id: root; - SecurityImageModel { - id: securityImageModel; - } - // Username Text RalewayRegular { id: usernameText; diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index 582052c4c3..82262ebf0a 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -50,8 +50,10 @@ Item { submitPassphraseInputButton.enabled = true; if (!isAuthenticated) { errorText.text = "Authentication failed - please try again."; + UserActivityLogger.commercePassphraseAuthenticationStatus("auth failure"); } else { - sendSignalToParent({method: 'authSuccess'});; + sendSignalToParent({method: 'authSuccess'}); + UserActivityLogger.commercePassphraseAuthenticationStatus("auth success"); } } } @@ -336,6 +338,7 @@ Item { text: "Cancel" onClicked: { sendSignalToParent({method: 'passphrasePopup_cancelClicked'}); + UserActivityLogger.commercePassphraseAuthenticationStatus("passphrase modal cancelled"); } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml index 7f767060f6..050b60159f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageChange.qml @@ -26,10 +26,6 @@ Item { id: root; property bool justSubmitted: false; - SecurityImageModel { - id: securityImageModel; - } - Hifi.QmlCommerce { id: commerce; @@ -213,4 +209,8 @@ Item { securityImageSubmitButton.enabled = true; securityImageSubmitButton.text = "Submit"; } + + function initModel() { + securityImageSelection.initModel(); + } } diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml index 7b1434aa3c..b8e9db09ab 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageModel.qml @@ -15,29 +15,28 @@ import QtQuick 2.5 ListModel { id: root; - ListElement{ - sourcePath: "images/01.jpg" - securityImageEnumValue: 1; + + function initModel() { + var array = []; + while (array.length < 6) { + // We currently have 34 security images to choose from + var randomNumber = Math.floor(Math.random() * 34) + 1; + if (array.indexOf(randomNumber) > -1) { + continue; + } + array[array.length] = randomNumber; + } + + var modelElement; + + for (var i = 0; i < 6; i++) { + modelElement = { "sourcePath":"images/" + addLeadingZero(array[i]) + ".jpg", "securityImageEnumValue": (i + 1) } + root.insert(i, modelElement); + } } - ListElement{ - sourcePath: "images/02.jpg" - securityImageEnumValue: 2; - } - ListElement{ - sourcePath: "images/03.jpg" - securityImageEnumValue: 3; - } - ListElement{ - sourcePath: "images/04.jpg" - securityImageEnumValue: 4; - } - ListElement{ - sourcePath: "images/05.jpg" - securityImageEnumValue: 5; - } - ListElement{ - sourcePath: "images/06.jpg" - securityImageEnumValue: 6; + + function addLeadingZero(n) { + return n < 10 ? '0' + n : '' + n; } function getImagePathFromImageID(imageID) { diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml index e12332cd0c..1f5e67eaa5 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml @@ -95,6 +95,10 @@ Item { function getSelectedImageIndex() { return gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue; } + + function initModel() { + gridModel.initModel(); + } // // FUNCTION DEFINITIONS END // diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index ac05bf7c84..5d269dc5af 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -57,6 +57,7 @@ Rectangle { } else if (walletStatus === 2) { if (root.activeView !== "passphraseModal") { root.activeView = "passphraseModal"; + UserActivityLogger.commercePassphraseEntry("wallet app"); } } else if (walletStatus === 3) { root.activeView = "walletHome"; @@ -82,10 +83,6 @@ Rectangle { } } - SecurityImageModel { - id: securityImageModel; - } - HifiCommerceCommon.CommerceLightbox { id: lightboxPopup; visible: false; @@ -342,6 +339,7 @@ Rectangle { passphraseChange.clearPassphraseFields(); passphraseChange.resetSubmitButton(); } else if (msg.method === 'walletSecurity_changeSecurityImage') { + securityImageChange.initModel(); root.activeView = "securityImageChange"; } } diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index d7859d2800..1f9b56ca22 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -236,6 +236,7 @@ Item { height: 50; text: "Set Up Wallet"; onClicked: { + securityImageSelection.initModel(); root.activeView = "step_2"; } } diff --git a/interface/resources/qml/hifi/commerce/wallet/images/01.jpg b/interface/resources/qml/hifi/commerce/wallet/images/01.jpg index 821be6c584..199920a32d 100644 Binary files a/interface/resources/qml/hifi/commerce/wallet/images/01.jpg and b/interface/resources/qml/hifi/commerce/wallet/images/01.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/02.jpg b/interface/resources/qml/hifi/commerce/wallet/images/02.jpg index cfcd3e38bf..821be6c584 100644 Binary files a/interface/resources/qml/hifi/commerce/wallet/images/02.jpg and b/interface/resources/qml/hifi/commerce/wallet/images/02.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/04.jpg b/interface/resources/qml/hifi/commerce/wallet/images/04.jpg index 82e3e64be3..1acedf8b10 100644 Binary files a/interface/resources/qml/hifi/commerce/wallet/images/04.jpg and b/interface/resources/qml/hifi/commerce/wallet/images/04.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/05.jpg b/interface/resources/qml/hifi/commerce/wallet/images/05.jpg index c6ce0ac0a1..da73727dea 100644 Binary files a/interface/resources/qml/hifi/commerce/wallet/images/05.jpg and b/interface/resources/qml/hifi/commerce/wallet/images/05.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/06.jpg b/interface/resources/qml/hifi/commerce/wallet/images/06.jpg index 1acedf8b10..bbd0399d3f 100644 Binary files a/interface/resources/qml/hifi/commerce/wallet/images/06.jpg and b/interface/resources/qml/hifi/commerce/wallet/images/06.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/07.jpg b/interface/resources/qml/hifi/commerce/wallet/images/07.jpg new file mode 100644 index 0000000000..e5f86a4a80 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/07.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/08.jpg b/interface/resources/qml/hifi/commerce/wallet/images/08.jpg new file mode 100644 index 0000000000..9d49866c91 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/08.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/09.jpg b/interface/resources/qml/hifi/commerce/wallet/images/09.jpg new file mode 100644 index 0000000000..a1c31bce7d Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/09.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/10.jpg b/interface/resources/qml/hifi/commerce/wallet/images/10.jpg new file mode 100644 index 0000000000..1f6fc1de27 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/10.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/11.jpg b/interface/resources/qml/hifi/commerce/wallet/images/11.jpg new file mode 100644 index 0000000000..37c3b937a5 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/11.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/12.jpg b/interface/resources/qml/hifi/commerce/wallet/images/12.jpg new file mode 100644 index 0000000000..39f653630c Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/12.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/13.jpg b/interface/resources/qml/hifi/commerce/wallet/images/13.jpg new file mode 100644 index 0000000000..3ae86be465 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/13.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/14.jpg b/interface/resources/qml/hifi/commerce/wallet/images/14.jpg new file mode 100644 index 0000000000..8de661e58e Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/14.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/15.jpg b/interface/resources/qml/hifi/commerce/wallet/images/15.jpg new file mode 100644 index 0000000000..f0ac801b89 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/15.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/16.jpg b/interface/resources/qml/hifi/commerce/wallet/images/16.jpg new file mode 100644 index 0000000000..f7c32a696b Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/16.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/17.jpg b/interface/resources/qml/hifi/commerce/wallet/images/17.jpg new file mode 100644 index 0000000000..75e811ee36 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/17.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/18.jpg b/interface/resources/qml/hifi/commerce/wallet/images/18.jpg new file mode 100644 index 0000000000..e4ca0b6f28 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/18.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/19.jpg b/interface/resources/qml/hifi/commerce/wallet/images/19.jpg new file mode 100644 index 0000000000..4c25f3dda2 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/19.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/20.jpg b/interface/resources/qml/hifi/commerce/wallet/images/20.jpg new file mode 100644 index 0000000000..a5a4565a87 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/20.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/21.jpg b/interface/resources/qml/hifi/commerce/wallet/images/21.jpg new file mode 100644 index 0000000000..3747c9558e Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/21.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/22.jpg b/interface/resources/qml/hifi/commerce/wallet/images/22.jpg new file mode 100644 index 0000000000..66003db9c7 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/22.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/23.jpg b/interface/resources/qml/hifi/commerce/wallet/images/23.jpg new file mode 100644 index 0000000000..72e02c6db0 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/23.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/24.jpg b/interface/resources/qml/hifi/commerce/wallet/images/24.jpg new file mode 100644 index 0000000000..e481a587a5 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/24.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/25.jpg b/interface/resources/qml/hifi/commerce/wallet/images/25.jpg new file mode 100644 index 0000000000..1300edf984 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/25.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/26.jpg b/interface/resources/qml/hifi/commerce/wallet/images/26.jpg new file mode 100644 index 0000000000..9e3aaf9383 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/26.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/27.jpg b/interface/resources/qml/hifi/commerce/wallet/images/27.jpg new file mode 100644 index 0000000000..f64c0c6a3d Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/27.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/28.jpg b/interface/resources/qml/hifi/commerce/wallet/images/28.jpg new file mode 100644 index 0000000000..8f1580ab21 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/28.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/29.jpg b/interface/resources/qml/hifi/commerce/wallet/images/29.jpg new file mode 100644 index 0000000000..fdfb4438ec Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/29.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/30.jpg b/interface/resources/qml/hifi/commerce/wallet/images/30.jpg new file mode 100644 index 0000000000..785ea5a999 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/30.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/31.jpg b/interface/resources/qml/hifi/commerce/wallet/images/31.jpg new file mode 100644 index 0000000000..06dc95afab Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/31.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/32.jpg b/interface/resources/qml/hifi/commerce/wallet/images/32.jpg new file mode 100644 index 0000000000..b987c44e13 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/32.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/33.jpg b/interface/resources/qml/hifi/commerce/wallet/images/33.jpg new file mode 100644 index 0000000000..19b05a5f73 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/33.jpg differ diff --git a/interface/resources/qml/hifi/commerce/wallet/images/34.jpg b/interface/resources/qml/hifi/commerce/wallet/images/34.jpg new file mode 100644 index 0000000000..39f2a5f4ce Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/images/34.jpg differ diff --git a/interface/resources/qml/js/Utils.jsc b/interface/resources/qml/js/Utils.jsc new file mode 100644 index 0000000000..ab20e996b9 Binary files /dev/null and b/interface/resources/qml/js/Utils.jsc differ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0176acf108..e93a17553e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -971,7 +971,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // set the account manager's root URL and trigger a login request if we don't have the access token accountManager->setIsAgent(true); - accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); + accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL()); auto addressManager = DependencyManager::get(); @@ -1496,6 +1496,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } }); + connect(getEntities()->getTree().get(), &EntityTree::deletingEntity, [=](const EntityItemID& entityItemID) { + auto avatarManager = DependencyManager::get(); + auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr; + if (myAvatar) { + myAvatar->clearAvatarEntity(entityItemID); + } + }); + // Keyboard focus handling for Web overlays. auto overlays = &(qApp->getOverlays()); connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) { @@ -3222,8 +3230,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } } - - void Application::keyReleaseEvent(QKeyEvent* event) { _keysPressed.remove(event->key()); @@ -4848,8 +4854,7 @@ void Application::update(float deltaTime) { if (_physicsEnabled) { { PROFILE_RANGE(simulation_physics, "PreStep"); - - PerformanceTimer perfTimer("updateStates)"); + PerformanceTimer perfTimer("preStep)"); static VectorOfMotionStates motionStates; _entitySimulation->getObjectsToRemoveFromPhysics(motionStates); _physicsEngine->removeObjects(motionStates); @@ -4882,22 +4887,22 @@ void Application::update(float deltaTime) { } { PROFILE_RANGE(simulation_physics, "Step"); - PerformanceTimer perfTimer("stepSimulation"); + PerformanceTimer perfTimer("step"); getEntities()->getTree()->withWriteLock([&] { _physicsEngine->stepSimulation(); }); } { PROFILE_RANGE(simulation_physics, "PostStep"); - PerformanceTimer perfTimer("harvestChanges"); + PerformanceTimer perfTimer("postStep"); if (_physicsEngine->hasOutgoingChanges()) { // grab the collision events BEFORE handleOutgoingChanges() because at this point // we have a better idea of which objects we own or should own. auto& collisionEvents = _physicsEngine->getCollisionEvents(); getEntities()->getTree()->withWriteLock([&] { - PROFILE_RANGE(simulation_physics, "Harvest"); - PerformanceTimer perfTimer("handleOutgoingChanges"); + PROFILE_RANGE(simulation_physics, "HandleChanges"); + PerformanceTimer perfTimer("handleChanges"); const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); _entitySimulation->handleChangedMotionStates(outgoingChanges); @@ -4908,17 +4913,15 @@ void Application::update(float deltaTime) { }); if (!_aboutToQuit) { - // handleCollisionEvents() AFTER handleOutgoinChanges() + // handleCollisionEvents() AFTER handleOutgoingChanges() { PROFILE_RANGE(simulation_physics, "CollisionEvents"); - PerformanceTimer perfTimer("entities"); avatarManager->handleCollisionEvents(collisionEvents); // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk // deadlock.) _entitySimulation->handleCollisionEvents(collisionEvents); } - PROFILE_RANGE(simulation_physics, "UpdateEntities"); // NOTE: the getEntities()->update() call below will wait for lock // and will simulate entity motion (the EntityTree has been given an EntitySimulation). getEntities()->update(true); // update the models... @@ -4929,7 +4932,8 @@ void Application::update(float deltaTime) { myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); } - if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && + if (PerformanceTimer::isActive() && + Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) { _physicsEngine->harvestPerformanceStats(); } @@ -7506,4 +7510,9 @@ void Application::setAvatarOverrideUrl(const QUrl& url, bool save) { _avatarOverrideUrl = url; _saveAvatarOverrideUrl = save; } + +void Application::saveNextPhysicsStats(QString filename) { + _physicsEngine->saveNextPhysicsStats(filename); +} + #include "Application.moc" diff --git a/interface/src/Application.h b/interface/src/Application.h index 9542c5ccb6..ee16740f20 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -280,6 +280,7 @@ public: void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; } QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; } bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; } + void saveNextPhysicsStats(QString filename); signals: void svoImportRequested(const QString& url); @@ -432,6 +433,7 @@ private slots: void handleSandboxStatus(QNetworkReply* reply); void switchDisplayMode(); + private: static void initDisplay(); void init(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9bbb72357b..2d15134b70 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -645,7 +645,8 @@ Menu::Menu() { // Developer > Timing >>> MenuWrapper* timingMenu = developerMenu->addMenu("Timing"); MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer"); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false, + qApp, SLOT(enablePerfStats(bool))); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c874205611..02a1959a95 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -81,6 +81,7 @@ const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amaz const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; +const float MIN_SCALE_CHANGED_DELTA = 0.001f; MyAvatar::MyAvatar(QThread* thread) : Avatar(thread), @@ -670,6 +671,11 @@ void MyAvatar::updateSensorToWorldMatrix() { glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), getWorldOrientation(), getWorldPosition()); _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); + bool hasSensorToWorldScaleChanged = false; + if (fabsf(getSensorToWorldScale() - sensorToWorldScale) > MIN_SCALE_CHANGED_DELTA) { + hasSensorToWorldScaleChanged = true; + } + lateUpdatePalms(); if (_enableDebugDrawSensorToWorldMatrix) { @@ -678,9 +684,13 @@ void MyAvatar::updateSensorToWorldMatrix() { } _sensorToWorldMatrixCache.set(_sensorToWorldMatrix); - updateJointFromController(controller::Action::LEFT_HAND, _controllerLeftHandMatrixCache); updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache); + + if (hasSensorToWorldScaleChanged) { + emit sensorToWorldScaleChanged(sensorToWorldScale); + } + } // Update avatar head rotation with sensor data @@ -1405,6 +1415,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene()); _headBoneSet.clear(); emit skeletonChanged(); + } @@ -1440,6 +1451,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN UserActivityLogger::getInstance().changedModel("skeleton", urlString); } markIdentityDataChanged(); + } void MyAvatar::setAttachmentData(const QVector& attachmentData) { diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index a707031167..f249be33ea 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -9,9 +9,12 @@ #include "MySkeletonModel.h" #include +#include #include "Application.h" #include "InterfaceLogging.h" +#include "AnimUtil.h" + MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent) : SkeletonModel(owningAvatar, parent) { } @@ -30,6 +33,39 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle }; } +static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { + glm::mat4 hipsMat = myAvatar->deriveBodyFromHMDSensor(); + glm::vec3 hipsPos = extractTranslation(hipsMat); + glm::quat hipsRot = glmExtractRotation(hipsMat); + + glm::mat4 avatarToWorldMat = myAvatar->getTransform().getMatrix(); + glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); + glm::mat4 avatarToSensorMat = worldToSensorMat * avatarToWorldMat; + + // dampen hips rotation, by mixing it with the avatar orientation in sensor space + const float MIX_RATIO = 0.5f; + hipsRot = safeLerp(glmExtractRotation(avatarToSensorMat), hipsRot, MIX_RATIO); + + if (isFlying) { + // rotate the hips back to match the flying animation. + + const float TILT_ANGLE = 0.523f; + const glm::quat tiltRot = glm::angleAxis(TILT_ANGLE, transformVectorFast(avatarToSensorMat, -Vectors::UNIT_X)); + + glm::vec3 headPos; + int headIndex = myAvatar->getJointIndex("Head"); + if (headIndex != -1) { + headPos = transformPoint(avatarToSensorMat, myAvatar->getAbsoluteJointTranslationInObjectFrame(headIndex)); + } else { + headPos = transformPoint(myAvatar->getSensorToWorldMatrix(), myAvatar->getHMDSensorPosition()); + } + hipsRot = tiltRot * hipsRot; + hipsPos = headPos + tiltRot * (hipsPos - headPos); + } + + return AnimPose(hipsRot * Quaternions::Y_180, hipsPos); +} + // Called within Model::simulate call, below. void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const FBXGeometry& geometry = getFBXGeometry(); @@ -124,6 +160,39 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { } } + // if hips are not under direct control, estimate the hips position. + if (avatarHeadPose.isValid() && !params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips]) { + bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS); + + if (!_prevHipsValid) { + AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying); + _prevHips = hips; + } + + AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying); + + // smootly lerp hips, in sensorframe, with different coeff for horiz and vertical translation. + const float ROT_ALPHA = 0.9f; + const float TRANS_HORIZ_ALPHA = 0.9f; + const float TRANS_VERT_ALPHA = 0.1f; + float hipsY = hips.trans().y; + hips.trans() = lerp(hips.trans(), _prevHips.trans(), TRANS_HORIZ_ALPHA); + hips.trans().y = lerp(hipsY, _prevHips.trans().y, TRANS_VERT_ALPHA); + hips.rot() = safeLerp(hips.rot(), _prevHips.rot(), ROT_ALPHA); + + _prevHips = hips; + _prevHipsValid = true; + + glm::mat4 invRigMat = glm::inverse(myAvatar->getTransform().getMatrix() * Matrices::Y_180); + AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix()); + + params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips; + params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips] = true; + + } else { + _prevHipsValid = false; + } + params.isTalking = head->getTimeWithoutTalking() <= 1.5f; // pass detailed torso k-dops to rig. diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h index ad0ae1b8e9..d9f57a439a 100644 --- a/interface/src/avatar/MySkeletonModel.h +++ b/interface/src/avatar/MySkeletonModel.h @@ -25,6 +25,9 @@ public: private: void updateFingers(); + + AnimPose _prevHips; // sensor frame + bool _prevHipsValid { false }; }; #endif // hifi_MySkeletonModel_h diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index b0d293584c..3257a634c7 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -130,7 +130,7 @@ QString amountString(const QString& label, const QString&color, const QJsonValue return result + QString(""); } -static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/"; +static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/"; void Ledger::historySuccess(QNetworkReply& reply) { // here we send a historyResult with some extra stuff in it // Namely, the styled text we'd like to show. The issue is the diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 69914e97a4..00941d6c50 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -315,7 +315,6 @@ Wallet::Wallet() { } walletScriptingInterface->setWalletStatus(status); - emit walletStatusResult(status); }); auto accountManager = DependencyManager::get(); @@ -491,6 +490,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { } if (_publicKeys.count() > 0) { // we _must_ be authenticated if the publicKeys are there + DependencyManager::get()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY); return true; } @@ -503,6 +503,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { // be sure to add the public key so we don't do this over and over _publicKeys.push_back(publicKey.toBase64()); + DependencyManager::get()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY); return true; } } @@ -801,15 +802,12 @@ void Wallet::account() { void Wallet::getWalletStatus() { auto walletScriptingInterface = DependencyManager::get(); - uint status; if (DependencyManager::get()->isLoggedIn()) { // This will set account info for the wallet, allowing us to decrypt and display the security image. account(); } else { - status = (uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN; - emit walletStatusResult(status); - walletScriptingInterface->setWalletStatus(status); + walletScriptingInterface->setWalletStatus((uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN); return; } } diff --git a/interface/src/networking/CloseEventSender.cpp b/interface/src/networking/CloseEventSender.cpp index de8bd897b2..fe939afe05 100644 --- a/interface/src/networking/CloseEventSender.cpp +++ b/interface/src/networking/CloseEventSender.cpp @@ -28,7 +28,7 @@ QNetworkRequest createNetworkRequest() { QNetworkRequest request; - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath(USER_ACTIVITY_URL); request.setUrl(requestURL); diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 21d257048c..5afbc2058a 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -23,10 +23,10 @@ static const float WEB_STYLUS_LENGTH = 0.2f; static const float TABLET_MIN_HOVER_DISTANCE = -0.1f; static const float TABLET_MAX_HOVER_DISTANCE = 0.1f; static const float TABLET_MIN_TOUCH_DISTANCE = -0.1f; -static const float TABLET_MAX_TOUCH_DISTANCE = 0.01f; +static const float TABLET_MAX_TOUCH_DISTANCE = 0.005f; static const float HOVER_HYSTERESIS = 0.01f; -static const float TOUCH_HYSTERESIS = 0.02f; +static const float TOUCH_HYSTERESIS = 0.001f; static const float STYLUS_MOVE_DELAY = 0.33f * USECS_PER_SECOND; static const float TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481f; diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index d4b4ba1480..74a15db0ce 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -11,11 +11,12 @@ #include #include +#include #include #include -#include -#include #include +#include +#include #include "Application.h" @@ -141,6 +142,15 @@ void TestScriptingInterface::endTraceEvent(QString name) { tracing::traceEvent(trace_test(), name, tracing::DurationEnd); } +void TestScriptingInterface::savePhysicsSimulationStats(QString originalPath) { + QString path = FileUtils::replaceDateTimeTokens(originalPath); + path = FileUtils::computeDocumentPath(path); + if (!FileUtils::canCreateFile(path)) { + return; + } + qApp->saveNextPhysicsStats(path); +} + void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) { PROFILE_RANGE(script, name); fn.call(); diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 73b8f0ac93..aca07d110b 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -71,6 +71,11 @@ public slots: void endTraceEvent(QString name); + /**jsdoc + * Write detailed timing stats of next physics stepSimulation() to filename + */ + void savePhysicsSimulationStats(QString filename); + Q_INVOKABLE void profileRange(const QString& name, QScriptValue function); private: diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 8b4279af02..71a7076bdf 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -22,3 +22,8 @@ void WalletScriptingInterface::refreshWalletStatus() { auto wallet = DependencyManager::get(); wallet->getWalletStatus(); } + +void WalletScriptingInterface::setWalletStatus(const uint& status) { + _walletStatus = status; + emit DependencyManager::get()->walletStatusResult(status); +} \ No newline at end of file diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index d7f9d9242e..5469e732c7 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -39,7 +39,9 @@ public: Q_INVOKABLE void refreshWalletStatus(); Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } - void setWalletStatus(const uint& status) { _walletStatus = status; } + // setWalletStatus() should never be made Q_INVOKABLE. If it were, + // scripts could cause the Wallet to incorrectly report its status. + void setWalletStatus(const uint& status); signals: void walletStatusChanged(); diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index cab533cce3..66f208ca90 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -30,7 +30,7 @@ public: bool forwardEnabled() { return _forwardEnabled; } bool useFeed() { return _useFeed; } void setUseFeed(bool useFeed) { if (_useFeed != useFeed) { _useFeed = useFeed; emit useFeedChanged(); } } - QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL.toString(); } + QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL().toString(); } signals: void backEnabledChanged(); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index e194551add..f991420fe8 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -78,6 +78,8 @@ bool Stats::includeTimingRecord(const QString& name) { return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); } else if (name.startsWith("/paintGL/")) { return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); + } else if (name.startsWith("step/")) { + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming); } return true; } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index de644f165b..c41663fcc9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -289,7 +289,7 @@ void ContextOverlayInterface::openInspectionCertificate() { QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); QJsonObject request; request["certificate_id"] = entityProperties.getCertificateID(); @@ -359,7 +359,7 @@ void ContextOverlayInterface::openInspectionCertificate() { } } -static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/"; +static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 78aa1f4ba8..44745c5c2d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1641,9 +1641,17 @@ void Rig::initAnimGraph(const QUrl& url) { // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); _animLoading = true; - connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) { + std::weak_ptr weakSkeletonPtr = _animSkeleton; + connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) { _animNode = nodeIn; - _animNode->setSkeleton(_animSkeleton); + + // abort load if the previous skeleton was deleted. + auto sharedSkeletonPtr = weakSkeletonPtr.lock(); + if (!sharedSkeletonPtr) { + return; + } + + _animNode->setSkeleton(sharedSkeletonPtr); if (_userAnimState.clipNodeEnum != UserAnimState::None) { // restore the user animation we had before reset. @@ -1651,6 +1659,7 @@ void Rig::initAnimGraph(const QUrl& url) { _userAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f }; overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame); } + // restore the role animations we had before reset. for (auto& roleAnimState : _roleAnimStates) { auto roleState = roleAnimState.second; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e738ad1c19..1ec4d9527f 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -31,7 +31,7 @@ class AnimInverseKinematics; // Rig instances are reentrant. // However only specific methods thread-safe. Noted below. -class Rig : public QObject, public std::enable_shared_from_this { +class Rig : public QObject { Q_OBJECT public: struct StateHandler { diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 476a8d4d88..672c0b69b3 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -96,9 +96,13 @@ void SoundProcessor::run() { QByteArray outputAudioByteArray; int sampleRate = interpretAsWav(rawAudioByteArray, outputAudioByteArray); - if (sampleRate != 0) { - downSample(outputAudioByteArray, sampleRate); + if (sampleRate == 0) { + qCDebug(audio) << "Unsupported WAV file type"; + emit onError(300, "Failed to load sound file, reason: unsupported WAV file type"); + return; } + + downSample(outputAudioByteArray, sampleRate); } else if (fileName.endsWith(RAW_EXTENSION)) { // check if this was a stereo raw file // since it's raw the only way for us to know that is if the file was called .stereo.raw diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 5f7899ae74..8c3b56d89e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -251,10 +251,10 @@ void EntityTreeRenderer::shutdown() { } void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction) { - PROFILE_RANGE_EX(simulation_physics, "Add", 0xffff00ff, (uint64_t)_entitiesToAdd.size()); + PROFILE_RANGE_EX(simulation_physics, "AddToScene", 0xffff00ff, (uint64_t)_entitiesToAdd.size()); PerformanceTimer pt("add"); - // Clear any expired entities - // FIXME should be able to use std::remove_if, but it fails due to some + // Clear any expired entities + // FIXME should be able to use std::remove_if, but it fails due to some // weird compilation error related to EntityItemID assignment operators for (auto itr = _entitiesToAdd.begin(); _entitiesToAdd.end() != itr; ) { if (itr->second.expired()) { @@ -272,7 +272,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r continue; } - // Path to the parent transforms is not valid, + // Path to the parent transforms is not valid, // don't add to the scene graph yet if (!entity->isParentPathComplete()) { continue; @@ -296,7 +296,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r } void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction) { - PROFILE_RANGE_EX(simulation_physics, "Change", 0xffff00ff, (uint64_t)_changedEntities.size()); + PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size()); PerformanceTimer pt("change"); std::unordered_set changedEntities; _changedEntitiesGuard.withWriteLock([&] { @@ -402,6 +402,8 @@ void EntityTreeRenderer::update(bool simulate) { PerformanceTimer perfTimer("ETRupdate"); if (_tree && !_shuttingDown) { EntityTreePointer tree = std::static_pointer_cast(_tree); + + // here we update _currentFrame and _lastAnimated and sync with the server properties. tree->update(simulate); // Update the rendereable entities as needed @@ -736,7 +738,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { PickRay ray = _viewState->computePickRay(event->x(), event->y()); RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); if (rayPickResult.intersects && rayPickResult.entity) { - //qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; + // qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); PointerEvent pointerEvent(PointerEvent::Release, PointerManager::MOUSE_POINTER_ID, diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index c992eb5dc4..e578e4858d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -63,13 +63,17 @@ bool ModelEntityWrapper::isModelLoaded() const { EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()), [](EntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; } RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityItemID, bool dimensionsInitialized) : ModelEntityWrapper(entityItemID), _dimensionsInitialized(dimensionsInitialized) { + + } RenderableModelEntityItem::~RenderableModelEntityItem() { } @@ -464,7 +468,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { shapeInfo.setParams(type, dimensions, getCompoundShapeURL()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { // TODO: assert we never fall in here when model not fully loaded - //assert(_model && _model->isLoaded()); + // assert(_model && _model->isLoaded()); updateModelBounds(); model->updateGeometry(); @@ -974,9 +978,6 @@ void ModelEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entit entity->setModel({}); } -bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { - return !(a == b); -} void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { if (!_animation || !_animation->isLoaded()) { @@ -991,19 +992,12 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - if (!_lastAnimated) { - _lastAnimated = usecTimestampNow(); - return; - } - - auto now = usecTimestampNow(); - auto interval = now - _lastAnimated; - _lastAnimated = now; - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); - { - int animationCurrentFrame = (int)(glm::floor(_currentFrame)) % frameCount; + // the current frame is set on the server in update() in ModelEntityItem.cpp + int animationCurrentFrame = (int)(glm::floor(entity->getAnimationCurrentFrame())); + + // in the case where the last frame is greater than the framecount then clamp + // it to the end of the animation until it loops around. if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) { animationCurrentFrame = 0; } @@ -1039,10 +1033,10 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { glm::mat4 translationMat; if (allowTranslation) { - if(index < translations.size()){ + if (index < translations.size()) { translationMat = glm::translate(translations[index]); } - } else if (index < animationJointNames.size()){ + } else if (index < animationJointNames.size()) { QString jointName = fbxJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation if (originalFbxIndices.contains(jointName)) { @@ -1317,25 +1311,17 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (model->getRenderItemsNeedUpdate()) { model->updateRenderItems(); } - - { - DETAILED_PROFILE_RANGE(simulation_physics, "CheckAnimation"); - // make a copy of the animation properites - auto newAnimationProperties = entity->getAnimationProperties(); - if (newAnimationProperties != _renderAnimationProperties) { - withWriteLock([&] { - _renderAnimationProperties = newAnimationProperties; - _currentFrame = _renderAnimationProperties.getCurrentFrame(); - }); - } - } - + + // The code to deal with the change of properties is now in ModelEntityItem.cpp + // That is where _currentFrame and _lastAnimated were updated. if (_animating) { DETAILED_PROFILE_RANGE(simulation_physics, "Animate"); if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); } - animate(entity); + if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) { + animate(entity); + } emit requestRenderUpdate(); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 0272bed575..b4f2665692 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -33,7 +33,7 @@ namespace render { namespace entities { class ModelEntityRenderer; } } -//#define MODEL_ENTITY_USE_FADE_EFFECT +// #define MODEL_ENTITY_USE_FADE_EFFECT class ModelEntityWrapper : public ModelEntityItem { using Parent = ModelEntityItem; friend class render::entities::ModelEntityRenderer; @@ -133,7 +133,7 @@ class ModelEntityRenderer : public TypedEntityRenderer #include #include +#include #include #include // usecTimestampNow() #include @@ -114,6 +115,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_EDITION_NUMBER; requestedProperties += PROP_ENTITY_INSTANCE_NUMBER; requestedProperties += PROP_CERTIFICATE_ID; + requestedProperties += PROP_STATIC_CERTIFICATE_VERSION; requestedProperties += PROP_NAME; requestedProperties += PROP_HREF; @@ -271,6 +273,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, getEditionNumber()); APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, getEntityInstanceNumber()); APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, getCertificateID()); + APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, getStaticCertificateVersion()); APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); @@ -819,6 +822,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_EDITION_NUMBER, quint32, setEditionNumber); READ_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber); READ_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, QString, setCertificateID); + READ_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion); READ_ENTITY_PROPERTY(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); @@ -984,6 +988,7 @@ void EntityItem::setCollisionSoundURL(const QString& value) { } void EntityItem::simulate(const quint64& now) { + DETAILED_PROFILE_RANGE(simulation_physics, "Simulate"); if (getLastSimulated() == 0) { setLastSimulated(now); } @@ -1039,6 +1044,7 @@ void EntityItem::simulate(const quint64& now) { } bool EntityItem::stepKinematicMotion(float timeElapsed) { + DETAILED_PROFILE_RANGE(simulation_physics, "StepKinematicMotion"); // get all the data Transform transform; glm::vec3 linearVelocity; @@ -1249,6 +1255,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper COPY_ENTITY_PROPERTY_TO_PROPERTIES(editionNumber, getEditionNumber); COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityInstanceNumber, getEntityInstanceNumber); COPY_ENTITY_PROPERTY_TO_PROPERTIES(certificateID, getCertificateID); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(staticCertificateVersion, getStaticCertificateVersion); COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName); COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref); @@ -1356,6 +1363,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(editionNumber, setEditionNumber); SET_ENTITY_PROPERTY_FROM_PROPERTIES(entityInstanceNumber, setEntityInstanceNumber); SET_ENTITY_PROPERTY_FROM_PROPERTIES(certificateID, setCertificateID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(staticCertificateVersion, setStaticCertificateVersion); SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName); SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref); @@ -1626,6 +1634,10 @@ void EntityItem::setParentID(const QUuid& value) { newParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING; } + if (!oldParentID.isNull() && (oldParentID == Physics::getSessionUUID() || oldParentID == AVATAR_SELF_ID)) { + oldParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING; + } + if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) { if ((bool)(newParentNoBootstrapping & Simulation::NO_BOOTSTRAPPING)) { markDirtyFlags(Simulation::NO_BOOTSTRAPPING); @@ -2783,6 +2795,7 @@ DEFINE_PROPERTY_ACCESSOR(QString, MarketplaceID, marketplaceID) DEFINE_PROPERTY_ACCESSOR(quint32, EditionNumber, editionNumber) DEFINE_PROPERTY_ACCESSOR(quint32, EntityInstanceNumber, entityInstanceNumber) DEFINE_PROPERTY_ACCESSOR(QString, CertificateID, certificateID) +DEFINE_PROPERTY_ACCESSOR(quint32, StaticCertificateVersion, staticCertificateVersion) uint32_t EntityItem::getDirtyFlags() const { uint32_t result; @@ -2840,7 +2853,7 @@ void EntityItem::retrieveMarketplacePublicKey() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath("/api/v1/commerce/marketplace_key"); QJsonObject request; networkRequest.setUrl(requestURL); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 4c7f37bd6a..088d21e84d 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -328,6 +328,8 @@ public: void setEntityInstanceNumber(const quint32&); QString getCertificateID() const; void setCertificateID(const QString& value); + quint32 getStaticCertificateVersion() const; + void setStaticCertificateVersion(const quint32&); // TODO: get rid of users of getRadius()... float getRadius() const; @@ -547,6 +549,7 @@ protected: quint32 _editionNumber { ENTITY_ITEM_DEFAULT_EDITION_NUMBER }; quint32 _entityInstanceNumber { ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER }; QString _marketplaceID { ENTITY_ITEM_DEFAULT_MARKETPLACE_ID }; + quint32 _staticCertificateVersion { ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION }; // NOTE: Damping is applied like this: v *= pow(1 - damping, dt) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 9f7ba1cc80..678ddfcea5 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -325,6 +324,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_EDITION_NUMBER, editionNumber); CHECK_PROPERTY_CHANGE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber); CHECK_PROPERTY_CHANGE(PROP_CERTIFICATE_ID, certificateID); + CHECK_PROPERTY_CHANGE(PROP_STATIC_CERTIFICATE_VERSION, staticCertificateVersion); CHECK_PROPERTY_CHANGE(PROP_NAME, name); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode); @@ -460,6 +460,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EDITION_NUMBER, editionNumber); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CERTIFICATE_ID, certificateID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STATIC_CERTIFICATE_VERSION, staticCertificateVersion); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL); @@ -743,6 +744,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(editionNumber, quint32, setEditionNumber); COPY_PROPERTY_FROM_QSCRIPTVALUE(entityInstanceNumber, quint32, setEntityInstanceNumber); COPY_PROPERTY_FROM_QSCRIPTVALUE(certificateID, QString, setCertificateID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(staticCertificateVersion, quint32, setStaticCertificateVersion); COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName); COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL); @@ -900,6 +902,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(editionNumber); COPY_PROPERTY_IF_CHANGED(entityInstanceNumber); COPY_PROPERTY_IF_CHANGED(certificateID); + COPY_PROPERTY_IF_CHANGED(staticCertificateVersion); COPY_PROPERTY_IF_CHANGED(name); COPY_PROPERTY_IF_CHANGED(collisionSoundURL); @@ -1090,6 +1093,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32); ADD_PROPERTY_TO_MAP(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32); ADD_PROPERTY_TO_MAP(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString); + ADD_PROPERTY_TO_MAP(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); @@ -1478,6 +1482,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, properties.getEditionNumber()); APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber()); APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID()); + APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion()); } if (propertyCount > 0) { @@ -1829,6 +1834,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EDITION_NUMBER, quint32, setEditionNumber); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion); return valid; } @@ -1996,6 +2002,7 @@ void EntityItemProperties::markAllChanged() { _editionNumberChanged = true; _entityInstanceNumberChanged = true; _certificateIDChanged = true; + _staticCertificateVersionChanged = true; _keyLight.markAllChanged(); @@ -2338,6 +2345,9 @@ QList EntityItemProperties::listChangedProperties() { if (certificateIDChanged()) { out += "certificateID"; } + if (staticCertificateVersionChanged()) { + out += "staticCertificateVersion"; + } if (backgroundModeChanged()) { out += "backgroundMode"; @@ -2480,6 +2490,9 @@ QByteArray EntityItemProperties::getStaticCertificateJSON() const { // of the entity as reviewed during the certification submission. QJsonObject json; + + quint32 staticCertificateVersion = getStaticCertificateVersion(); + if (!getAnimation().getURL().isEmpty()) { json["animationURL"] = getAnimation().getURL(); } @@ -2496,7 +2509,11 @@ QByteArray EntityItemProperties::getStaticCertificateJSON() const { ADD_STRING_PROPERTY(marketplaceID, MarketplaceID); ADD_STRING_PROPERTY(modelURL, ModelURL); ADD_STRING_PROPERTY(script, Script); + if (staticCertificateVersion >= 1) { + ADD_STRING_PROPERTY(serverScripts, ServerScripts); + } ADD_ENUM_PROPERTY(shapeType, ShapeType); + ADD_INT_PROPERTY(staticCertificateVersion, StaticCertificateVersion); json["type"] = EntityTypes::getEntityTypeName(getType()); return QJsonDocument(json).toJson(QJsonDocument::Compact); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index ec192d7c9f..4f7ba1317b 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -220,6 +220,7 @@ public: DEFINE_PROPERTY_REF(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32, ENTITY_ITEM_DEFAULT_EDITION_NUMBER); DEFINE_PROPERTY_REF(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32, ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER); DEFINE_PROPERTY_REF(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString, ENTITY_ITEM_DEFAULT_CERTIFICATE_ID); + DEFINE_PROPERTY_REF(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32, ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION); // these are used when bouncing location data into and out of scripts DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3); @@ -473,6 +474,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, EditionNumber, editionNumber, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityInstanceNumber, entityInstanceNumber, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CertificateID, certificateID, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, StaticCertificateVersion, staticCertificateVersion, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, HazeMode, hazeMode, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index f4e8a18012..49bce37fbd 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -41,6 +41,7 @@ const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString(""); const quint32 ENTITY_ITEM_DEFAULT_EDITION_NUMBER = 0; const quint32 ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER = 0; const QString ENTITY_ITEM_DEFAULT_CERTIFICATE_ID = QString(""); +const quint32 ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION = 0; const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f; const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 35d40b669a..41c1e77bb8 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -200,6 +200,7 @@ enum EntityPropertyList { PROP_EDITION_NUMBER, PROP_ENTITY_INSTANCE_NUMBER, PROP_CERTIFICATE_ID, + PROP_STATIC_CERTIFICATE_VERSION, PROP_HAZE_MODE, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 35941c09d3..d278283ffa 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1821,6 +1821,19 @@ glm::mat4 EntityScriptingInterface::getEntityLocalTransform(const QUuid& entityI return result; } +QString EntityScriptingInterface::getStaticCertificateJSON(const QUuid& entityID) { + QByteArray result; + if (_entityTree) { + _entityTree->withReadLock([&] { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID)); + if (entity) { + result = entity->getProperties().getStaticCertificateJSON(); + } + }); + } + return result; +} + bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& entityID) { bool result = false; if (_entityTree) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index cc5d8ff16f..095a821482 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -423,6 +423,12 @@ public slots: */ Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID); + + /**jsdoc + * Return the Static Certificate JSON for the specified {EntityID}. + * @return {QByteArray} The Static Certificate JSON for the specified entity. + */ + Q_INVOKABLE QString getStaticCertificateJSON(const QUuid& entityID); Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID); signals: diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index f91d728d78..36b0d8ab2d 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -29,7 +29,6 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) { } void EntitySimulation::updateEntities() { - PROFILE_RANGE(simulation_physics, "ES::updateEntities"); QMutexLocker lock(&_mutex); quint64 now = usecTimestampNow(); @@ -38,12 +37,7 @@ void EntitySimulation::updateEntities() { callUpdateOnEntitiesThatNeedIt(now); moveSimpleKinematics(now); updateEntitiesInternal(now); - - { - PROFILE_RANGE(simulation_physics, "Sort"); - PerformanceTimer perfTimer("sortingEntities"); - sortEntitiesThatMoved(); - } + sortEntitiesThatMoved(); } void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) { @@ -101,6 +95,7 @@ void EntitySimulation::changeEntityInternal(EntityItemPointer entity) { // protected void EntitySimulation::expireMortalEntities(const quint64& now) { if (now > _nextExpiry) { + PROFILE_RANGE_EX(simulation_physics, "ExpireMortals", 0xffff00ff, (uint64_t)_mortalEntities.size()); // only search for expired entities if we expect to find one _nextExpiry = quint64(-1); QMutexLocker lock(&_mutex); @@ -146,6 +141,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) { // protected void EntitySimulation::sortEntitiesThatMoved() { + PROFILE_RANGE_EX(simulation_physics, "SortTree", 0xffff00ff, (uint64_t)_entitiesToSort.size()); // NOTE: this is only for entities that have been moved by THIS EntitySimulation. // External changes to entity position/shape are expected to be sorted outside of the EntitySimulation. MovingEntitiesOperator moveOperator; @@ -265,7 +261,7 @@ void EntitySimulation::clearEntities() { } void EntitySimulation::moveSimpleKinematics(const quint64& now) { - PROFILE_RANGE_EX(simulation_physics, "Kinematics", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size()); + PROFILE_RANGE_EX(simulation_physics, "MoveSimples", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size()); SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin(); while (itemItr != _simpleKinematicEntities.end()) { EntityItemPointer entity = *itemItr; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index e62399ce95..b5765bb44b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1308,7 +1308,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt QNetworkRequest networkRequest; networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); QJsonObject request; request["certificate_id"] = certID; @@ -1770,24 +1770,26 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) { } void EntityTree::update(bool simulate) { - PROFILE_RANGE(simulation_physics, "ET::update"); + PROFILE_RANGE(simulation_physics, "UpdateTree"); fixupNeedsParentFixups(); if (simulate && _simulation) { withWriteLock([&] { _simulation->updateEntities(); - VectorOfEntities pendingDeletes; - _simulation->takeEntitiesToDelete(pendingDeletes); + { + PROFILE_RANGE(simulation_physics, "Deletes"); + VectorOfEntities pendingDeletes; + _simulation->takeEntitiesToDelete(pendingDeletes); + if (pendingDeletes.size() > 0) { + // translate into list of ID's + QSet idsToDelete; - if (pendingDeletes.size() > 0) { - // translate into list of ID's - QSet idsToDelete; + for (auto entity : pendingDeletes) { + idsToDelete.insert(entity->getEntityItemID()); + } - for (auto entity : pendingDeletes) { - idsToDelete.insert(entity->getEntityItemID()); + // delete these things the roundabout way + deleteEntities(idsToDelete, true); } - - // delete these things the roundabout way - deleteEntities(idsToDelete, true); } }); } diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 313721ff54..323584c7ee 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -33,6 +33,8 @@ EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const E ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _lastAnimated = usecTimestampNow(); + // set the last animated when interface (re)starts _type = EntityTypes::Model; _lastKnownCurrentFrame = -1; _color[0] = _color[1] = _color[2] = 0; @@ -186,6 +188,105 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit } + +// added update function back for property fix +void ModelEntityItem::update(const quint64& now) { + + { + auto currentAnimationProperties = this->getAnimationProperties(); + + if (_previousAnimationProperties != currentAnimationProperties) { + withWriteLock([&] { + // if we hit start animation or change the first or last frame then restart the animation + if ((currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || + (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || + (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { + + // when we start interface and the property is are set then the current frame is initialized to -1 + if (_currentFrame < 0) { + // don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set + _currentFrame = currentAnimationProperties.getCurrentFrame(); + setAnimationCurrentFrame(_currentFrame); + } else { + _lastAnimated = usecTimestampNow(); + _currentFrame = currentAnimationProperties.getFirstFrame(); + setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); + } + } else if (!currentAnimationProperties.getRunning() && _previousAnimationProperties.getRunning()) { + _currentFrame = currentAnimationProperties.getFirstFrame(); + setAnimationCurrentFrame(_currentFrame); + } else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) { + // don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated + _currentFrame = currentAnimationProperties.getCurrentFrame(); + } + + }); + _previousAnimationProperties = this->getAnimationProperties(); + + } + + if (isAnimatingSomething()) { + if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame())) { + updateFrameCount(); + } + } + } + + EntityItem::update(now); +} + +bool ModelEntityItem::needsToCallUpdate() const { + + return true; +} + +void ModelEntityItem::updateFrameCount() { + + if (_currentFrame < 0.0f) { + return; + } + + if (!_lastAnimated) { + _lastAnimated = usecTimestampNow(); + return; + } + + auto now = usecTimestampNow(); + + // update the interval since the last animation. + auto interval = now - _lastAnimated; + _lastAnimated = now; + + // if fps is negative then increment timestamp and return. + if (getAnimationFPS() < 0.0f) { + return; + } + + int updatedFrameCount = getAnimationLastFrame() - getAnimationFirstFrame() + 1; + + if (!getAnimationHold() && getAnimationIsPlaying()) { + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentFrame += (deltaTime * getAnimationFPS()); + if (_currentFrame > getAnimationLastFrame()) { + if (getAnimationLoop()) { + _currentFrame = getAnimationFirstFrame() + (int)(glm::floor(_currentFrame - getAnimationFirstFrame())) % (updatedFrameCount - 1); + } else { + _currentFrame = getAnimationLastFrame(); + } + } else if (_currentFrame < getAnimationFirstFrame()) { + if (getAnimationFirstFrame() < 0) { + _currentFrame = 0; + } else { + _currentFrame = getAnimationFirstFrame(); + } + } + // qCDebug(entities) << "in update frame " << _currentFrame; + setAnimationCurrentFrame(_currentFrame); + } + + +} + void ModelEntityItem::debugDump() const { qCDebug(entities) << "ModelEntityItem id:" << getEntityItemID(); qCDebug(entities) << " edited ago:" << getEditedAgo(); @@ -538,6 +639,13 @@ void ModelEntityItem::setAnimationLoop(bool loop) { }); } +bool ModelEntityItem::getAnimationLoop() const { + return resultWithReadLock([&] { + return _animationProperties.getLoop(); + }); +} + + void ModelEntityItem::setAnimationHold(bool hold) { withWriteLock([&] { _animationProperties.setHold(hold); @@ -573,8 +681,9 @@ float ModelEntityItem::getAnimationLastFrame() const { return _animationProperties.getLastFrame(); }); } + bool ModelEntityItem::getAnimationIsPlaying() const { - return resultWithReadLock([&] { + return resultWithReadLock([&] { return _animationProperties.getRunning(); }); } @@ -585,8 +694,15 @@ float ModelEntityItem::getAnimationCurrentFrame() const { }); } -bool ModelEntityItem::isAnimatingSomething() const { +float ModelEntityItem::getAnimationFPS() const { return resultWithReadLock([&] { + return _animationProperties.getFPS(); + }); +} + + +bool ModelEntityItem::isAnimatingSomething() const { + return resultWithReadLock([&] { return !_animationProperties.getURL().isEmpty() && _animationProperties.getRunning() && (_animationProperties.getFPS() != 0.0f); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 2c3ef3aa2d..7fee022011 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -17,6 +17,7 @@ #include #include "AnimationPropertyGroup.h" + class ModelEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -46,8 +47,11 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - //virtual void update(const quint64& now) override; - //virtual bool needsToCallUpdate() const override; + // update() and needstocallupdate() added back for the entity property fix + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const override; + void updateFrameCount(); + virtual void debugDump() const override; void setShapeType(ShapeType type) override; @@ -90,6 +94,7 @@ public: bool getAnimationAllowTranslation() const { return _animationProperties.getAllowTranslation(); }; void setAnimationLoop(bool loop); + bool getAnimationLoop() const; void setAnimationHold(bool hold); bool getAnimationHold() const; @@ -102,6 +107,7 @@ public: bool getAnimationIsPlaying() const; float getAnimationCurrentFrame() const; + float getAnimationFPS() const; bool isAnimatingSomething() const; static const QString DEFAULT_TEXTURES; @@ -147,7 +153,7 @@ protected: }; QVector _localJointData; - int _lastKnownCurrentFrame; + int _lastKnownCurrentFrame{-1}; rgbColor _color; QString _modelURL; @@ -160,6 +166,11 @@ protected: QString _textures; ShapeType _shapeType = SHAPE_TYPE_NONE; + +private: + uint64_t _lastAnimated{ 0 }; + AnimationPropertyGroup _previousAnimationProperties; + float _currentFrame{ -1.0f }; }; #endif // hifi_ModelEntityItem_h diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index e0c2efd72e..315c6a86d2 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -56,7 +56,7 @@ float OBJTokenizer::getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } -int OBJTokenizer::nextToken() { +int OBJTokenizer::nextToken(bool allowSpaceChar /*= false*/) { if (_pushedBackToken != NO_PUSHBACKED_TOKEN) { int token = _pushedBackToken; _pushedBackToken = NO_PUSHBACKED_TOKEN; @@ -93,7 +93,7 @@ int OBJTokenizer::nextToken() { _datum = ""; _datum.append(ch); while (_device->getChar(&ch)) { - if (QChar(ch).isSpace() || ch == '\"') { + if ((QChar(ch).isSpace() || ch == '\"') && (!allowSpaceChar || ch != ' ')) { ungetChar(ch); // read until we encounter a special character, then replace it break; } @@ -399,7 +399,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi currentMaterialName = QString("part-") + QString::number(_partCounter++); } } else if (token == "mtllib" && !_url.isEmpty()) { - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { + if (tokenizer.nextToken(true) != OBJTokenizer::DATUM_TOKEN) { break; } QByteArray libraryName = tokenizer.getDatum(); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index fb250833cf..45e3f79480 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -11,7 +11,7 @@ public: DATUM_TOKEN = 0x100, COMMENT_TOKEN = 0x101 }; - int nextToken(); + int nextToken(bool allowSpaceChar = false); const QByteArray& getDatum() const { return _datum; } bool isNextTokenFloat(); const QByteArray getLineAsDatum(); // some "filenames" have spaces in them diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 9e4ab2e43f..f78ed1a583 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -981,7 +981,7 @@ public: static QImage extractEquirectangularFace(const QImage& source, gpu::Texture::CubeFace face, int faceWidth) { QImage image(faceWidth, faceWidth, source.format()); - glm::vec2 dstInvSize(1.0f / (float)source.width(), 1.0f / (float)source.height()); + glm::vec2 dstInvSize(1.0f / faceWidth); struct CubeToXYZ { gpu::Texture::CubeFace _face; diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 7d97687d0b..87b17d00d5 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -97,7 +97,7 @@ public: void setTemporaryDomain(const QUuid& domainID, const QString& key); const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); } - QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL; } + QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL(); } public slots: void requestAccessToken(const QString& login, const QString& password); diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 27d4a31ccf..58a4446aa6 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -127,7 +127,11 @@ void Assignment::swap(Assignment& otherAssignment) { } const char* Assignment::getTypeName() const { - switch (_type) { + return typeToString(_type); +} + +const char* Assignment::typeToString(Assignment::Type type) { + switch (type) { case Assignment::AudioMixerType: return "audio-mixer"; case Assignment::AvatarMixerType: diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index bbec40682f..e958c84d87 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -28,6 +28,7 @@ class Assignment : public QObject { public: enum Type : uint8_t { + FirstType = 0, AudioMixerType = 0, AvatarMixerType = 1, AgentType = 2, @@ -89,6 +90,7 @@ public: const QString& getNodeVersion() const { return _nodeVersion; } const char* getTypeName() const; + static const char* typeToString(Assignment::Type type); friend QDebug operator<<(QDebug debug, const Assignment& assignment); friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment); diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 0c210e4360..a4726f9b1a 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -12,15 +12,31 @@ #ifndef hifi_NetworkingConstants_h #define hifi_NetworkingConstants_h +#include #include namespace NetworkingConstants { // If you want to use STAGING instead of STABLE, - // don't forget to ALSO change the Domain Server Metaverse Server URL inside of: + // links from the Domain Server web interface (like the connect account token generation) + // will still point at stable unless you ALSO change the Domain Server Metaverse Server URL inside of: // \domain-server\resources\web\js\shared.js + + // You can avoid changing that and still effectively use a connected domain on staging + // if you manually generate a personal access token for the domains scope + // at https://staging.highfidelity.com/user/tokens/new?for_domain_server=true + const QUrl METAVERSE_SERVER_URL_STABLE("https://metaverse.highfidelity.com"); const QUrl METAVERSE_SERVER_URL_STAGING("https://staging.highfidelity.com"); - const QUrl METAVERSE_SERVER_URL = METAVERSE_SERVER_URL_STABLE; + + // You can change the return of this function if you want to use a custom metaverse URL at compile time + // or you can pass a custom URL via the env variable + static const QUrl METAVERSE_SERVER_URL() { + static const QString HIFI_METAVERSE_URL_ENV = "HIFI_METAVERSE_URL"; + static const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains(HIFI_METAVERSE_URL_ENV) + ? QUrl(QProcessEnvironment::systemEnvironment().value(HIFI_METAVERSE_URL_ENV)) + : METAVERSE_SERVER_URL_STABLE; + return serverURL; + }; } #endif // hifi_NetworkingConstants_h diff --git a/libraries/networking/src/OAuthNetworkAccessManager.cpp b/libraries/networking/src/OAuthNetworkAccessManager.cpp index 15d5acbc67..a30786efa4 100644 --- a/libraries/networking/src/OAuthNetworkAccessManager.cpp +++ b/libraries/networking/src/OAuthNetworkAccessManager.cpp @@ -35,7 +35,7 @@ QNetworkReply* OAuthNetworkAccessManager::createRequest(QNetworkAccessManager::O auto accountManager = DependencyManager::get(); if (accountManager->hasValidAccessToken() - && req.url().host() == NetworkingConstants::METAVERSE_SERVER_URL.host()) { + && req.url().host() == NetworkingConstants::METAVERSE_SERVER_URL().host()) { QNetworkRequest authenticatedRequest(req); authenticatedRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); authenticatedRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index 0965c9834f..aec6df4f14 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -141,3 +141,16 @@ void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(int times payload["secondsToComplete"] = secondsToComplete; doLogAction("commerceWalletSetupFinished", payload); } + +void UserActivityLoggerScriptingInterface::commercePassphraseEntry(QString source) { + QJsonObject payload; + payload["source"] = source; + doLogAction("commercePassphraseEntry", payload); +} + +void UserActivityLoggerScriptingInterface::commercePassphraseAuthenticationStatus(QString status) { + QJsonObject payload; + payload["status"] = status; + doLogAction("commercePassphraseAuthenticationStatus", payload); + +} diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index e71723f03c..0e08b050d7 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -39,6 +39,8 @@ public: Q_INVOKABLE void commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain); Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName); Q_INVOKABLE void commerceWalletSetupFinished(int timestamp, QString setupAttemptID, int secondsToComplete); + Q_INVOKABLE void commercePassphraseEntry(QString source); + Q_INVOKABLE void commercePassphraseAuthenticationStatus(QString status); private: void doLogAction(QString action, QJsonObject details = {}); }; diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index f42049f107..2f57523f79 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -11,6 +11,8 @@ #include "Connection.h" +#include + #include #include @@ -60,6 +62,15 @@ Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::uniq _ack2Packet = ControlPacket::create(ControlPacket::ACK2, ACK2_PAYLOAD_BYTES); _lossReport = ControlPacket::create(ControlPacket::NAK, NAK_PACKET_PAYLOAD_BYTES); _handshakeACK = ControlPacket::create(ControlPacket::HandshakeACK, HANDSHAKE_ACK_PAYLOAD_BYTES); + + + // setup psuedo-random number generation shared by all connections + static std::random_device rd; + static std::mt19937 generator(rd()); + static std::uniform_int_distribution<> distribution(0, SequenceNumber::MAX); + + // randomize the intial sequence number + _initialSequenceNumber = SequenceNumber(distribution(generator)); } Connection::~Connection() { @@ -79,11 +90,11 @@ void Connection::stopSendQueue() { // tell the send queue to stop and be deleted sendQueue->stop(); + + _lastMessageNumber = sendQueue->getCurrentMessageNumber(); + sendQueue->deleteLater(); - // since we're stopping the send queue we should consider our handshake ACK not receieved - _hasReceivedHandshakeACK = false; - // wait on the send queue thread so we know the send queue is gone sendQueueThread->quit(); sendQueueThread->wait(); @@ -101,13 +112,19 @@ void Connection::setMaxBandwidth(int maxBandwidth) { SendQueue& Connection::getSendQueue() { if (!_sendQueue) { - // we may have a sequence number from the previous inactive queue - re-use that so that the // receiver is getting the sequence numbers it expects (given that the connection must still be active) // Lasily create send queue - _sendQueue = SendQueue::create(_parentSocket, _destination); - _lastReceivedACK = _sendQueue->getCurrentSequenceNumber(); + + if (!_hasReceivedHandshakeACK) { + // First time creating a send queue for this connection + _sendQueue = SendQueue::create(_parentSocket, _destination, _initialSequenceNumber - 1, _lastMessageNumber, _hasReceivedHandshakeACK); + _lastReceivedACK = _sendQueue->getCurrentSequenceNumber(); + } else { + // Connection already has a handshake from a previous send queue + _sendQueue = SendQueue::create(_parentSocket, _destination, _lastReceivedACK, _lastMessageNumber, _hasReceivedHandshakeACK); + } #ifdef UDT_CONNECTION_DEBUG qCDebug(networking) << "Created SendQueue for connection to" << _destination; @@ -142,14 +159,6 @@ void Connection::queueInactive() { #ifdef UDT_CONNECTION_DEBUG qCDebug(networking) << "Connection to" << _destination << "has stopped its SendQueue."; #endif - - if (!_hasReceivedHandshake || !_isReceivingData) { -#ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "Connection SendQueue to" << _destination << "stopped and no data is being received - stopping connection."; -#endif - - deactivate(); - } } void Connection::queueTimeout() { @@ -184,11 +193,16 @@ void Connection::queueReceivedMessagePacket(std::unique_ptr packet) { while (pendingMessage.hasAvailablePackets()) { auto packet = pendingMessage.removeNextPacket(); - _parentSocket->messageReceived(std::move(packet)); - } - if (pendingMessage.isComplete()) { - _pendingReceivedMessages.erase(messageNumber); + auto packetPosition = packet->getPacketPosition(); + + _parentSocket->messageReceived(std::move(packet)); + + // if this was the last or only packet, then we can remove the pending message from our hash + if (packetPosition == Packet::PacketPosition::LAST || + packetPosition == Packet::PacketPosition::ONLY) { + _pendingReceivedMessages.erase(messageNumber); + } } } @@ -208,19 +222,6 @@ void Connection::sync() { && duration_cast(sincePacketReceive).count() >= MIN_SECONDS_BEFORE_EXPIRY ) { // the receive side of this connection is expired _isReceivingData = false; - - // if we don't have a send queue that means the whole connection has expired and we can emit our signal - // otherwise we'll wait for it to also timeout before cleaning up - if (!_sendQueue) { - -#ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "Connection to" << _destination << "no longer receiving any data and there is currently no send queue - stopping connection."; -#endif - - deactivate(); - - return; - } } // reset the number of light ACKs or non SYN ACKs during this sync interval @@ -242,26 +243,6 @@ void Connection::sync() { sendTimeoutNAK(); } } - } else if (!_sendQueue) { - // we haven't received a packet and we're not sending - // this most likely means we were started erroneously - // check the start time for this connection and auto expire it after 5 seconds of not receiving or sending any data - static const int CONNECTION_NOT_USED_EXPIRY_SECONDS = 5; - auto secondsSinceStart = duration_cast(p_high_resolution_clock::now() - _connectionStart).count(); - - if (secondsSinceStart >= CONNECTION_NOT_USED_EXPIRY_SECONDS) { - // it's been CONNECTION_NOT_USED_EXPIRY_SECONDS and nothing has actually happened with this connection - // consider it inactive and emit our inactivity signal - -#ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "Connection to" << _destination << "did not receive or send any data in last" - << CONNECTION_NOT_USED_EXPIRY_SECONDS << "seconds - stopping connection."; -#endif - - deactivate(); - - return; - } } } @@ -444,7 +425,6 @@ void Connection::sendHandshakeRequest() { } bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, int packetSize, int payloadSize) { - if (!_hasReceivedHandshake) { // Refuse to process any packets until we've received the handshake // Send handshake request to re-request a handshake @@ -536,7 +516,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in } else { _stats.recordReceivedPackets(payloadSize, packetSize); } - + return !wasDuplicate; } @@ -827,11 +807,13 @@ void Connection::processHandshakeACK(ControlPacketPointer controlPacket) { SequenceNumber initialSequenceNumber; controlPacket->readPrimitive(&initialSequenceNumber); - // hand off this handshake ACK to the send queue so it knows it can start sending - getSendQueue().handshakeACK(initialSequenceNumber); - - // indicate that handshake ACK was received - _hasReceivedHandshakeACK = true; + if (initialSequenceNumber == _initialSequenceNumber) { + // hand off this handshake ACK to the send queue so it knows it can start sending + getSendQueue().handshakeACK(); + + // indicate that handshake ACK was received + _hasReceivedHandshakeACK = true; + } } } diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index c134081dde..0017eb204a 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -37,7 +37,6 @@ class Socket; class PendingReceivedMessage { public: void enqueuePacket(std::unique_ptr packet); - bool isComplete() const { return _hasLastPacket && _numPackets == _packets.size(); } bool hasAvailablePackets() const; std::unique_ptr removeNextPacket(); @@ -72,8 +71,6 @@ public: void queueReceivedMessagePacket(std::unique_ptr packet); ConnectionStats::Stats sampleStats() { return _stats.sample(); } - - bool isActive() const { return _isActive; } HifiSockAddr getDestination() const { return _destination; } @@ -83,7 +80,6 @@ public: signals: void packetSent(); - void connectionInactive(const HifiSockAddr& sockAddr); void receiverHandshakeRequestComplete(const HifiSockAddr& sockAddr); private slots: @@ -112,8 +108,6 @@ private: void resetReceiveState(); void resetRTT(); - void deactivate() { _isActive = false; emit connectionInactive(_destination); } - SendQueue& getSendQueue(); SequenceNumber nextACK() const; void updateRTT(int rtt); @@ -138,9 +132,11 @@ private: p_high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender bool _isReceivingData { false }; // flag used for expiry of receipt portion of connection - bool _isActive { true }; // flag used for inactivity of connection - SequenceNumber _initialReceiveSequenceNumber; // Randomized by peer SendQueue on creation, identifies connection during re-connect requests + SequenceNumber _initialSequenceNumber; // Randomized on Connection creation, identifies connection during re-connect requests + SequenceNumber _initialReceiveSequenceNumber; // Randomized by peer Connection on creation, identifies connection during re-connect requests + + MessageNumber _lastMessageNumber { 0 }; LossList _lossList; // List of all missing packets SequenceNumber _lastReceivedSequenceNumber; // The largest sequence number received from the peer diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 207ddf6bbb..62354da11a 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::HazeEffect); + return static_cast(EntityVersion::StaticCertJsonVersionOne); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConnectionIdentifier); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index e5cb87c379..618ac2de0c 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -199,7 +199,8 @@ QDebug operator<<(QDebug debug, const PacketType& type); enum class EntityVersion : PacketVersion { StrokeColorProperty = 77, HasDynamicOwnershipTests, - HazeEffect + HazeEffect, + StaticCertJsonVersionOne }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/networking/src/udt/PacketQueue.cpp b/libraries/networking/src/udt/PacketQueue.cpp index 9560f2f187..0560855ecb 100644 --- a/libraries/networking/src/udt/PacketQueue.cpp +++ b/libraries/networking/src/udt/PacketQueue.cpp @@ -15,7 +15,7 @@ using namespace udt; -PacketQueue::PacketQueue() { +PacketQueue::PacketQueue(MessageNumber messageNumber) : _currentMessageNumber(messageNumber) { _channels.emplace_back(new std::list()); } diff --git a/libraries/networking/src/udt/PacketQueue.h b/libraries/networking/src/udt/PacketQueue.h index 2b3d3a4b5b..bc4c5e3432 100644 --- a/libraries/networking/src/udt/PacketQueue.h +++ b/libraries/networking/src/udt/PacketQueue.h @@ -34,7 +34,7 @@ class PacketQueue { using Channels = std::vector; public: - PacketQueue(); + PacketQueue(MessageNumber messageNumber = 0); void queuePacket(PacketPointer packet); void queuePacketList(PacketListPointer packetList); @@ -42,6 +42,8 @@ public: PacketPointer takePacket(); Mutex& getLock() { return _packetsLock; } + + MessageNumber getCurrentMessageNumber() const { return _currentMessageNumber; } private: MessageNumber getNextMessageNumber(); diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 0c029751aa..b62624aab9 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -12,7 +12,6 @@ #include "SendQueue.h" #include -#include #include #include @@ -62,10 +61,12 @@ private: Mutex2& _mutex2; }; -std::unique_ptr SendQueue::create(Socket* socket, HifiSockAddr destination) { +std::unique_ptr SendQueue::create(Socket* socket, HifiSockAddr destination, SequenceNumber currentSequenceNumber, + MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK) { Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*"); - auto queue = std::unique_ptr(new SendQueue(socket, destination)); + auto queue = std::unique_ptr(new SendQueue(socket, destination, currentSequenceNumber, + currentMessageNumber, hasReceivedHandshakeACK)); // Setup queue private thread QThread* thread = new QThread; @@ -84,25 +85,18 @@ std::unique_ptr SendQueue::create(Socket* socket, HifiSockAddr destin return queue; } -SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) : +SendQueue::SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber, + MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK) : + _packets(currentMessageNumber), _socket(socket), _destination(dest) { - // setup psuedo-random number generation for all instances of SendQueue - static std::random_device rd; - static std::mt19937 generator(rd()); - static std::uniform_int_distribution<> distribution(0, SequenceNumber::MAX); - - // randomize the intial sequence number - _initialSequenceNumber = SequenceNumber(distribution(generator)); - - // set our member variables from randomized initial number - _currentSequenceNumber = _initialSequenceNumber - 1; + // set our member variables from current sequence number + _currentSequenceNumber = currentSequenceNumber; _atomicCurrentSequenceNumber = uint32_t(_currentSequenceNumber); - _lastACKSequenceNumber = uint32_t(_currentSequenceNumber) - 1; + _lastACKSequenceNumber = uint32_t(_currentSequenceNumber); - // default the last receiver response to the current time - _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); + _hasReceivedHandshakeACK = hasReceivedHandshakeACK; } SendQueue::~SendQueue() { @@ -114,8 +108,8 @@ void SendQueue::queuePacket(std::unique_ptr packet) { // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets _emptyCondition.notify_one(); - if (!this->thread()->isRunning() && _state == State::NotStarted) { - this->thread()->start(); + if (!thread()->isRunning() && _state == State::NotStarted) { + thread()->start(); } } @@ -125,8 +119,8 @@ void SendQueue::queuePacketList(std::unique_ptr packetList) { // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets _emptyCondition.notify_one(); - if (!this->thread()->isRunning() && _state == State::NotStarted) { - this->thread()->start(); + if (!thread()->isRunning() && _state == State::NotStarted) { + thread()->start(); } } @@ -144,9 +138,6 @@ int SendQueue::sendPacket(const Packet& packet) { } void SendQueue::ack(SequenceNumber ack) { - // this is a response from the client, re-set our timeout expiry and our last response time - _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); - if (_lastACKSequenceNumber == (uint32_t) ack) { return; } @@ -173,10 +164,7 @@ void SendQueue::ack(SequenceNumber ack) { _emptyCondition.notify_one(); } -void SendQueue::nak(SequenceNumber start, SequenceNumber end) { - // this is a response from the client, re-set our timeout expiry - _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); - +void SendQueue::nak(SequenceNumber start, SequenceNumber end) { { std::lock_guard nakLocker(_naksLock); _naks.insert(start, end); @@ -197,9 +185,6 @@ void SendQueue::fastRetransmit(udt::SequenceNumber ack) { } void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) { - // this is a response from the client, re-set our timeout expiry - _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); - { std::lock_guard nakLocker(_naksLock); _naks.clear(); @@ -225,8 +210,11 @@ void SendQueue::sendHandshake() { std::unique_lock handshakeLock { _handshakeMutex }; if (!_hasReceivedHandshakeACK) { // we haven't received a handshake ACK from the client, send another now + // if the handshake hasn't been completed, then the initial sequence number + // should be the current sequence number + 1 + SequenceNumber initialSequenceNumber = _currentSequenceNumber + 1; auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, sizeof(SequenceNumber)); - handshakePacket->writePrimitive(_initialSequenceNumber); + handshakePacket->writePrimitive(initialSequenceNumber); _socket->writeBasePacket(*handshakePacket, _destination); // we wait for the ACK or the re-send interval to expire @@ -235,18 +223,14 @@ void SendQueue::sendHandshake() { } } -void SendQueue::handshakeACK(SequenceNumber initialSequenceNumber) { - if (initialSequenceNumber == _initialSequenceNumber) { - { - std::lock_guard locker { _handshakeMutex }; - _hasReceivedHandshakeACK = true; - } - - _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); - - // Notify on the handshake ACK condition - _handshakeACKCondition.notify_one(); +void SendQueue::handshakeACK() { + { + std::lock_guard locker { _handshakeMutex }; + _hasReceivedHandshakeACK = true; } + + // Notify on the handshake ACK condition + _handshakeACKCondition.notify_one(); } SequenceNumber SendQueue::getNextSequenceNumber() { @@ -540,28 +524,6 @@ bool SendQueue::maybeResendPacket() { bool SendQueue::isInactive(bool attemptedToSendPacket) { // check for connection timeout first - // that will be the case if we have had 16 timeouts since hearing back from the client, and it has been - // at least 5 seconds - static const int NUM_TIMEOUTS_BEFORE_INACTIVE = 16; - static const int MIN_MS_BEFORE_INACTIVE = 5 * 1000; - - auto sinceLastResponse = (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse); - - if (sinceLastResponse > 0 && - sinceLastResponse >= int64_t(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) && - sinceLastResponse > MIN_MS_BEFORE_INACTIVE) { - // If the flow window has been full for over CONSIDER_INACTIVE_AFTER, - // then signal the queue is inactive and return so it can be cleaned up - -#ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "SendQueue to" << _destination << "reached" << NUM_TIMEOUTS_BEFORE_INACTIVE << "timeouts" - << "and" << MIN_MS_BEFORE_INACTIVE << "milliseconds before receiving any ACK/NAK and is now inactive. Stopping."; -#endif - - deactivate(); - return true; - } - if (!attemptedToSendPacket) { // During our processing above we didn't send any packets diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 484afcb88e..a11aacdb91 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -50,7 +50,9 @@ public: Stopped }; - static std::unique_ptr create(Socket* socket, HifiSockAddr destination); + static std::unique_ptr create(Socket* socket, HifiSockAddr destination, + SequenceNumber currentSequenceNumber, MessageNumber currentMessageNumber, + bool hasReceivedHandshakeACK); virtual ~SendQueue(); @@ -58,6 +60,7 @@ public: void queuePacketList(std::unique_ptr packetList); SequenceNumber getCurrentSequenceNumber() const { return SequenceNumber(_atomicCurrentSequenceNumber); } + MessageNumber getCurrentMessageNumber() const { return _packets.getCurrentMessageNumber(); } void setFlowWindowSize(int flowWindowSize) { _flowWindowSize = flowWindowSize; } @@ -76,7 +79,7 @@ public slots: void nak(SequenceNumber start, SequenceNumber end); void fastRetransmit(SequenceNumber ack); void overrideNAKListFromPacket(ControlPacket& packet); - void handshakeACK(SequenceNumber initialSequenceNumber); + void handshakeACK(); signals: void packetSent(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint); @@ -91,7 +94,8 @@ private slots: void run(); private: - SendQueue(Socket* socket, HifiSockAddr dest); + SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber, + MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK); SendQueue(SendQueue& other) = delete; SendQueue(SendQueue&& other) = delete; @@ -115,8 +119,6 @@ private: Socket* _socket { nullptr }; // Socket to send packet on HifiSockAddr _destination; // Destination addr - - SequenceNumber _initialSequenceNumber; // Randomized on SendQueue creation, identifies connection during re-connect requests std::atomic _lastACKSequenceNumber { 0 }; // Last ACKed sequence number @@ -128,7 +130,6 @@ private: std::atomic _estimatedTimeout { 0 }; // Estimated timeout, set from CC std::atomic _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC - std::atomic _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK) std::atomic _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index a3374a0f47..55643985c8 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -257,9 +257,6 @@ Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) { congestionControl->setMaxBandwidth(_maxBandwidth); auto connection = std::unique_ptr(new Connection(this, sockAddr, std::move(congestionControl))); - // we queue the connection to cleanup connection in case it asks for it during its own rate control sync - QObject::connect(connection.get(), &Connection::connectionInactive, this, &Socket::cleanupConnection); - // allow higher-level classes to find out when connections have completed a handshake QObject::connect(connection.get(), &Connection::receiverHandshakeRequestComplete, this, &Socket::clientHandshakeRequestComplete); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 7e8b431ceb..a688d521d6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -14,8 +14,9 @@ #include #include #include -#include #include +#include +#include #include "BulletUtil.h" #include "EntityMotionState.h" @@ -325,6 +326,7 @@ bool EntityMotionState::isCandidateForOwnership() const { } bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { + DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync"); // NOTE: we only get here if we think we own the simulation assert(_body); @@ -476,6 +478,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { } bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { + DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend"); // NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called // after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL. assert(_entity); @@ -516,6 +519,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { } void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) { + DETAILED_PROFILE_RANGE(simulation_physics, "Send"); assert(_entity); assert(entityTreeIsLocked()); @@ -731,6 +735,7 @@ void EntityMotionState::resetMeasuredBodyAcceleration() { } void EntityMotionState::measureBodyAcceleration() { + DETAILED_PROFILE_RANGE(simulation_physics, "MeasureAccel"); // try to manually measure the true acceleration of the object uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); uint32_t numSubsteps = thisStep - _lastMeasureStep; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 3e87b9437d..e4ba47e205 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -10,12 +10,14 @@ // +#include "PhysicalEntitySimulation.h" + +#include #include "PhysicsHelpers.h" #include "PhysicsLogging.h" #include "ShapeManager.h" -#include "PhysicalEntitySimulation.h" PhysicalEntitySimulation::PhysicalEntitySimulation() { } @@ -274,20 +276,24 @@ void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotio } void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) { + PROFILE_RANGE_EX(simulation_physics, "ChangedEntities", 0x00000000, (uint64_t)motionStates.size()); QMutexLocker lock(&_mutex); // walk the motionStates looking for those that correspond to entities - for (auto stateItr : motionStates) { - ObjectMotionState* state = &(*stateItr); - assert(state); - if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { - EntityMotionState* entityState = static_cast(state); - EntityItemPointer entity = entityState->getEntity(); - assert(entity.get()); - if (entityState->isCandidateForOwnership()) { - _outgoingChanges.insert(entityState); + { + PROFILE_RANGE_EX(simulation_physics, "Filter", 0x00000000, (uint64_t)motionStates.size()); + for (auto stateItr : motionStates) { + ObjectMotionState* state = &(*stateItr); + assert(state); + if (state->getType() == MOTIONSTATE_TYPE_ENTITY) { + EntityMotionState* entityState = static_cast(state); + EntityItemPointer entity = entityState->getEntity(); + assert(entity.get()); + if (entityState->isCandidateForOwnership()) { + _outgoingChanges.insert(entityState); + } + _entitiesToSort.insert(entity); } - _entitiesToSort.insert(entity); } } @@ -302,6 +308,7 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta } // look for entities to prune or update + PROFILE_RANGE_EX(simulation_physics, "Prune/Send", 0x00000000, (uint64_t)_outgoingChanges.size()); QSet::iterator stateItr = _outgoingChanges.begin(); while (stateItr != _outgoingChanges.end()) { EntityMotionState* state = *stateItr; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index a64796308e..fe794772e2 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -11,7 +11,12 @@ #include +#include + +#include + #include +#include #include "CharacterController.h" #include "ObjectMotionState.h" @@ -290,6 +295,7 @@ void PhysicsEngine::stepSimulation() { float timeStep = btMin(dt, MAX_TIMESTEP); if (_myAvatarController) { + DETAILED_PROFILE_RANGE(simulation_physics, "avatarController"); BT_PROFILE("avatarController"); // TODO: move this stuff outside and in front of stepSimulation, because // the updateShapeIfNecessary() call needs info from MyAvatar and should @@ -328,45 +334,107 @@ void PhysicsEngine::stepSimulation() { } } +class CProfileOperator { +public: + CProfileOperator() {} + void recurse(CProfileIterator* itr, QString context) { + // The context string will get too long if we accumulate it properly + //QString newContext = context + QString("/") + itr->Get_Current_Parent_Name(); + // so we use this four-character indentation + QString newContext = context + QString(".../"); + process(itr, newContext); + + // count the children + int32_t numChildren = 0; + itr->First(); + while (!itr->Is_Done()) { + itr->Next(); + ++numChildren; + } + + // recurse the children + if (numChildren > 0) { + // recurse the children + for (int32_t i = 0; i < numChildren; ++i) { + itr->Enter_Child(i); + recurse(itr, newContext); + } + } + // retreat back to parent + itr->Enter_Parent(); + } + virtual void process(CProfileIterator*, QString context) = 0; +}; + +class StatsHarvester : public CProfileOperator { +public: + StatsHarvester() {} + void process(CProfileIterator* itr, QString context) override { + QString name = context + itr->Get_Current_Parent_Name(); + uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time()); + PerformanceTimer::addTimerRecord(name, time); + }; +}; + +class StatsWriter : public CProfileOperator { +public: + StatsWriter(QString filename) : _file(filename) { + _file.open(QFile::WriteOnly); + if (_file.error() != QFileDevice::NoError) { + qCDebug(physics) << "unable to open file " << _file.fileName() << " to save stepSimulation() stats"; + } + } + ~StatsWriter() { + _file.close(); + } + void process(CProfileIterator* itr, QString context) override { + QString name = context + itr->Get_Current_Parent_Name(); + float time = (btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time(); + if (_file.error() == QFileDevice::NoError) { + QTextStream s(&_file); + s << name << " = " << time << "\n"; + } + } +protected: + QFile _file; +}; + void PhysicsEngine::harvestPerformanceStats() { // unfortunately the full context names get too long for our stats presentation format //QString contextName = PerformanceTimer::getContextName(); // TODO: how to show full context name? QString contextName("..."); - CProfileIterator* profileIterator = CProfileManager::Get_Iterator(); - if (profileIterator) { + CProfileIterator* itr = CProfileManager::Get_Iterator(); + if (itr) { // hunt for stepSimulation context - profileIterator->First(); - for (int32_t childIndex = 0; !profileIterator->Is_Done(); ++childIndex) { - if (QString(profileIterator->Get_Current_Name()) == "stepSimulation") { - profileIterator->Enter_Child(childIndex); - recursivelyHarvestPerformanceStats(profileIterator, contextName); + itr->First(); + for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) { + if (QString(itr->Get_Current_Name()) == "stepSimulation") { + itr->Enter_Child(childIndex); + StatsHarvester harvester; + harvester.recurse(itr, "step/"); break; } - profileIterator->Next(); + itr->Next(); } } } -void PhysicsEngine::recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName) { - QString parentContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Parent_Name()); - // get the stats for the children - int32_t numChildren = 0; - profileIterator->First(); - while (!profileIterator->Is_Done()) { - QString childContextName = parentContextName + QString("/") + QString(profileIterator->Get_Current_Name()); - uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * profileIterator->Get_Current_Total_Time()); - PerformanceTimer::addTimerRecord(childContextName, time); - profileIterator->Next(); - ++numChildren; +void PhysicsEngine::printPerformanceStatsToFile(const QString& filename) { + CProfileIterator* itr = CProfileManager::Get_Iterator(); + if (itr) { + // hunt for stepSimulation context + itr->First(); + for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) { + if (QString(itr->Get_Current_Name()) == "stepSimulation") { + itr->Enter_Child(childIndex); + StatsWriter writer(filename); + writer.recurse(itr, ""); + break; + } + itr->Next(); + } } - // recurse the children - for (int32_t i = 0; i < numChildren; ++i) { - profileIterator->Enter_Child(i); - recursivelyHarvestPerformanceStats(profileIterator, contextName); - } - // retreat back to parent - profileIterator->Enter_Parent(); } void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) { @@ -399,6 +467,7 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const } void PhysicsEngine::updateContactMap() { + DETAILED_PROFILE_RANGE(simulation_physics, "updateContactMap"); BT_PROFILE("updateContactMap"); ++_numContactFrames; @@ -515,10 +584,21 @@ const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() { void PhysicsEngine::dumpStatsIfNecessary() { if (_dumpNextStats) { _dumpNextStats = false; + CProfileManager::Increment_Frame_Counter(); + if (_saveNextStats) { + _saveNextStats = false; + printPerformanceStatsToFile(_statsFilename); + } CProfileManager::dumpAll(); } } +void PhysicsEngine::saveNextPhysicsStats(QString filename) { + _saveNextStats = true; + _dumpNextStats = true; + _statsFilename = filename; +} + // Bullet collision flags are as follows: // CF_STATIC_OBJECT= 1, // CF_KINEMATIC_OBJECT= 2, diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 3063a4a89a..6619a5489d 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -62,6 +62,7 @@ public: void stepSimulation(); void harvestPerformanceStats(); + void printPerformanceStatsToFile(const QString& filename); void updateContactMap(); bool hasOutgoingChanges() const { return _hasOutgoingChanges; } @@ -76,6 +77,9 @@ public: /// \brief prints timings for last frame if stats have been requested. void dumpStatsIfNecessary(); + /// \brief saves timings for last frame in filename + void saveNextPhysicsStats(QString filename); + /// \param offset position of simulation origin in domain-frame void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; } @@ -94,7 +98,6 @@ public: private: QList removeDynamicsForBody(btRigidBody* body); void addObjectToDynamicsWorld(ObjectMotionState* motionState); - void recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName); /// \brief bump any objects that touch this one, then remove contact info void bumpAndPruneContacts(ObjectMotionState* motionState); @@ -116,6 +119,7 @@ private: QHash _objectDynamics; QHash> _objectDynamicsByBody; std::set _activeStaticBodies; + QString _statsFilename; glm::vec3 _originOffset; @@ -124,8 +128,9 @@ private: uint32_t _numContactFrames = 0; uint32_t _numSubsteps; - bool _dumpNextStats = false; - bool _hasOutgoingChanges = false; + bool _dumpNextStats { false }; + bool _saveNextStats { false }; + bool _hasOutgoingChanges { false }; }; diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 24cfbc2609..5b8c0d5843 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -18,6 +18,7 @@ #include #include "ThreadSafeDynamicsWorld.h" +#include "Profile.h" ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( btDispatcher* dispatcher, @@ -29,6 +30,7 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep, SubStepCallback onSubStep) { + DETAILED_PROFILE_RANGE(simulation_physics, "stepWithCB"); BT_PROFILE("stepSimulationWithSubstepCallback"); int subSteps = 0; if (maxSubSteps) { @@ -68,11 +70,13 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep saveKinematicState(fixedTimeStep*clampedSimulationSteps); { + DETAILED_PROFILE_RANGE(simulation_physics, "applyGravity"); BT_PROFILE("applyGravity"); applyGravity(); } for (int i=0;i_scene->getStage(); if (hazeStage && hazeStage->_currentFrame._hazes.size() > 0) { model::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); - batch.setUniformBuffer(HazeEffect_ParamsSlot, hazePointer->getHazeParametersBuffer()); + if (hazePointer) { + batch.setUniformBuffer(HazeEffect_ParamsSlot, hazePointer->getHazeParametersBuffer()); + } else { + // Something is wrong, so just quit Haze + return; + } } batch.setUniformBuffer(HazeEffect_TransformBufferSlot, transformBuffer->getFrameTransformBuffer()); @@ -178,7 +183,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu if (lightStage) { model::LightPointer keyLight; keyLight = lightStage->getCurrentKeyLight(); - if (keyLight != nullptr) { + if (keyLight) { batch.setUniformBuffer(HazeEffect_LightingMapSlot, keyLight->getLightSchemaBuffer()); } } diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 1d3c8fda32..62384f9d97 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -22,7 +22,7 @@ #include "ScriptEngine.h" #include "XMLHttpRequestClass.h" -const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/api/"; +const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/"; Q_DECLARE_METATYPE(QByteArray*) diff --git a/libraries/shared/src/Trace.cpp b/libraries/shared/src/Trace.cpp index d7feb65ff3..3f6a2dd643 100644 --- a/libraries/shared/src/Trace.cpp +++ b/libraries/shared/src/Trace.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -31,6 +30,8 @@ #include "Gzip.h" #include "PortableHighResolutionClock.h" +#include "SharedLogging.h" +#include "shared/FileUtils.h" #include "shared/GlobalAppProperties.h" using namespace tracing; @@ -104,30 +105,13 @@ void TraceEvent::writeJson(QTextStream& out) const { #endif } -void Tracer::serialize(const QString& originalPath) { - - QString path = originalPath; - - // Filter for specific tokens potentially present in the path: - auto now = QDateTime::currentDateTime(); - - path = path.replace("{DATE}", now.date().toString("yyyyMMdd")); - path = path.replace("{TIME}", now.time().toString("HHmm")); - - // If the filename is relative, turn it into an absolute path relative to the document directory. - QFileInfo originalFileInfo(path); - if (originalFileInfo.isRelative()) { - QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - path = docsLocation + "/" + path; - QFileInfo info(path); - if (!info.absoluteDir().exists()) { - QString originalRelativePath = originalFileInfo.path(); - QDir(docsLocation).mkpath(originalRelativePath); - } +void Tracer::serialize(const QString& filename) { + QString fullPath = FileUtils::replaceDateTimeTokens(filename); + fullPath = FileUtils::computeDocumentPath(fullPath); + if (!FileUtils::canCreateFile(fullPath)) { + return; } - - std::list currentEvents; { std::lock_guard guard(_eventsMutex); @@ -137,11 +121,6 @@ void Tracer::serialize(const QString& originalPath) { } } - // If the file exists and we can't remove it, fail early - if (QFileInfo(path).exists() && !QFile::remove(path)) { - return; - } - // If we can't open a temp file for writing, fail early QByteArray data; { @@ -159,15 +138,16 @@ void Tracer::serialize(const QString& originalPath) { out << "\n]"; } - if (path.endsWith(".gz")) { + if (fullPath.endsWith(".gz")) { QByteArray compressed; gzip(data, compressed); data = compressed; - } - + } + { - QFile file(path); + QFile file(fullPath); if (!file.open(QIODevice::WriteOnly)) { + qDebug(shared) << "failed to open file '" << fullPath << "'"; return; } file.write(data); @@ -191,7 +171,6 @@ void Tracer::serialize(const QString& originalPath) { } } } }; - data = document.toJson(QJsonDocument::Compact); } #endif diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index 8c962dfd6d..dba0af7b16 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -12,6 +12,7 @@ #include "FileUtils.h" +#include #include #include #include @@ -20,6 +21,8 @@ #include #include +#include "../SharedLogging.h" + QString FileUtils::readFile(const QString& filename) { QFile file(filename); @@ -82,20 +85,54 @@ QString FileUtils::standardPath(QString subfolder) { // standard path // Mac: ~/Library/Application Support/Interface QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - if (!subfolder.startsWith("/")) { subfolder.prepend("/"); } - if (!subfolder.endsWith("/")) { subfolder.append("/"); } - path.append(subfolder); QDir logDir(path); if (!logDir.exists(path)) { logDir.mkpath(path); } - return path; } + +QString FileUtils::replaceDateTimeTokens(const QString& originalPath) { + // Filter for specific tokens potentially present in the path: + auto now = QDateTime::currentDateTime(); + QString path = originalPath; + path.replace("{DATE}", now.date().toString("yyyyMMdd")); + path.replace("{TIME}", now.time().toString("HHmm")); + return path; +} + + +QString FileUtils::computeDocumentPath(const QString& originalPath) { + // If the filename is relative, turn it into an absolute path relative to the document directory. + QString path = originalPath; + QFileInfo originalFileInfo(originalPath); + if (originalFileInfo.isRelative()) { + QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + path = docsLocation + "/" + originalPath; + } + return path; +} + +bool FileUtils::canCreateFile(const QString& fullPath) { + // If the file exists and we can't remove it, fail early + QFileInfo fileInfo(fullPath); + if (fileInfo.exists() && !QFile::remove(fullPath)) { + qDebug(shared) << "unable to overwrite file '" << fullPath << "'"; + return false; + } + QDir dir(fileInfo.absolutePath()); + if (!dir.exists()) { + if (!dir.mkpath(fullPath)) { + qDebug(shared) << "unable to create dir '" << dir.absolutePath() << "'"; + return false; + } + } + return true; +} diff --git a/libraries/shared/src/shared/FileUtils.h b/libraries/shared/src/shared/FileUtils.h index 4f2c1b7af5..d68fcd8a44 100644 --- a/libraries/shared/src/shared/FileUtils.h +++ b/libraries/shared/src/shared/FileUtils.h @@ -21,6 +21,9 @@ public: static QString standardPath(QString subfolder); static QString readFile(const QString& filename); static QStringList readLines(const QString& filename, QString::SplitBehavior splitBehavior = QString::KeepEmptyParts); + static QString replaceDateTimeTokens(const QString& path); + static QString computeDocumentPath(const QString& path); + static bool canCreateFile(const QString& fullPath); }; #endif // hifi_FileUtils_h diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 942c9f71a5..12220a8079 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -679,7 +679,9 @@ void OffscreenQmlSurface::create() { // Setup the update of the QML media components with the current audio output device QObject::connect(&_audioOutputUpdateTimer, &QTimer::timeout, this, [this]() { - new AudioHandler(sharedFromThis(), _currentAudioOutputDevice); + if (_currentAudioOutputDevice.size() > 0) { + new AudioHandler(sharedFromThis(), _currentAudioOutputDevice); + } }); int waitForAudioQmlMs = 200; _audioOutputUpdateTimer.setInterval(waitForAudioQmlMs); @@ -695,6 +697,7 @@ void OffscreenQmlSurface::create() { } void OffscreenQmlSurface::changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate) { + _currentAudioOutputDevice = deviceName; if (_rootItem != nullptr && !isHtmlUpdate) { QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); } @@ -702,18 +705,16 @@ void OffscreenQmlSurface::changeAudioOutputDevice(const QString& deviceName, boo } void OffscreenQmlSurface::forceHtmlAudioOutputDeviceUpdate() { - auto audioIO = DependencyManager::get(); - QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); - QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, - Q_ARG(QString, deviceName), Q_ARG(bool, true)); + if (_currentAudioOutputDevice.size() > 0) { + QMetaObject::invokeMethod(this, "changeAudioOutputDevice", Qt::QueuedConnection, + Q_ARG(QString, _currentAudioOutputDevice), Q_ARG(bool, true)); + } } void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { if (QThread::currentThread() != qApp->thread()) { QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); } else { - auto audioIO = DependencyManager::get(); - _currentAudioOutputDevice = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); if (_audioOutputUpdateTimer.isActive()) { _audioOutputUpdateTimer.stop(); } diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 0a0e67756d..d8658f1c14 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -20,7 +20,7 @@ namespace { bool isAuthableHighFidelityURL(const QUrl& url) { - auto metaverseServerURL = NetworkingConstants::METAVERSE_SERVER_URL; + auto metaverseServerURL = NetworkingConstants::METAVERSE_SERVER_URL(); static const QStringList HF_HOSTS = { "highfidelity.com", "highfidelity.io", metaverseServerURL.toString(), "metaverse.highfidelity.io" diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 915b3b3680..51f927f224 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -412,6 +412,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); triggers: [{action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"}], posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true), hover: true, + scaleWithAvatar: true, distanceScaleEnd: true, hand: LEFT_HAND }); @@ -421,6 +422,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); triggers: [{action: Controller.Standard.RTClick, button: "Focus"}, {action: Controller.Standard.RTClick, button: "Primary"}], posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true), hover: true, + scaleWithAvatar: true, distanceScaleEnd: true, hand: RIGHT_HAND }); @@ -431,6 +433,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true), triggers: [{action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"}], hover: true, + scaleWithAvatar: true, distanceScaleEnd: true, hand: LEFT_HAND }); @@ -441,6 +444,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true), triggers: [{action: Controller.Standard.RTClick, button: "Focus"}, {action: Controller.Standard.RTClick, button: "Primary"}], hover: true, + scaleWithAvatar: true, distanceScaleEnd: true, hand: RIGHT_HAND }); diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 7733ca1d28..a51cea67f8 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -269,6 +269,7 @@ function Grabber() { joint: "Mouse", filter: Picks.PICK_ENTITIES, faceAvatar: true, + scaleWithAvatar: true, enabled: true, renderStates: renderStates }); diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 878c3b51f1..96c1916889 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -199,7 +199,7 @@ var purchasesElement = document.createElement('a'); var dropDownElement = document.getElementById('user-dropdown'); - $('#user-dropdown').find('.username')[0].style = "max-width:80px;white-space:nowrap;overflow:hidden;" + + $('#user-dropdown').find('.username')[0].style = "max-width:80px;white-space:nowrap;overflow:hidden;" + "text-overflow:ellipsis;display:inline-block;position:relative;top:4px;"; $('#user-dropdown').find('.caret')[0].style = "position:relative;top:-3px;"; @@ -265,8 +265,10 @@ }); $('.grid-item').find('#price-or-edit').find('a').each(function() { - $(this).attr('data-href', $(this).attr('href')); - $(this).attr('href', '#'); + if ($(this).attr('href') !== '#') { // Guard necessary because of the AJAX nature of Marketplace site + $(this).attr('data-href', $(this).attr('href')); + $(this).attr('href', '#'); + } cost = $(this).closest('.col-xs-3').find('.item-cost').text(); $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); @@ -387,27 +389,40 @@ var href = purchaseButton.attr('href'); purchaseButton.attr('href', '#'); - purchaseButton.css({ - "background": "linear-gradient(#00b4ef, #0093C5)", - "color": "#FFF", - "font-weight": "600", - "padding-bottom": "10px" - }); + var availability = $.trim($('.item-availability').text()); + if (availability === 'available') { + purchaseButton.css({ + "background": "linear-gradient(#00b4ef, #0093C5)", + "color": "#FFF", + "font-weight": "600", + "padding-bottom": "10px" + }); + } else { + purchaseButton.css({ + "background": "linear-gradient(#a2a2a2, #fefefe)", + "color": "#000", + "font-weight": "600", + "padding-bottom": "10px" + }); + } var cost = $('.item-cost').text(); - - if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { + if (availability !== 'available') { + purchaseButton.html('UNAVAILABLE (' + availability + ')'); + } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { purchaseButton.html('PURCHASE ' + cost); } purchaseButton.on('click', function () { - buyButtonClicked(window.location.pathname.split("/")[3], - $('#top-center').find('h1').text(), - $('#creator').find('.value').text(), - cost, - href); - }); + if ('availabile' === availability) { + buyButtonClicked(window.location.pathname.split("/")[3], + $('#top-center').find('h1').text(), + $('#creator').find('.value').text(), + cost, + href); + } + }); maybeAddPurchasesButton(); } } diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 392ff3c0b5..d9003ffeaa 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -892,12 +892,13 @@ } function keyPressEvent(event) { - if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) { + if ((event.text.toUpperCase() === "X") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl + && !event.isAlt) { updateTriggers(1.0, true, Controller.Standard.RightHand); } } function keyReleaseEvent(event) { - if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) { + if (event.text.toUpperCase() === "X" && !event.isAutoRepeat) { updateTriggers(0.0, true, Controller.Standard.RightHand); } } diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp index e00560158f..88884a4fee 100644 --- a/tools/ac-client/src/ACClientApp.cpp +++ b/tools/ac-client/src/ACClientApp.cpp @@ -106,7 +106,7 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : auto accountManager = DependencyManager::get(); accountManager->setIsAgent(true); - accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); + accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL()); auto nodeList = DependencyManager::get(); diff --git a/tools/atp-client/src/ATPClientApp.cpp b/tools/atp-client/src/ATPClientApp.cpp index c5edf27b67..9fd1bf8d4f 100644 --- a/tools/atp-client/src/ATPClientApp.cpp +++ b/tools/atp-client/src/ATPClientApp.cpp @@ -145,7 +145,7 @@ ATPClientApp::ATPClientApp(int argc, char* argv[]) : auto accountManager = DependencyManager::get(); accountManager->setIsAgent(true); - accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); + accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL()); auto nodeList = DependencyManager::get(); diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 321f81ba8f..00344179bd 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -8,13 +8,14 @@ setup_memory_debugger() if (WIN32) package_libraries_for_deployment() -endif () - -if (UNIX) +elseif (UNIX AND NOT APPLE) find_package(Threads REQUIRED) if(THREADS_HAVE_PTHREAD_ARG) target_compile_options(PUBLIC oven "-pthread") endif() -endif () +elseif (APPLE) + # Fix up the rpath so macdeployqt works + set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") +endif() install_beside_console()