From d65101c4e9c7f8a259051e7c7f0c13f2555e1912 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 20 Nov 2017 10:42:58 -0800 Subject: [PATCH 01/70] sort and throttle avatar updates in interface --- interface/src/avatar/AvatarManager.cpp | 63 +++++++++++++------------ libraries/shared/src/PrioritySortUtil.h | 6 +-- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 9ffe74d470..6441415cb8 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -142,32 +143,36 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceTimer perfTimer("otherAvatars"); - auto avatarMap = getHashCopy(); - QList avatarList = avatarMap.values(); + class SortableAvatar: public PrioritySortUtil::Sortable { + public: + SortableAvatar() = delete; + SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {} + glm::vec3 getPosition() const override { return _avatar->getPosition(); } + float getRadius() const override { return std::static_pointer_cast(_avatar)->getBoundingRadius(); } + uint64_t getTimestamp() const override { return std::static_pointer_cast(_avatar)->getLastRenderUpdateTime(); } + const AvatarSharedPointer& getAvatar() const { return _avatar; } + private: + AvatarSharedPointer _avatar; + }; + ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); + PrioritySortUtil::PriorityQueue sortedAvatars(cameraView); - std::priority_queue sortedAvatars; - AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, - - [](AvatarSharedPointer avatar)->uint64_t{ - return std::static_pointer_cast(avatar)->getLastRenderUpdateTime(); - }, - - [](AvatarSharedPointer avatar)->float{ - return std::static_pointer_cast(avatar)->getBoundingRadius(); - }, - - [this](AvatarSharedPointer avatar)->bool{ - const auto& castedAvatar = std::static_pointer_cast(avatar); - if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) { - // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. - // DO NOT update or fade out uninitialized Avatars - return true; // ignore it - } - return false; - }); + // sort + auto avatarMap = getHashCopy(); + AvatarHash::iterator itr = avatarMap.begin(); + while (itr != avatarMap.end()) { + const auto& avatar = std::static_pointer_cast(*itr); + // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. + // DO NOT update or fade out uninitialized Avatars + if (avatar != _myAvatar && avatar->isInitialized()) { + sortedAvatars.push(SortableAvatar(avatar)); + } + ++itr; + } + // process in sorted order uint64_t startTime = usecTimestampNow(); const uint64_t UPDATE_BUDGET = 2000; // usec uint64_t updateExpiry = startTime + UPDATE_BUDGET; @@ -176,8 +181,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { render::Transaction transaction; while (!sortedAvatars.empty()) { - const AvatarPriority& sortData = sortedAvatars.top(); - const auto& avatar = std::static_pointer_cast(sortData.avatar); + const SortableAvatar& sortData = sortedAvatars.top(); + const auto& avatar = std::static_pointer_cast(sortData.getAvatar()); bool ignoring = DependencyManager::get()->isPersonalMutingNode(avatar->getID()); if (ignoring) { @@ -207,7 +212,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { uint64_t now = usecTimestampNow(); if (now < updateExpiry) { // we're within budget - bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (inView && avatar->hasNewJointData()) { numAvatarsUpdated++; } @@ -221,7 +226,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // --> some avatar velocity measurements may be a little off // no time simulate, but we take the time to count how many were tragically missed - bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (!inView) { break; } @@ -230,9 +235,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } sortedAvatars.pop(); while (inView && !sortedAvatars.empty()) { - const AvatarPriority& newSortData = sortedAvatars.top(); - const auto& newAvatar = std::static_pointer_cast(newSortData.avatar); - inView = newSortData.priority > OUT_OF_VIEW_THRESHOLD; + const SortableAvatar& newSortData = sortedAvatars.top(); + const auto& newAvatar = std::static_pointer_cast(newSortData.getAvatar()); + inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (inView && newAvatar->hasNewJointData()) { numAVatarsNotUpdated++; } diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 1d11a04265..6026d2b21f 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -32,11 +32,10 @@ (2) Make a PrioritySortUtil::PriorityQueue and add them to the queue: - PrioritySortUtil::Prioritizer prioritizer(viewFrustum); + PrioritySortUtil::PriorityQueue sortedThings(viewFrustum); std::priority_queue< PrioritySortUtil::Sortable > sortedThings; for (thing in things) { - float priority = prioritizer.computePriority(PrioritySortUtil::PrioritizableThing(thing)); - sortedThings.push(PrioritySortUtil::Sortable entry(thing, priority)); + sortedThings.push(SortableWrapper(thing)); } (3) Loop over your priority queue and do timeboxed work: @@ -65,6 +64,7 @@ namespace PrioritySortUtil { virtual uint64_t getTimestamp() const = 0; void setPriority(float priority) { _priority = priority; } + float getPriority() const { return _priority; } bool operator<(const Sortable& other) const { return _priority < other._priority; } private: float _priority { 0.0f }; From e068eb879cb613176ad76b7a357e8a5c2e5d29d1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 20 Nov 2017 14:23:56 -0800 Subject: [PATCH 02/70] use PrioritySortUtil for outgoing avatar updates --- .../src/avatars/AvatarMixerClientData.h | 2 +- .../src/avatars/AvatarMixerSlave.cpp | 90 ++++++++++++------- libraries/shared/src/PrioritySortUtil.h | 3 + 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index d5c7784da7..1a9da292ac 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -110,7 +110,7 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } - ViewFrustum getViewFrustom() const { return _currentViewFrustum; } + ViewFrustum getViewFrustum() const { return _currentViewFrustum; } quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { quint64 result = 0; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index dd045c24ea..341231dc1b 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,10 @@ #include "AvatarMixerClientData.h" #include "AvatarMixerSlave.h" +namespace PrioritySortUtil { + // we declare this callback here but override it later + std::function getAvatarAgeCallback = [] (const AvatarSharedPointer& avatar) { return 0; }; +} void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) { _begin = begin; @@ -185,7 +190,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes // for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes - QList avatarList; + std::vector avatarsToSort; std::unordered_map avatarDataToNodes; std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { @@ -195,36 +200,59 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); - avatarList << otherAvatar; + avatarsToSort.push_back(otherAvatar); avatarDataToNodes[otherAvatar] = otherNode; } }); - AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); - ViewFrustum cameraView = nodeData->getViewFrustom(); - std::priority_queue sortedAvatars; - AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, - [&](AvatarSharedPointer avatar)->uint64_t { - auto avatarNode = avatarDataToNodes[avatar]; - assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map - return nodeData->getLastBroadcastTime(avatarNode->getUUID()); - }, [&](AvatarSharedPointer avatar)->float{ - glm::vec3 nodeBoxHalfScale = (avatar->getWorldPosition() - avatar->getGlobalBoundingBoxCorner() * avatar->getSensorToWorldScale()); - return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); - }, [&](AvatarSharedPointer avatar)->bool { + // now that we've assembled the avatarDataToNodes map we can replace PrioritySortUtil::getAvatarAgeCallback + // with the true implementation + PrioritySortUtil::getAvatarAgeCallback = [&] (const AvatarSharedPointer& avatar) { + auto avatarNode = avatarDataToNodes[avatar]; + assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map + return nodeData->getLastBroadcastTime(avatarNode->getUUID()); + }; + + class SortableAvatar: public PrioritySortUtil::Sortable { + public: + SortableAvatar() = delete; + SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {} + glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } + float getRadius() const override { + glm::vec3 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale()); + return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); + } + uint64_t getTimestamp() const override { + // use the callback implemented above + return PrioritySortUtil::getAvatarAgeCallback(_avatar); + } + const AvatarSharedPointer& getAvatar() const { return _avatar; } + + private: + AvatarSharedPointer _avatar; + }; + + // prepare to sort + ViewFrustum cameraView = nodeData->getViewFrustum(); + PrioritySortUtil::PriorityQueue sortedAvatars(cameraView); + + // ignore or sort + const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer(); + for (size_t i = 0; i < avatarsToSort.size(); ++i) { + const AvatarSharedPointer& avatar = avatarsToSort[i]; if (avatar == thisAvatar) { - return true; // ignore ourselves... + // don't echo updates to self + continue; } bool shouldIgnore = false; - - // We will also ignore other nodes for a couple of different reasons: + // We ignore other nodes for a couple of reasons: // 1) ignore bubbles and ignore specific node // 2) the node hasn't really updated it's frame data recently, this can // happen if for example the avatar is connected on a desktop and sending // updates at ~30hz. So every 3 frames we skip a frame. - auto avatarNode = avatarDataToNodes[avatar]; + auto avatarNode = avatarDataToNodes[avatar]; assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); @@ -240,7 +268,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { shouldIgnore = true; } else { - // Check to see if the space bubble is enabled // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { @@ -267,8 +294,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); } } - quint64 endIgnoreCalculation = usecTimestampNow(); - _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); if (!shouldIgnore) { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); @@ -292,20 +317,21 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) ++numAvatarsWithSkippedFrames; } } - return shouldIgnore; - }); + quint64 endIgnoreCalculation = usecTimestampNow(); + _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + + if (!shouldIgnore) { + // sort this one for later + sortedAvatars.push(SortableAvatar(avatar)); + } + } // loop through our sorted avatars and allocate our bandwidth to them accordingly - int avatarRank = 0; - // this is overly conservative, because it includes some avatars we might not consider int remainingAvatars = (int)sortedAvatars.size(); - while (!sortedAvatars.empty()) { - AvatarPriority sortData = sortedAvatars.top(); + const auto& avatarData = sortedAvatars.top().getAvatar(); sortedAvatars.pop(); - const auto& avatarData = sortData.avatar; - avatarRank++; remainingAvatars--; auto otherNode = avatarDataToNodes[avatarData]; @@ -332,10 +358,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow()); } + // determine if avatar is in view which determines how much data to send glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); - - - // determine if avatar is in view, to determine how much data to include... glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale(); AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); bool isInView = nodeData->otherAvatarInView(otherNodeBox); @@ -412,7 +436,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - }; + } quint64 startPacketSending = usecTimestampNow(); diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 6026d2b21f..409450ac08 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -12,6 +12,9 @@ #define hifi_PrioritySortUtil_h #include +#include + +#include "NumericalConstants.h" #include "ViewFrustum.h" /* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: From deccc549654700c92d30c929e80aceabdb65fef0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 20 Nov 2017 15:08:52 -0800 Subject: [PATCH 03/70] remove cruft --- .../src/avatars/AvatarMixerSlave.cpp | 2 - libraries/avatars/src/AvatarData.cpp | 53 ------------------- libraries/avatars/src/AvatarData.h | 8 --- 3 files changed, 63 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 341231dc1b..77bf447547 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -189,10 +189,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes - // for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes std::vector avatarsToSort; std::unordered_map avatarDataToNodes; - std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { // make sure this is an agent that we have avatar data for before considering it for inclusion if (otherNode->getType() == NodeType::Agent diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index cb43aeee5d..786b7b6912 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2391,59 +2391,6 @@ float AvatarData::_avatarSortCoefficientSize { 0.5f }; float AvatarData::_avatarSortCoefficientCenter { 0.25 }; float AvatarData::_avatarSortCoefficientAge { 1.0f }; -void AvatarData::sortAvatars( - QList avatarList, - const ViewFrustum& cameraView, - std::priority_queue& sortedAvatarsOut, - std::function getLastUpdated, - std::function getBoundingRadius, - std::function shouldIgnore) { - - PROFILE_RANGE(simulation, "sort"); - uint64_t now = usecTimestampNow(); - - glm::vec3 frustumCenter = cameraView.getPosition(); - const glm::vec3& forward = cameraView.getDirection(); - for (int32_t i = 0; i < avatarList.size(); ++i) { - const auto& avatar = avatarList.at(i); - - if (shouldIgnore(avatar)) { - continue; - } - - // priority = weighted linear combination of: - // (a) apparentSize - // (b) proximity to center of view - // (c) time since last update - glm::vec3 avatarPosition = avatar->getWorldPosition(); - glm::vec3 offset = avatarPosition - frustumCenter; - float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero - - // FIXME - AvatarData has something equivolent to this - float radius = getBoundingRadius(avatar); - - float apparentSize = 2.0f * radius / distance; - float cosineAngle = glm::dot(offset, forward) / distance; - float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); - - // NOTE: we are adding values of different units to get a single measure of "priority". - // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. - // These weights are pure magic tuning and should be hard coded in the relation below, - // but are currently exposed for anyone who would like to explore fine tuning: - float priority = _avatarSortCoefficientSize * apparentSize - + _avatarSortCoefficientCenter * cosineAngle - + _avatarSortCoefficientAge * age; - - // decrement priority of avatars outside keyhole - if (distance > cameraView.getCenterRadius()) { - if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { - priority += OUT_OF_VIEW_PENALTY; - } - } - sortedAvatarsOut.push(AvatarPriority(avatar, priority)); - } -} - QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { QScriptValue obj = engine->newObject(); for (auto entityID : value.keys()) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 2f3154ad08..1df0e94496 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -629,14 +629,6 @@ public: static const float OUT_OF_VIEW_PENALTY; - static void sortAvatars( - QList avatarList, - const ViewFrustum& cameraView, - std::priority_queue& sortedAvatarsOut, - std::function getLastUpdated, - std::function getBoundingRadius, - std::function shouldIgnore); - // TODO: remove this HACK once we settle on optimal sort coefficients // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. static float _avatarSortCoefficientSize; From e4436d264a9df7d2a3b856201eaa9a61b4c76bc3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Nov 2017 09:03:13 -0800 Subject: [PATCH 04/70] simplify for-loop format --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 77bf447547..9131358cac 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -236,8 +236,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // ignore or sort const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer(); - for (size_t i = 0; i < avatarsToSort.size(); ++i) { - const AvatarSharedPointer& avatar = avatarsToSort[i]; + for (const auto& avatar : avatarsToSort) { if (avatar == thisAvatar) { // don't echo updates to self continue; From e212ac67c1a3b781bb4146c75fb260e3af3be0e6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Nov 2017 11:30:06 -0800 Subject: [PATCH 05/70] enable custom avatar sort tuning --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 5 ++++- interface/src/avatar/AvatarManager.cpp | 7 +++++-- libraries/avatars/src/AvatarData.cpp | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 9131358cac..769789ef6f 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -232,7 +232,10 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // prepare to sort ViewFrustum cameraView = nodeData->getViewFrustum(); - PrioritySortUtil::PriorityQueue sortedAvatars(cameraView); + PrioritySortUtil::PriorityQueue sortedAvatars(cameraView, + AvatarData::_avatarSortCoefficientSize, + AvatarData::_avatarSortCoefficientCenter, + AvatarData::_avatarSortCoefficientAge); // ignore or sort const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6441415cb8..8a294182bd 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -147,7 +147,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { public: SortableAvatar() = delete; SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {} - glm::vec3 getPosition() const override { return _avatar->getPosition(); } + glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } float getRadius() const override { return std::static_pointer_cast(_avatar)->getBoundingRadius(); } uint64_t getTimestamp() const override { return std::static_pointer_cast(_avatar)->getLastRenderUpdateTime(); } const AvatarSharedPointer& getAvatar() const { return _avatar; } @@ -157,7 +157,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); - PrioritySortUtil::PriorityQueue sortedAvatars(cameraView); + PrioritySortUtil::PriorityQueue sortedAvatars(cameraView, + AvatarData::_avatarSortCoefficientSize, + AvatarData::_avatarSortCoefficientCenter, + AvatarData::_avatarSortCoefficientAge); // sort auto avatarMap = getHashCopy(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 786b7b6912..f68dce8d26 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2387,7 +2387,7 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f; -float AvatarData::_avatarSortCoefficientSize { 0.5f }; +float AvatarData::_avatarSortCoefficientSize { 1.0f }; float AvatarData::_avatarSortCoefficientCenter { 0.25 }; float AvatarData::_avatarSortCoefficientAge { 1.0f }; From 7d34469679c7145252d10bf39d1089a3128f8b95 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 28 Nov 2017 12:19:16 -0800 Subject: [PATCH 06/70] Head tracking bug fix for vive HMD users (cherry picked from commit 8364700b7aa8a7b8311c04235c6570d4610f2771) --- plugins/openvr/src/ViveControllerManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 6767aafad2..772facc21f 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -194,7 +194,7 @@ private: bool _overrideHands { false }; mutable std::recursive_mutex _lock; - bool _hmdTrackingEnabled { false }; + bool _hmdTrackingEnabled { true }; QString configToString(Config config); friend class ViveControllerManager; From 5cceceb868918fce2acf47b9173d1b805ad7e0d1 Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 28 Nov 2017 13:00:41 -0800 Subject: [PATCH 07/70] secondary cam texture fixes --- .../src/model-networking/TextureCache.cpp | 22 +++++++++++++------ .../src/model-networking/TextureCache.h | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 4184351c2d..7e82d044c7 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -1006,14 +1006,11 @@ NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl) if (!_spectatorCameraNetworkTexture) { _spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); } - if (_spectatorCameraFramebuffer) { - texture = _spectatorCameraFramebuffer->getRenderBuffer(0); - if (texture) { - texture->setSource(SPECTATOR_CAMERA_FRAME_URL.toString().toStdString()); - _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); - return _spectatorCameraNetworkTexture; - } + if (!_spectatorCameraFramebuffer) { + getSpectatorCameraFramebuffer(); // initialize frame buffer } + updateSpectatorCameraNetworkTexture(); + return _spectatorCameraNetworkTexture; } // FIXME: Generalize this, DRY up this code if (resourceTextureUrl == HMD_PREVIEW_FRAME_URL) { @@ -1052,7 +1049,18 @@ const gpu::FramebufferPointer& TextureCache::getSpectatorCameraFramebuffer(int w // If we aren't taking a screenshot, we might need to resize or create the camera buffer if (!_spectatorCameraFramebuffer || _spectatorCameraFramebuffer->getWidth() != width || _spectatorCameraFramebuffer->getHeight() != height) { _spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height)); + updateSpectatorCameraNetworkTexture(); emit spectatorCameraFramebufferReset(); } return _spectatorCameraFramebuffer; } + +void TextureCache::updateSpectatorCameraNetworkTexture() { + if (_spectatorCameraFramebuffer && _spectatorCameraNetworkTexture) { + gpu::TexturePointer texture = _spectatorCameraFramebuffer->getRenderBuffer(0); + if (texture) { + texture->setSource(SPECTATOR_CAMERA_FRAME_URL.toString().toStdString()); + _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); + } + } +} \ No newline at end of file diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 1102694f86..5a96fcf5e6 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -171,6 +171,7 @@ public: const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height); + void updateSpectatorCameraNetworkTexture(); static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 }; static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 }; From 710171929559cd934d72a89b3a32a578449722b7 Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 28 Nov 2017 13:01:47 -0800 Subject: [PATCH 08/70] newline --- .../model-networking/src/model-networking/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7e82d044c7..d25c5225d6 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -1063,4 +1063,4 @@ void TextureCache::updateSpectatorCameraNetworkTexture() { _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); } } -} \ No newline at end of file +} From c5ebb8a279b57feb981709568b61cbbec9bce836 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 28 Nov 2017 16:16:30 -0800 Subject: [PATCH 09/70] expose Window.getDeviceSize() --- interface/src/Application.cpp | 10 +++------- interface/src/Application.h | 4 +--- interface/src/Application_render.cpp | 3 +-- interface/src/scripting/WindowScriptingInterface.cpp | 4 ++++ interface/src/scripting/WindowScriptingInterface.h | 3 +++ 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d7fcbf6467..7bb88b0445 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -7075,11 +7075,11 @@ QRect Application::getRecommendedHUDRect() const { return result; } -QSize Application::getDeviceSize() const { +glm::vec2 Application::getDeviceSize() const { static const int MIN_SIZE = 1; - QSize result(MIN_SIZE, MIN_SIZE); + glm::vec2 result(MIN_SIZE); if (_displayPlugin) { - result = fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize()); + result = getActiveDisplayPlugin()->getRecommendedRenderSize(); } return result; } @@ -7098,10 +7098,6 @@ bool Application::hasFocus() const { return (QApplication::activeWindow() != nullptr); } -glm::vec2 Application::getViewportDimensions() const { - return toGlm(getDeviceSize()); -} - void Application::setMaxOctreePacketsPerSecond(int maxOctreePPS) { if (maxOctreePPS != _maxOctreePPS) { _maxOctreePPS = maxOctreePPS; diff --git a/interface/src/Application.h b/interface/src/Application.h index 5d9028f835..9542c5ccb6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -158,7 +158,7 @@ public: glm::uvec2 getUiSize() const; QRect getRecommendedHUDRect() const; - QSize getDeviceSize() const; + glm::vec2 getDeviceSize() const; bool hasFocus() const; void showCursor(const Cursor::Icon& cursor); @@ -228,8 +228,6 @@ public: FileLogger* getLogger() const { return _logger; } - glm::vec2 getViewportDimensions() const; - NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; } float getRenderResolutionScale() const; diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 44d9dfee03..1231e5834b 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -104,8 +104,7 @@ void Application::paintGL() { PerformanceTimer perfTimer("renderOverlay"); // NOTE: There is no batch associated with this renderArgs // the ApplicationOverlay class assumes it's viewport is setup to be the device size - QSize size = getDeviceSize(); - renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); + renderArgs._viewport = glm::ivec4(0, 0, getDeviceSize()); _applicationOverlay.renderOverlay(&renderArgs); } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index c99e190d12..4b355653b6 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -176,6 +176,10 @@ bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) { return offscreenUi->isPointOnDesktopWindow(point); } +glm::vec2 WindowScriptingInterface::getDeviceSize() const { + return qApp->getDeviceSize(); +} + /// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and /// might be in same thread as a script that sets the reticle to invisible void WindowScriptingInterface::ensureReticleVisible() const { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 61aaec7bea..d223f95af4 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -12,6 +12,8 @@ #ifndef hifi_WindowScriptingInterface_h #define hifi_WindowScriptingInterface_h +#include + #include #include #include @@ -73,6 +75,7 @@ public slots: bool isPhysicsEnabled(); bool setDisplayTexture(const QString& name); bool isPointOnDesktopWindow(QVariant point); + glm::vec2 getDeviceSize() const; int openMessageBox(QString title, QString text, int buttons, int defaultButton); void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton); From 982fad50507ec8408fbcb218814945dfba878bdd Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 29 Nov 2017 10:42:35 -0800 Subject: [PATCH 10/70] fix laser interaction on HUD in HMD mode --- .../system/controllers/controllerModules/hudOverlayPointer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index 38ee29ae3b..a1faacbdd6 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -62,7 +62,7 @@ this.pointingAtTablet = function(controllerData) { var rayPick = controllerData.rayPicks[this.hand]; - return (rayPick.objectID === HMD.tabletScreenID || rayPick.objectID === HMD.homeButtonID); + return (HMD.tabletScreenID && HMD.homeButtonID && (rayPick.objectID === HMD.tabletScreenID || rayPick.objectID === HMD.homeButtonID)); }; this.getOtherModule = function() { From 39afb4ab6a8ad06476f816b132c370fa6ea20a0c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 29 Nov 2017 15:46:22 -0800 Subject: [PATCH 11/70] Merge pull request #11882 from howard-stearns/the-lost-ECDSA The lost ecdsa --- interface/src/commerce/Wallet.cpp | 196 ++++++++---------- .../ui/overlays/ContextOverlayInterface.cpp | 18 +- .../entities/src/EntityItemProperties.cpp | 50 ++--- libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityTree.cpp | 110 ++++------ libraries/entities/src/EntityTree.h | 8 +- 6 files changed, 171 insertions(+), 212 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 85632ff8f1..d4611d3e9a 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -27,11 +27,11 @@ #include #include -#include #include #include #include #include +#include // I know, right? But per https://www.openssl.org/docs/faq.html // this avoids OPENSSL_Uplink(00007FF847238000,08): no OPENSSL_Applink @@ -78,18 +78,19 @@ int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { } } -RSA* readKeys(const char* filename) { +EC_KEY* readKeys(const char* filename) { FILE* fp; - RSA* key = NULL; + EC_KEY *key = NULL; if ((fp = fopen(filename, "rt"))) { // file opened successfully qCDebug(commerce) << "opened key file" << filename; - if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) { + + if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) { // now read private key qCDebug(commerce) << "read public key"; - if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) { + if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) { qCDebug(commerce) << "read private key"; fclose(fp); return key; @@ -137,18 +138,18 @@ bool Wallet::writeBackupInstructions() { return retval; } -bool writeKeys(const char* filename, RSA* keys) { +bool writeKeys(const char* filename, EC_KEY* keys) { FILE* fp; bool retval = false; if ((fp = fopen(filename, "wt"))) { - if (!PEM_write_RSAPublicKey(fp, keys)) { + if (!PEM_write_EC_PUBKEY(fp, keys)) { fclose(fp); qCDebug(commerce) << "failed to write public key"; QFile(QString(filename)).remove(); return retval; } - if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { + if (!PEM_write_ECPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { fclose(fp); qCDebug(commerce) << "failed to write private key"; QFile(QString(filename)).remove(); @@ -164,50 +165,29 @@ bool writeKeys(const char* filename, RSA* keys) { return retval; } -// copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp. -// We will have a different implementation in practice, but this gives us a start for now -// -// TODO: we don't really use the private keys returned - we can see how this evolves, but probably -// we should just return a list of public keys? -// or perhaps return the RSA* instead? -QPair generateRSAKeypair() { +QPair generateECKeypair() { - RSA* keyPair = RSA_new(); - BIGNUM* exponent = BN_new(); + EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); QPair retval; - - const unsigned long RSA_KEY_EXPONENT = 65537; - BN_set_word(exponent, RSA_KEY_EXPONENT); - - // seed the random number generator before we call RSA_generate_key_ex - srand(time(NULL)); - - const int RSA_KEY_BITS = 2048; - - if (!RSA_generate_key_ex(keyPair, RSA_KEY_BITS, exponent, NULL)) { - qCDebug(commerce) << "Error generating 2048-bit RSA Keypair -" << ERR_get_error(); - - // we're going to bust out of here but first we cleanup the BIGNUM - BN_free(exponent); + EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE); + if (!EC_KEY_generate_key(keyPair)) { + qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error(); return retval; } - // we don't need the BIGNUM anymore so clean that up - BN_free(exponent); - // grab the public key and private key from the file unsigned char* publicKeyDER = NULL; - int publicKeyLength = i2d_RSAPublicKey(keyPair, &publicKeyDER); + int publicKeyLength = i2d_EC_PUBKEY(keyPair, &publicKeyDER); unsigned char* privateKeyDER = NULL; - int privateKeyLength = i2d_RSAPrivateKey(keyPair, &privateKeyDER); + int privateKeyLength = i2d_ECPrivateKey(keyPair, &privateKeyDER); if (publicKeyLength <= 0 || privateKeyLength <= 0) { - qCDebug(commerce) << "Error getting DER public or private key from RSA struct -" << ERR_get_error(); + qCDebug(commerce) << "Error getting DER public or private key from EC struct -" << ERR_get_error(); - // cleanup the RSA struct - RSA_free(keyPair); + // cleanup the EC struct + EC_KEY_free(keyPair); // cleanup the public and private key DER data, if required if (publicKeyLength > 0) { @@ -227,13 +207,13 @@ QPair generateRSAKeypair() { return retval; } - RSA_free(keyPair); + EC_KEY_free(keyPair); // prepare the return values. TODO: Fix this - we probably don't really even want the // private key at all (better to read it when we need it?). Or maybe we do, when we have // multiple keys? - retval.first = new QByteArray(reinterpret_cast(publicKeyDER), publicKeyLength ), - retval.second = new QByteArray(reinterpret_cast(privateKeyDER), privateKeyLength ); + retval.first = new QByteArray(reinterpret_cast(publicKeyDER), publicKeyLength); + retval.second = new QByteArray(reinterpret_cast(privateKeyDER), privateKeyLength); // cleanup the publicKeyDER and publicKeyDER data OPENSSL_free(publicKeyDER); @@ -245,18 +225,18 @@ QPair generateRSAKeypair() { // the public key can just go into a byte array QByteArray readPublicKey(const char* filename) { FILE* fp; - RSA* key = NULL; + EC_KEY* key = NULL; if ((fp = fopen(filename, "r"))) { // file opened successfully qCDebug(commerce) << "opened key file" << filename; - if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) { + if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) { // file read successfully unsigned char* publicKeyDER = NULL; - int publicKeyLength = i2d_RSAPublicKey(key, &publicKeyDER); + int publicKeyLength = i2d_EC_PUBKEY(key, &publicKeyDER); // TODO: check for 0 length? // cleanup - RSA_free(key); + EC_KEY_free(key); fclose(fp); qCDebug(commerce) << "parsed public key file successfully"; @@ -274,15 +254,15 @@ QByteArray readPublicKey(const char* filename) { return QByteArray(); } -// the private key should be read/copied into heap memory. For now, we need the RSA struct -// so I'll return that. Note we need to RSA_free(key) later!!! -RSA* readPrivateKey(const char* filename) { +// the private key should be read/copied into heap memory. For now, we need the EC_KEY struct +// so I'll return that. +EC_KEY* readPrivateKey(const char* filename) { FILE* fp; - RSA* key = NULL; + EC_KEY* key = NULL; if ((fp = fopen(filename, "r"))) { // file opened successfully qCDebug(commerce) << "opened key file" << filename; - if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) { + if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) { qCDebug(commerce) << "parsed private key file successfully"; } else { @@ -509,7 +489,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { if (publicKey.size() > 0) { if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) { - RSA_free(key); + EC_KEY_free(key); // be sure to add the public key so we don't do this over and over _publicKeys.push_back(publicKey.toBase64()); @@ -525,7 +505,7 @@ bool Wallet::generateKeyPair() { initialize(); qCInfo(commerce) << "Generating keypair."; - auto keyPair = generateRSAKeypair(); + auto keyPair = generateECKeypair(); writeBackupInstructions(); @@ -557,25 +537,25 @@ QStringList Wallet::listPublicKeys() { // the horror of code pages and so on (changing the bytes) by just returning a base64 // encoded string representing the signature (suitable for http, etc...) QString Wallet::signWithKey(const QByteArray& text, const QString& key) { - qCInfo(commerce) << "Signing text."; - RSA* rsaPrivateKey = NULL; - if ((rsaPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) { - QByteArray signature(RSA_size(rsaPrivateKey), 0); + qCInfo(commerce) << "Signing text" << text << "with key" << key; + EC_KEY* ecPrivateKey = NULL; + if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) { + unsigned char* sig = new unsigned char[ECDSA_size(ecPrivateKey)]; + unsigned int signatureBytes = 0; QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256); - int encryptReturn = RSA_sign(NID_sha256, - reinterpret_cast(hashedPlaintext.constData()), - hashedPlaintext.size(), - reinterpret_cast(signature.data()), - &signatureBytes, - rsaPrivateKey); - // free the private key RSA struct now that we are done with it - RSA_free(rsaPrivateKey); + int retrn = ECDSA_sign(0, + reinterpret_cast(hashedPlaintext.constData()), + hashedPlaintext.size(), + sig, + &signatureBytes, ecPrivateKey); - if (encryptReturn != -1) { + EC_KEY_free(ecPrivateKey); + QByteArray signature(reinterpret_cast(sig), signatureBytes); + if (retrn != -1) { return signature.toBase64(); } } @@ -674,7 +654,7 @@ void Wallet::reset() { keyFile.remove(); } bool Wallet::writeWallet(const QString& newPassphrase) { - RSA* keys = readKeys(keyFilePath().toStdString().c_str()); + EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str()); if (keys) { // we read successfully, so now write to a new temp file QString tempFileName = QString("%1.%2").arg(keyFilePath(), QString("temp")); @@ -720,82 +700,86 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { void Wallet::handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode) { auto nodeList = DependencyManager::get(); + // With EC keys, we receive a nonce from the metaverse server, which is signed + // here with the private key and returned. Verification is done at server. + bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest; - unsigned char decryptedText[64]; + int status; int certIDByteArraySize; - int encryptedTextByteArraySize; + int textByteArraySize; int challengingNodeUUIDByteArraySize; packet->readPrimitive(&certIDByteArraySize); - packet->readPrimitive(&encryptedTextByteArraySize); + packet->readPrimitive(&textByteArraySize); // returns a cast char*, size if (challengeOriginatedFromClient) { packet->readPrimitive(&challengingNodeUUIDByteArraySize); } + // "encryptedText" is now a series of random bytes, a nonce QByteArray certID = packet->read(certIDByteArraySize); - QByteArray encryptedText = packet->read(encryptedTextByteArraySize); + QByteArray text = packet->read(textByteArraySize); QByteArray challengingNodeUUID; if (challengeOriginatedFromClient) { challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize); } - RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); - int decryptionStatus = -1; + EC_KEY* ec = readKeys(keyFilePath().toStdString().c_str()); + QString sig; - if (rsa) { + if (ec) { ERR_clear_error(); - decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, - reinterpret_cast(encryptedText.constData()), - decryptedText, - rsa, - RSA_PKCS1_OAEP_PADDING); - - RSA_free(rsa); + sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with + status = 1; } else { - qCDebug(commerce) << "During entity ownership challenge, creating the RSA object failed."; + qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed."; + status = -1; } - QByteArray decryptedTextByteArray; - if (decryptionStatus > -1) { - decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), decryptionStatus); + EC_KEY_free(ec); + QByteArray ba = sig.toLocal8Bit(); + const char *sigChar = ba.data(); + + QByteArray textByteArray; + if (status > -1) { + textByteArray = QByteArray(sigChar, (int) strlen(sigChar)); } - int decryptedTextByteArraySize = decryptedTextByteArray.size(); + textByteArraySize = textByteArray.size(); int certIDSize = certID.size(); // setup the packet if (challengeOriginatedFromClient) { - auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDSize + decryptedTextByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), + auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, + certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), true); - decryptedTextPacket->writePrimitive(certIDSize); - decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); - decryptedTextPacket->writePrimitive(challengingNodeUUIDByteArraySize); - decryptedTextPacket->write(certID); - decryptedTextPacket->write(decryptedTextByteArray); - decryptedTextPacket->write(challengingNodeUUID); + textPacket->writePrimitive(certIDSize); + textPacket->writePrimitive(textByteArraySize); + textPacket->writePrimitive(challengingNodeUUIDByteArraySize); + textPacket->write(certID); + textPacket->write(textByteArray); + textPacket->write(challengingNodeUUID); - qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID; - nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + nodeList->sendPacket(std::move(textPacket), *sendingNode); } else { - auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2 * sizeof(int), true); + auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + textByteArraySize + 2 * sizeof(int), true); - decryptedTextPacket->writePrimitive(certIDSize); - decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); - decryptedTextPacket->write(certID); - decryptedTextPacket->write(decryptedTextByteArray); + textPacket->writePrimitive(certIDSize); + textPacket->writePrimitive(textByteArraySize); + textPacket->write(certID); + textPacket->write(textByteArray); - qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID; - nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + nodeList->sendPacket(std::move(textPacket), *sendingNode); } - if (decryptionStatus == -1) { - qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed."; + if (status == -1) { + qCDebug(commerce) << "During entity ownership challenge, signing the text failed."; long error = ERR_get_error(); if (error != 0) { const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "RSA error:" << error_str; + qCWarning(entities) << "EC error:" << error_str; } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 1ea0631d1f..3681f42381 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -326,21 +326,21 @@ void ContextOverlayInterface::openInspectionCertificate() { QString ownerKey = jsonObject["transfer_recipient_key"].toString(); QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray encryptedText = DependencyManager::get()->getTree()->computeEncryptedNonce(certID, ownerKey); + QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); int certIDByteArraySize = certID.length(); - int encryptedTextByteArraySize = encryptedText.length(); + int textByteArraySize = text.length(); int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + encryptedTextByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), + certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), true); challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->writePrimitive(textByteArraySize); challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); challengeOwnershipPacket->write(certID); - challengeOwnershipPacket->write(encryptedText); + challengeOwnershipPacket->write(text); challengeOwnershipPacket->write(nodeToChallengeByteArray); nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); @@ -421,16 +421,16 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer _challengeOwnershipTimeoutTimer.stop(); int certIDByteArraySize; - int decryptedTextByteArraySize; + int textByteArraySize; packet->readPrimitive(&certIDByteArraySize); - packet->readPrimitive(&decryptedTextByteArraySize); + packet->readPrimitive(&textByteArraySize); QString certID(packet->read(certIDByteArraySize)); - QString decryptedText(packet->read(decryptedTextByteArraySize)); + QString text(packet->read(textByteArraySize)); EntityItemID id; - bool verificationSuccess = DependencyManager::get()->getTree()->verifyDecryptedNonce(certID, decryptedText, id); + bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, text, id); if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 108fc14e30..5ab4bdee01 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -13,21 +13,18 @@ #include #include #include - #include -#include #include #include +#include #include #include #include #include - #include #include #include #include - #include "EntitiesLogging.h" #include "EntityItem.h" #include "EntityItemProperties.h" @@ -2508,48 +2505,47 @@ QByteArray EntityItemProperties::getStaticCertificateHash() const { return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256); } -bool EntityItemProperties::verifyStaticCertificateProperties() { - // True IIF a non-empty certificateID matches the static certificate json. - // I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash. +// FIXME: This is largely copied from EntityItemProperties::verifyStaticCertificateProperties, which should be refactored to use this. +// I also don't like the nested-if style, but for this step I'm deliberately preserving the similarity. +bool EntityItemProperties::verifySignature(const QString& publicKey, const QByteArray& digestByteArray, const QByteArray& signatureByteArray) { - if (getCertificateID().isEmpty()) { + if (digestByteArray.isEmpty()) { return false; } - const QByteArray marketplacePublicKeyByteArray = EntityItem::_marketplacePublicKey.toUtf8(); - const unsigned char* marketplacePublicKey = reinterpret_cast(marketplacePublicKeyByteArray.constData()); - int marketplacePublicKeyLength = marketplacePublicKeyByteArray.length(); + const unsigned char* key = reinterpret_cast(publicKey.toUtf8().constData()); + int keyLength = publicKey.length(); - BIO *bio = BIO_new_mem_buf((void*)marketplacePublicKey, marketplacePublicKeyLength); + BIO *bio = BIO_new_mem_buf((void*)key, keyLength); EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); if (evp_key) { - RSA* rsa = EVP_PKEY_get1_RSA(evp_key); - if (rsa) { - const QByteArray digestByteArray = getStaticCertificateHash(); + EC_KEY* ec = EVP_PKEY_get1_EC_KEY(evp_key); + if (ec) { const unsigned char* digest = reinterpret_cast(digestByteArray.constData()); int digestLength = digestByteArray.length(); - const QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8()); const unsigned char* signature = reinterpret_cast(signatureByteArray.constData()); int signatureLength = signatureByteArray.length(); ERR_clear_error(); - bool answer = RSA_verify(NID_sha256, + // ECSDA verification prototype: note that type is currently ignored + // int ECDSA_verify(int type, const unsigned char *dgst, int dgstlen, + // const unsigned char *sig, int siglen, EC_KEY *eckey); + bool answer = ECDSA_verify(0, digest, digestLength, signature, signatureLength, - rsa); + ec); long error = ERR_get_error(); if (error != 0) { const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "ERROR while verifying static certificate properties! RSA error:" << error_str - << "\nStatic Cert JSON:" << getStaticCertificateJSON() - << "\nKey:" << EntityItem::_marketplacePublicKey << "\nKey Length:" << marketplacePublicKeyLength + qCWarning(entities) << "ERROR while verifying signature! EC error:" << error_str + << "\nKey:" << publicKey << "\nutf8 Key Length:" << keyLength << "\nDigest:" << digest << "\nDigest Length:" << digestLength << "\nSignature:" << signature << "\nSignature Length:" << signatureLength; } - RSA_free(rsa); + EC_KEY_free(ec); if (bio) { BIO_free(bio); } @@ -2566,7 +2562,7 @@ bool EntityItemProperties::verifyStaticCertificateProperties() { } long error = ERR_get_error(); const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str; + qCWarning(entities) << "Failed to verify signature! key" << publicKey << " EC key error:" << error_str; return false; } } else { @@ -2575,7 +2571,13 @@ bool EntityItemProperties::verifyStaticCertificateProperties() { } long error = ERR_get_error(); const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str; + qCWarning(entities) << "Failed to verify signature! key" << publicKey << " EC PEM error:" << error_str; return false; } } + +bool EntityItemProperties::verifyStaticCertificateProperties() { + // True IFF a non-empty certificateID matches the static certificate json. + // I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash. + return verifySignature(EntityItem::_marketplacePublicKey, getStaticCertificateHash(), QByteArray::fromBase64(getCertificateID().toUtf8())); +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 732dbdf69f..ec192d7c9f 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -339,6 +339,7 @@ public: QByteArray getStaticCertificateJSON() const; QByteArray getStaticCertificateHash() const; bool verifyStaticCertificateProperties(); + static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature); protected: QString getCollisionMaskAsString() const; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 22a4d3a2dc..e62399ce95 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -12,9 +12,7 @@ #include "EntityTree.h" #include #include - #include -#include #include #include #include @@ -1167,63 +1165,37 @@ void EntityTree::startPendingTransferStatusTimer(const QString& certID, const En transferStatusRetryTimer->start(90000); } -QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QString ownerKey) { - QString ownerKeyWithHeaders = ("-----BEGIN RSA PUBLIC KEY-----\n" + ownerKey + "\n-----END RSA PUBLIC KEY-----"); - BIO* bio = BIO_new_mem_buf((void*)ownerKeyWithHeaders.toUtf8().constData(), -1); - BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // NO NEWLINE - RSA* rsa = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL); +QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKey) { + QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-" + QByteArray nonceBytes = nonce.toByteArray(); - if (rsa) { - QUuid nonce = QUuid::createUuid(); - const unsigned int textLength = nonce.toString().length(); - QByteArray encryptedText(RSA_size(rsa), 0); - const int encryptStatus = RSA_public_encrypt(textLength, - reinterpret_cast(qPrintable(nonce.toString())), - reinterpret_cast(encryptedText.data()), - rsa, - RSA_PKCS1_OAEP_PADDING); - if (bio) { - BIO_free(bio); - } - RSA_free(rsa); - if (encryptStatus == -1) { - long error = ERR_get_error(); - const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "Unable to compute encrypted nonce for" << certID << "\nRSA error:" << error_str; - return ""; - } + QWriteLocker locker(&_certNonceMapLock); + _certNonceMap.insert(certID, QPair(nonce, ownerKey)); - QWriteLocker locker(&_certNonceMapLock); - _certNonceMap.insert(certID, nonce); - - return encryptedText; - } else { - if (bio) { - BIO_free(bio); - } - return ""; - } + return nonceBytes; } -bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce, EntityItemID& id) { +bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id) { { QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); id = _entityCertificateIDMap.value(certID); } - QString actualNonce; + QString actualNonce, key; { QWriteLocker locker(&_certNonceMapLock); - actualNonce = _certNonceMap.take(certID).toString(); + QPair sent = _certNonceMap.take(certID); + actualNonce = sent.first.toString(); + key = sent.second; } - bool verificationSuccess = (actualNonce == decryptedNonce); + QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key.insert(64, "\n") + "\n-----END PUBLIC KEY-----"; + bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), actualNonce.toUtf8(), nonce.toUtf8()); if (verificationSuccess) { qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded."; } else { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed." - << "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce; + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed for nonce" << actualNonce << "key" << key << "signature" << nonce; } return verificationSuccess; @@ -1231,67 +1203,67 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; - int encryptedTextByteArraySize; + int textByteArraySize; int nodeToChallengeByteArraySize; message.readPrimitive(&certIDByteArraySize); - message.readPrimitive(&encryptedTextByteArraySize); + message.readPrimitive(&textByteArraySize); message.readPrimitive(&nodeToChallengeByteArraySize); QByteArray certID(message.read(certIDByteArraySize)); - QByteArray encryptedText(message.read(encryptedTextByteArraySize)); + QByteArray text(message.read(textByteArraySize)); QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize)); - sendChallengeOwnershipRequestPacket(certID, encryptedText, nodeToChallenge, sourceNode); + sendChallengeOwnershipRequestPacket(certID, text, nodeToChallenge, sourceNode); } void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { auto nodeList = DependencyManager::get(); int certIDByteArraySize; - int decryptedTextByteArraySize; + int textByteArraySize; int challengingNodeUUIDByteArraySize; message.readPrimitive(&certIDByteArraySize); - message.readPrimitive(&decryptedTextByteArraySize); + message.readPrimitive(&textByteArraySize); message.readPrimitive(&challengingNodeUUIDByteArraySize); QByteArray certID(message.read(certIDByteArraySize)); - QByteArray decryptedText(message.read(decryptedTextByteArraySize)); + QByteArray text(message.read(textByteArraySize)); QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize)); auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDByteArraySize + decryptedText.length() + 2 * sizeof(int), + certIDByteArraySize + text.length() + 2 * sizeof(int), true); challengeOwnershipReplyPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipReplyPacket->writePrimitive(decryptedText.length()); + challengeOwnershipReplyPacket->writePrimitive(text.length()); challengeOwnershipReplyPacket->write(certID); - challengeOwnershipReplyPacket->write(decryptedText); + challengeOwnershipReplyPacket->write(text); nodeList->sendPacket(std::move(challengeOwnershipReplyPacket), *(nodeList->nodeWithUUID(challengingNode))); } void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode) { - // 1. Encrypt a nonce with the owner's public key + // 1. Obtain a nonce auto nodeList = DependencyManager::get(); - QByteArray encryptedText = computeEncryptedNonce(certID, ownerKey); + QByteArray text = computeNonce(certID, ownerKey); - if (encryptedText == "") { - qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity..."; + if (text == "") { + qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity..."; deleteEntity(entityItemID, true); } else { qCDebug(entities) << "Challenging ownership of Cert ID" << certID; - // 2. Send the encrypted text to the rezzing avatar's node + // 2. Send the nonce to the rezzing avatar's node QByteArray certIDByteArray = certID.toUtf8(); int certIDByteArraySize = certIDByteArray.size(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, - certIDByteArraySize + encryptedText.length() + 2 * sizeof(int), + certIDByteArraySize + text.length() + 2 * sizeof(int), true); challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedText.length()); + challengeOwnershipPacket->writePrimitive(text.length()); challengeOwnershipPacket->write(certIDByteArray); - challengeOwnershipPacket->write(encryptedText); + challengeOwnershipPacket->write(text); nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time @@ -1304,7 +1276,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri } } -void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& encryptedText, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) { +void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) { auto nodeList = DependencyManager::get(); // In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants @@ -1312,17 +1284,17 @@ void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, c QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122(); int certIDByteArraySize = certID.length(); - int encryptedTextByteArraySize = encryptedText.length(); + int TextByteArraySize = text.length(); int senderNodeUUIDSize = senderNodeUUID.length(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + encryptedTextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int), + certIDByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int), true); challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->writePrimitive(TextByteArraySize); challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize); challengeOwnershipPacket->write(certID); - challengeOwnershipPacket->write(encryptedText); + challengeOwnershipPacket->write(text); challengeOwnershipPacket->write(senderNodeUUID); nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(QUuid::fromRfc4122(nodeToChallenge)))); @@ -1391,18 +1363,18 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; - int decryptedTextByteArraySize; + int textByteArraySize; message.readPrimitive(&certIDByteArraySize); - message.readPrimitive(&decryptedTextByteArraySize); + message.readPrimitive(&textByteArraySize); QString certID(message.read(certIDByteArraySize)); - QString decryptedText(message.read(decryptedTextByteArraySize)); + QString text(message.read(textByteArraySize)); emit killChallengeOwnershipTimeoutTimer(certID); EntityItemID id; - if (!verifyDecryptedNonce(certID, decryptedText, id)) { + if (!verifyNonce(certID, text, id)) { if (!id.isNull()) { deleteEntity(id, true); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d8981903d0..11a747d624 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -275,8 +275,8 @@ public: static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME; - QByteArray computeEncryptedNonce(const QString& certID, const QString ownerKey); - bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce, EntityItemID& id); + QByteArray computeNonce(const QString& certID, const QString ownerKey); + bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id); void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } @@ -334,7 +334,7 @@ protected: QHash _entityCertificateIDMap; mutable QReadWriteLock _certNonceMapLock; - QHash _certNonceMap; + QHash> _certNonceMap; EntitySimulationPointer _simulation; @@ -383,7 +383,7 @@ protected: private: void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); - void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& encryptedText, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); + void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); std::shared_ptr _myAvatar{ nullptr }; From b817143e2ecb3b4c8c345a90e137bf7ebfe03c67 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 29 Nov 2017 18:16:21 -0700 Subject: [PATCH 12/70] Fixes on web and QML audio sync --- .../resources/html/createGlobalEventBridge.js | 17 +++++++++++------ libraries/ui/src/ui/OffscreenQmlSurface.cpp | 7 +++---- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/interface/resources/html/createGlobalEventBridge.js b/interface/resources/html/createGlobalEventBridge.js index bccbdfaf7c..3355ca87c8 100644 --- a/interface/resources/html/createGlobalEventBridge.js +++ b/interface/resources/html/createGlobalEventBridge.js @@ -65,21 +65,26 @@ var EventBridge; // we need to listen to events that might precede the addition of this elements. // A more robust hack will be to add a setInterval that look for DOM changes every 100-300 ms (low performance?) - window.onload = function(){ + window.addEventListener("load",function(event) { setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); + console.log(":: Window Loaded"); }, 1200); - }; - document.onclick = function(){ + }, false); + + document.addEventListener("click",function(){ setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); + console.log(":: Window Clicked"); }, 1200); - }; - document.onchange = function(){ + }, false); + + document.addEventListener("change",function(){ setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); + console.log(":: Window Changes"); }, 1200); - }; + }, false); tempEventBridge._callbacks.forEach(function (callback) { EventBridge.scriptEventReceived.connect(callback); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 7bc88d73f6..a585b80090 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -139,7 +139,6 @@ public: QThread::msleep(_runDelayMs); } auto audioIO = DependencyManager::get(); - QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); for (auto player : _container->findChildren()) { auto mediaState = player->state(); QMediaService *svc = player->service(); @@ -156,7 +155,7 @@ public: for (int i = 0; i < outputs.size(); i++) { QString output = outputs[i]; QString description = out->outputDescription(output); - if (description == deviceName) { + if (description == _newTargetDevice) { deviceOuput = output; break; } @@ -171,7 +170,7 @@ public: player->stop(); } } - qDebug() << "QML Audio changed to " << deviceName; + qDebug() << "QML Audio changed to " << _newTargetDevice; } private: @@ -700,7 +699,7 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { } else { auto audioIO = DependencyManager::get(); QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); - int waitForAudioQmlMs = 500; + int waitForAudioQmlMs = 200; // The audio device need to be change using oth new AudioHandler(_rootItem, deviceName, waitForAudioQmlMs); } From b890a29b3a8cf87f8f67b2d1b057be59d12be10c Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 29 Nov 2017 19:05:01 -0700 Subject: [PATCH 13/70] Clean console log --- interface/resources/html/createGlobalEventBridge.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/resources/html/createGlobalEventBridge.js b/interface/resources/html/createGlobalEventBridge.js index 3355ca87c8..b85aa33e33 100644 --- a/interface/resources/html/createGlobalEventBridge.js +++ b/interface/resources/html/createGlobalEventBridge.js @@ -68,21 +68,18 @@ var EventBridge; window.addEventListener("load",function(event) { setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); - console.log(":: Window Loaded"); }, 1200); }, false); document.addEventListener("click",function(){ setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); - console.log(":: Window Clicked"); }, 1200); }, false); document.addEventListener("change",function(){ setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); - console.log(":: Window Changes"); }, 1200); }, false); From eb120b1bc10a995069af15c7188028b240f62d65 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 30 Nov 2017 11:03:27 -0800 Subject: [PATCH 14/70] use correct timestamp of avatar's outgoin data --- .../src/avatars/AvatarMixerClientData.cpp | 17 +++++++++++++++++ .../src/avatars/AvatarMixerClientData.h | 12 +++--------- .../src/avatars/AvatarMixerSlave.cpp | 6 +++++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index a4bf8fa253..288652715a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -25,6 +25,23 @@ AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) : _avatar->setID(nodeID); } +uint64_t AvatarMixerClientData::getLastOtherAvatarEncodeTime(QUuid otherAvatar) const { + std::unordered_map::const_iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar); + if (itr != _lastOtherAvatarEncodeTime.end()) { + return itr->second; + } + return 0; +} + +void AvatarMixerClientData::setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time) { + std::unordered_map::iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar); + if (itr != _lastOtherAvatarEncodeTime.end()) { + itr->second = time; + } else { + _lastOtherAvatarEncodeTime.emplace(std::pair(otherAvatar, time)); + } +} + void AvatarMixerClientData::queuePacket(QSharedPointer message, SharedNodePointer node) { if (!_packetQueue.node) { _packetQueue.node = node; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 1a9da292ac..acd9be0702 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -112,14 +112,8 @@ public: ViewFrustum getViewFrustum() const { return _currentViewFrustum; } - quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { - quint64 result = 0; - if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) { - result = _lastOtherAvatarEncodeTime[otherAvatar]; - } - _lastOtherAvatarEncodeTime[otherAvatar] = usecTimestampNow(); - return result; - } + uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const; + void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time); QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { _lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount()); @@ -143,7 +137,7 @@ private: // this is a map of the last time we encoded an "other" avatar for // sending to "this" node - std::unordered_map _lastOtherAvatarEncodeTime; + std::unordered_map _lastOtherAvatarEncodeTime; std::unordered_map> _lastOtherAvatarSentJoints; uint64_t _identityChangeTimestamp; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 769789ef6f..9ea1ed3637 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -208,7 +208,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) PrioritySortUtil::getAvatarAgeCallback = [&] (const AvatarSharedPointer& avatar) { auto avatarNode = avatarDataToNodes[avatar]; assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map - return nodeData->getLastBroadcastTime(avatarNode->getUUID()); + return nodeData->getLastOtherAvatarEncodeTime(avatarNode->getUUID()); }; class SortableAvatar: public PrioritySortUtil::Sortable { @@ -429,7 +429,11 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // set the last sent sequence number for this sender on the receiver nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), otherNodeData->getLastReceivedSequenceNumber()); + nodeData->setLastOtherAvatarEncodeTime(otherNode->getUUID(), usecTimestampNow()); } + } else { + // TODO? this avatar is not included now, and will probably not be included next frame. + // It would be nice if we could tweak its future sort priority to put it at the back of the list. } avatarPacketList->endSegment(); From 05fbf8e511e2f842d6f78958840625e79f8379e5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 30 Nov 2017 11:04:51 -0800 Subject: [PATCH 15/70] workaround for zero-size objects in priority formula --- libraries/shared/src/PrioritySortUtil.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 409450ac08..dc6a877bb9 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -112,11 +112,19 @@ namespace PrioritySortUtil { glm::vec3 position = thing.getPosition(); glm::vec3 offset = position - _view.getPosition(); float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero - float radius = thing.getRadius(); + const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance) + float radius = glm::min(thing.getRadius(), MIN_RADIUS); + float cosineAngle = (glm::dot(offset, _view.getDirection()) / distance); + float age = (float)(usecTimestampNow() - thing.getTimestamp()); - float priority = _angularWeight * (radius / distance) - + _centerWeight * (glm::dot(offset, _view.getDirection()) / distance) - + _ageWeight * (float)(usecTimestampNow() - thing.getTimestamp()); + // we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward + // at a reduced rate but we don't want the "age" term to go zero or negative so we clamp it + const float MIN_COSINE_ANGLE_FACTOR = 0.1f; + float cosineAngleFactor = glm::max(cosineAngle, MIN_COSINE_ANGLE_FACTOR); + + float priority = _angularWeight * glm::max(radius, MIN_RADIUS) / distance + + _centerWeight * cosineAngle + + _ageWeight * cosineAngleFactor * age; // decrement priority of things outside keyhole if (distance - radius > _view.getCenterRadius()) { From 532fc2ca5b9e77c42e69642ad381bcf25271493f Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Mon, 6 Nov 2017 18:04:59 -0500 Subject: [PATCH 16/70] [Case 7049] Remove duplicate color fields for shapes (details below). There are 2 types or styles of color picker controls. * color-control1: Composed of only a color picker * color-control2: Composed of a color picker and rgb fields. The color can be manipulated by either. Previously for shapes, the Properties tab utilized both types of color controls. Color-control1 was displayed within the quick properties section; whereas, color-control2 was displayed farther down within the more detailed sections of Properties tab. This commit removes the duplication of the fields, opting to utilize color-control2 within the quick properties portion of the Properties tab. Changes Committed: modified: scripts/system/html/entityProperties.html modified: scripts/system/html/js/entityProperties.js --- scripts/system/html/entityProperties.html | 20 ++++++----------- scripts/system/html/js/entityProperties.js | 26 ++-------------------- 2 files changed, 9 insertions(+), 37 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 8b2a088d83..e95646e3fa 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -66,9 +66,14 @@ -
-
+
+
+
+
+
+
+
@@ -216,17 +221,6 @@
-
-
-
- -
-
-
-
-
-
-
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index f54394a353..3286b06998 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -592,8 +592,7 @@ function loaded() { var elJSONEditor = document.getElementById("userdata-editor"); var elNewJSONEditor = document.getElementById('userdata-new-editor'); var elColorSections = document.querySelectorAll(".color-section"); - var elColorControl1 = document.getElementById("property-color-control1"); - var elColorControl2 = document.getElementById("property-color-control2"); + var elColorControlVariant2 = document.getElementById("property-color-control2"); var elColorRed = document.getElementById("property-color-red"); var elColorGreen = document.getElementById("property-color-green"); var elColorBlue = document.getElementById("property-color-blue"); @@ -977,7 +976,7 @@ function loaded() { elColorRed.value = properties.color.red; elColorGreen.value = properties.color.green; elColorBlue.value = properties.color.blue; - elColorControl1.style.backgroundColor = elColorControl2.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; + elColorControlVariant2.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; } if (properties.type == "Model") { @@ -1303,24 +1302,6 @@ function loaded() { elColorRed.addEventListener('change', colorChangeFunction); elColorGreen.addEventListener('change', colorChangeFunction); elColorBlue.addEventListener('change', colorChangeFunction); - colorPickers.push($('#property-color-control1').colpick({ - colorScheme: 'dark', - layout: 'hex', - color: '000000', - onShow: function(colpick) { - $('#property-color-control1').attr('active', 'true'); - }, - onHide: function(colpick) { - $('#property-color-control1').attr('active', 'false'); - }, - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); - emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); - // Keep the companion control in sync - elColorControl2.style.backgroundColor = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")"; - } - })); colorPickers.push($('#property-color-control2').colpick({ colorScheme: 'dark', layout: 'hex', @@ -1335,9 +1316,6 @@ function loaded() { $(el).css('background-color', '#' + hex); $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); - // Keep the companion control in sync - elColorControl1.style.backgroundColor = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")"; - } })); From b7a959255d51b57d4d0e65878b8163c15bc2a9e9 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Mon, 6 Nov 2017 19:57:41 -0500 Subject: [PATCH 17/70] [Case 7049] Remove basic color section from non-shape Properties tab. Changes Committed: modified: scripts/system/html/css/edit-style.css --- scripts/system/html/css/edit-style.css | 20 ++++++++++---------- scripts/system/html/entityProperties.html | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 6aacaa5333..aad42ca85c 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1500,8 +1500,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.LightMenu .shape-group.shape-section.property.dropdown, -#properties-list.LightMenu color-section.color-control1 { +#properties-list.LightMenu #shape-list, +#properties-list.LightMenu #base-color-section { display: none } @@ -1536,8 +1536,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.ModelMenu .shape-group.shape-section.property.dropdown, -#properties-list.ModelMenu .color-section.color-control1 { +#properties-list.ModelMenu #shape-list, +#properties-list.ModelMenu #base-color-section { display: none } @@ -1572,8 +1572,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.ZoneMenu .shape-group.shape-section.property.dropdown, -#properties-list.ZoneMenu .color-section.color-control1 { +#properties-list.ZoneMenu #shape-list, +#properties-list.ZoneMenu #base-color-section { display: none } @@ -1608,8 +1608,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.WebMenu .shape-group.shape-section.property.dropdown, -#properties-list.WebMenu .color-section.color-control1 { +#properties-list.WebMenu #shape-list, +#properties-list.WebMenu #base-color-section { display: none; } @@ -1645,8 +1645,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.TextMenu .shape-group.shape-section.property.dropdown, -#properties-list.TextMenu .color-section.color-control1 { +#properties-list.TextMenu #shape-list, +#properties-list.TextMenu #base-color-section { display: none } diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index e95646e3fa..18c3e8960e 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -44,7 +44,7 @@
-
-
+ +
+ CollisionM
From d271f619870163045927ea4d7b8298a9fddd96ed Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 1 Dec 2017 14:37:59 -0700 Subject: [PATCH 40/70] Proper fix --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 40 ++++++++++++++------- libraries/ui/src/ui/OffscreenQmlSurface.h | 5 +++ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 5bcf06ce82..b3ebf30a94 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -121,23 +121,18 @@ uint64_t uvec2ToUint64(const uvec2& v) { class AudioHandler : public QObject, QRunnable { Q_OBJECT public: - AudioHandler(QSharedPointer surface, const QString& deviceName, int runDelayMs = 0, QObject* parent = nullptr) : QObject(parent) { + AudioHandler(QSharedPointer surface, const QString& deviceName, QObject* parent = nullptr) : QObject(parent) { _newTargetDevice = deviceName; - _runDelayMs = runDelayMs; _surface = surface; setAutoDelete(true); - QThreadPool::globalInstance()->start(this); + if (deviceName.size() > 0) { + QThreadPool::globalInstance()->start(this); + } } virtual ~AudioHandler() { qDebug() << "Audio Handler Destroyed"; } void run() override { - if (_newTargetDevice.isEmpty()) { - return; - } - if (_runDelayMs > 0) { - QThread::msleep(_runDelayMs); - } if (!_surface.isNull() && _surface->getRootItem() && !_surface->getCleaned()) { for (auto player : _surface->getRootItem()->findChildren()) { auto mediaState = player->state(); @@ -602,6 +597,7 @@ OffscreenQmlSurface::OffscreenQmlSurface() { OffscreenQmlSurface::~OffscreenQmlSurface() { QObject::disconnect(&_updateTimer); + disconnectAudioOutputTimer(); QObject::disconnect(qApp); cleanup(); @@ -615,6 +611,15 @@ OffscreenQmlSurface::~OffscreenQmlSurface() { void OffscreenQmlSurface::onAboutToQuit() { _paused = true; QObject::disconnect(&_updateTimer); + disconnectAudioOutputTimer(); + +} + +void OffscreenQmlSurface::disconnectAudioOutputTimer() { + if (_audioOutputUpdateTimer.isActive()) { + _audioOutputUpdateTimer.stop(); + } + QObject::disconnect(&_audioOutputUpdateTimer); } void OffscreenQmlSurface::create() { @@ -673,6 +678,14 @@ 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); + }); + int waitForAudioQmlMs = 200; + _audioOutputUpdateTimer.setInterval(waitForAudioQmlMs); + _audioOutputUpdateTimer.setSingleShot(true); + // When Quick says there is a need to render, we will not render immediately. Instead, // a timer with a small interval is used to get better performance. QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); @@ -701,10 +714,11 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); } else { auto audioIO = DependencyManager::get(); - QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); - int waitForAudioQmlMs = 200; - // The audio device need to be change using oth - new AudioHandler(sharedFromThis(), deviceName, waitForAudioQmlMs); + _currentAudioOutputDevice = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); + if (_audioOutputUpdateTimer.isActive()) { + _audioOutputUpdateTimer.stop(); + } + _audioOutputUpdateTimer.start(); } } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 34da11c66c..4c23c62c12 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -117,6 +117,7 @@ public slots: void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false); void forceHtmlAudioOutputDeviceUpdate(); void forceQmlAudioOutputDeviceUpdate(); + signals: void audioOutputDeviceChanged(const QString& deviceName); @@ -148,6 +149,7 @@ private: void render(); void cleanup(); QJsonObject getGLContextData(); + void disconnectAudioOutputTimer(); private slots: void updateQuick(); @@ -171,6 +173,9 @@ private: uint64_t _lastRenderTime { 0 }; uvec2 _size; + QTimer _audioOutputUpdateTimer; + QString _currentAudioOutputDevice; + // Texture management TextureAndFence _latestTextureAndFence { 0, 0 }; From fefedc11c80fde2fd181088a61bd41c0976029c6 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 1 Dec 2017 15:17:23 -0700 Subject: [PATCH 41/70] mac fix --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index b3ebf30a94..9a591018f5 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -173,7 +173,6 @@ public: private: QString _newTargetDevice; QSharedPointer _surface; - int _runDelayMs; }; class OffscreenTextures { From a1d90b5dd92d5b114ebad6301090f6b03ed05229 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 1 Dec 2017 15:41:13 -0800 Subject: [PATCH 42/70] added extra bit --- libraries/entities/src/EntityItem.cpp | 81 ++++++++++++++--------- libraries/entities/src/EntityItem.h | 1 + libraries/entities/src/SimulationFlags.h | 1 + libraries/physics/src/ObjectMotionState.h | 2 +- 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4f2b290635..330e1fa854 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1832,39 +1832,8 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } } - if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { - bool iAmHoldingThis = false; - // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the - // "bootstrapping" problem where you can shoot yourself across the room by grabbing something - // and holding it against your own avatar. - if (isChildOfMyAvatar()) { - iAmHoldingThis = true; - } - // also, don't bootstrap our own avatar with a hold action - QList holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD); - QList::const_iterator i = holdActions.begin(); - while (i != holdActions.end()) { - EntityDynamicPointer action = *i; - if (action->isMine()) { - iAmHoldingThis = true; - break; - } - i++; - } - QList farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB); - i = farGrabActions.begin(); - while (i != farGrabActions.end()) { - EntityDynamicPointer action = *i; - if (action->isMine()) { - iAmHoldingThis = true; - break; - } - i++; - } - - if (iAmHoldingThis) { - userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; - } + if (_dirtyFlags & Simulation::DIRTY_IGNORE_MY_AVATAR) { + userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); } @@ -1960,6 +1929,17 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn _allActionsDataCache = newDataCache; _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + + auto actionType = action->getType(); + if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) { + _dirtyFlags |= Simulation::DIRTY_IGNORE_MY_AVATAR; + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::DIRTY_IGNORE_MY_AVATAR); + } + }); + } } else { qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed"; } @@ -2000,6 +1980,32 @@ bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& a return success; } +bool EntityItem::stillHasGrabActions() { + bool stillHasGrabAction = false; + QList holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD); + QList::const_iterator i = holdActions.begin(); + while (i != holdActions.end()) { + EntityDynamicPointer action = *i; + if (action->isMine()) { + stillHasGrabAction = true; + break; + } + i++; + } + QList farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB); + i = farGrabActions.begin(); + while (i != farGrabActions.end()) { + EntityDynamicPointer action = *i; + if (action->isMine()) { + stillHasGrabAction = true; + break; + } + i++; + } + + return stillHasGrabAction; +} + bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation) { _previouslyDeletedActions.insert(actionID, usecTimestampNow()); if (_objectActions.contains(actionID)) { @@ -2023,6 +2029,15 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + if (stillHasGrabActions()) { + _dirtyFlags |= Simulation::DIRTY_IGNORE_MY_AVATAR; + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::DIRTY_IGNORE_MY_AVATAR); + } + }); + } setDynamicDataNeedsTransmit(true); return success; } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 882b8e6812..ab6df1ab33 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -437,6 +437,7 @@ public: // if this entity is client-only, which avatar is it associated with? QUuid getOwningAvatarID() const { return _owningAvatarID; } void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } + bool stillHasGrabActions(); virtual bool wantsHandControllerPointerEvents() const { return false; } virtual bool wantsKeyboardFocus() const { return false; } diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/entities/src/SimulationFlags.h index e2b2224b4a..aeee1ae01b 100644 --- a/libraries/entities/src/SimulationFlags.h +++ b/libraries/entities/src/SimulationFlags.h @@ -27,6 +27,7 @@ namespace Simulation { const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed + const uint32_t DIRTY_IGNORE_MY_AVATAR = 0x4000; const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 0b91ede574..ae5496b076 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -47,7 +47,7 @@ enum MotionStateType { // The update flags trigger two varieties of updates: "hard" which require the body to be pulled // and re-added to the physics engine and "easy" which just updates the body properties. const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE | - Simulation::DIRTY_COLLISION_GROUP); + Simulation::DIRTY_COLLISION_GROUP | Simulation::DIRTY_IGNORE_MY_AVATAR); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES | Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY | From 1e6b5c0c75d269f37e10eada11c663210acdf72c Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 1 Dec 2017 17:09:00 -0800 Subject: [PATCH 43/70] make requested changes --- libraries/entities/src/EntityItem.cpp | 32 ++++++++++++--------- libraries/entities/src/EntityItem.h | 2 +- libraries/entities/src/SimulationFlags.h | 2 +- libraries/physics/src/EntityMotionState.cpp | 2 +- libraries/physics/src/ObjectMotionState.h | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 330e1fa854..e764cfff90 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1832,7 +1832,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } } - if (_dirtyFlags & Simulation::DIRTY_IGNORE_MY_AVATAR) { + if (_dirtyFlags & Simulation::NO_BOOTSTRAPPING) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); @@ -1932,11 +1932,11 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn auto actionType = action->getType(); if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) { - _dirtyFlags |= Simulation::DIRTY_IGNORE_MY_AVATAR; + _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::DIRTY_IGNORE_MY_AVATAR); + entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); } }); } @@ -1980,15 +1980,13 @@ bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& a return success; } -bool EntityItem::stillHasGrabActions() { - bool stillHasGrabAction = false; +bool EntityItem::stillHasGrabActions() const { QList holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD); QList::const_iterator i = holdActions.begin(); while (i != holdActions.end()) { EntityDynamicPointer action = *i; if (action->isMine()) { - stillHasGrabAction = true; - break; + return true; } i++; } @@ -1997,13 +1995,12 @@ bool EntityItem::stillHasGrabActions() { while (i != farGrabActions.end()) { EntityDynamicPointer action = *i; if (action->isMine()) { - stillHasGrabAction = true; - break; + return true; } i++; } - return stillHasGrabAction; + return false; } bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation) { @@ -2029,12 +2026,21 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar - if (stillHasGrabActions()) { - _dirtyFlags |= Simulation::DIRTY_IGNORE_MY_AVATAR; + if (stillHasGrabActions() && !(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { + _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::DIRTY_IGNORE_MY_AVATAR); + entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); + } + }); + } else if (_dirtyFlags & Simulation::NO_BOOTSTRAPPING) { + _dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING; + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); } }); } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ab6df1ab33..4c7f37bd6a 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -437,7 +437,6 @@ public: // if this entity is client-only, which avatar is it associated with? QUuid getOwningAvatarID() const { return _owningAvatarID; } void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } - bool stillHasGrabActions(); virtual bool wantsHandControllerPointerEvents() const { return false; } virtual bool wantsKeyboardFocus() const { return false; } @@ -471,6 +470,7 @@ protected: void setSimulated(bool simulated) { _simulated = simulated; } const QByteArray getDynamicDataInternal() const; + bool stillHasGrabActions() const; void setDynamicDataInternal(QByteArray dynamicData); virtual void dimensionsChanged() override; diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/entities/src/SimulationFlags.h index aeee1ae01b..aaa92000e7 100644 --- a/libraries/entities/src/SimulationFlags.h +++ b/libraries/entities/src/SimulationFlags.h @@ -27,7 +27,7 @@ namespace Simulation { const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed - const uint32_t DIRTY_IGNORE_MY_AVATAR = 0x4000; + const uint32_t NO_BOOTSTRAPPING = 0x4000; const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8ebce9f811..7e8b431ceb 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -700,7 +700,7 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() { void EntityMotionState::clearIncomingDirtyFlags() { assert(entityTreeIsLocked()); if (_body && _entity) { - _entity->clearDirtyFlags(); + _entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS); } } diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index ae5496b076..0b91ede574 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -47,7 +47,7 @@ enum MotionStateType { // The update flags trigger two varieties of updates: "hard" which require the body to be pulled // and re-added to the physics engine and "easy" which just updates the body properties. const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE | - Simulation::DIRTY_COLLISION_GROUP | Simulation::DIRTY_IGNORE_MY_AVATAR); + Simulation::DIRTY_COLLISION_GROUP); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES | Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY | From a1bf54ff005df24d296da280e4d2970e44c6cb5a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 4 Dec 2017 11:16:41 -0800 Subject: [PATCH 44/70] fix issue of no_bootstrapping not being set correctly --- libraries/entities/src/EntityItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 6ab4b3ccbe..29b104b421 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1834,7 +1834,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } } - if (_dirtyFlags & Simulation::NO_BOOTSTRAPPING) { + if ((bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); @@ -2028,7 +2028,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar - if (stillHasGrabActions() && !(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { + if (stillHasGrabActions()) { _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { @@ -2036,7 +2036,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); } }); - } else if (_dirtyFlags & Simulation::NO_BOOTSTRAPPING) { + } else if ((bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { _dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING; forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { From c67ff3b5d8450436dad40b8aefb0995ececee335 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Tue, 5 Dec 2017 00:03:48 +0300 Subject: [PATCH 45/70] 10024 Wallet: TextField Focus Problems note: removed excessive MouseArea-s --- .../hifi/commerce/wallet/PassphraseModal.qml | 10 ------- .../commerce/wallet/PassphraseSelection.qml | 27 ------------------- 2 files changed, 37 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index d967a36b68..249645e76f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -206,16 +206,6 @@ Item { root.isPasswordField = (focus && passphraseField.echoMode === TextInput.Password); } - MouseArea { - anchors.fill: parent; - - onClicked: { - root.keyboardRaised = true; - root.isPasswordField = (passphraseField.echoMode === TextInput.Password); - mouse.accepted = false; - } - } - onAccepted: { submitPassphraseInputButton.enabled = false; commerce.setPassphrase(passphraseField.text); diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index ffeedde8f0..166388afee 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -87,15 +87,6 @@ Item { } } - MouseArea { - anchors.fill: parent; - onPressed: { - var hidePassword = (currentPassphraseField.echoMode === TextInput.Password); - sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - mouse.accepted = false; - } - } - onAccepted: { passphraseField.focus = true; } @@ -115,15 +106,6 @@ Item { activeFocusOnPress: true; activeFocusOnTab: true; - MouseArea { - anchors.fill: parent; - onPressed: { - var hidePassword = (passphraseField.echoMode === TextInput.Password); - sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - mouse.accepted = false; - } - } - onFocusChanged: { if (focus) { var hidePassword = (passphraseField.echoMode === TextInput.Password); @@ -151,15 +133,6 @@ Item { activeFocusOnPress: true; activeFocusOnTab: true; - MouseArea { - anchors.fill: parent; - onPressed: { - var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password); - sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - mouse.accepted = false; - } - } - onFocusChanged: { if (focus) { var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password); From 8a5904373b13915cfca294e119ef51ab07658270 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Tue, 5 Dec 2017 00:04:46 +0300 Subject: [PATCH 46/70] remove 'collapse keyboard' hack as now there is dedicated 'collapse keyboard' button --- .../hifi/commerce/wallet/PassphraseModal.qml | 19 ------------------- .../qml/hifi/commerce/wallet/Wallet.qml | 19 ------------------- 2 files changed, 38 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index 249645e76f..582052c4c3 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -352,25 +352,6 @@ Item { right: parent.right; } - Image { - id: lowerKeyboardButton; - z: 999; - source: "images/lowerKeyboard.png"; - anchors.right: keyboard.right; - anchors.top: keyboard.showMirrorText ? keyboard.top : undefined; - anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom; - height: 50; - width: 60; - - MouseArea { - anchors.fill: parent; - - onClicked: { - root.keyboardRaised = false; - } - } - } - HifiControlsUit.Keyboard { id: keyboard; raised: HMD.mounted && root.keyboardRaised; diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 759d7a37eb..97fee72968 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -666,25 +666,6 @@ Rectangle { right: parent.right; } - Image { - id: lowerKeyboardButton; - z: 999; - source: "images/lowerKeyboard.png"; - anchors.right: keyboard.right; - anchors.top: keyboard.showMirrorText ? keyboard.top : undefined; - anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom; - height: 50; - width: 60; - - MouseArea { - anchors.fill: parent; - - onClicked: { - root.keyboardRaised = false; - } - } - } - HifiControlsUit.Keyboard { id: keyboard; raised: HMD.mounted && root.keyboardRaised; From f7328f05fb3c3fc5af3fc2ba11763af8d2e1840d Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Tue, 5 Dec 2017 00:51:42 +0300 Subject: [PATCH 47/70] remove keyboard-hiding logic as keyboard hides should be handled by focus change listener in 'onFocusObjectChanged' in the OffscreenQmlSurface --- .../qml/hifi/commerce/wallet/PassphraseSelection.qml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index 166388afee..9ba066a9fd 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -82,8 +82,6 @@ Item { if (focus) { var hidePassword = (currentPassphraseField.echoMode === TextInput.Password); sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - } else if (!passphraseFieldAgain.focus) { - sendSignalToWallet({method: 'walletSetup_lowerKeyboard', isPasswordField: false}); } } @@ -110,8 +108,6 @@ Item { if (focus) { var hidePassword = (passphraseField.echoMode === TextInput.Password); sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - } else if (!passphraseFieldAgain.focus) { - sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false}); } } @@ -137,8 +133,6 @@ Item { if (focus) { var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password); sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - } else if (!passphraseField.focus) { - sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false}); } } From eb8438a9e85246c2af14eeacf2ff8b9874cf4f62 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Mon, 4 Dec 2017 18:25:51 -0500 Subject: [PATCH 48/70] [Case 7049] Addresses code review feedback. Changes Committed: modified: scripts/system/html/js/entityProperties.js --- scripts/system/html/js/entityProperties.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 4475e010bb..0ab9f7a9cb 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -455,7 +455,7 @@ function saveJSONUserData(noUpdate) { savedJSONTimer = setTimeout(function() { $('#userdata-saved').hide(); - }, 1500); + }, EDITOR_TIMEOUT_DURATION); } function bindAllNonJSONEditorElements() { @@ -747,12 +747,12 @@ function loaded() { for (var i = 0; i < selections.length; i++) { ids.push(selections[i].id); - var curSelectedType = selections[i].properties.type; - if (types[curSelectedType] === undefined) { - types[curSelectedType] = 0; + var currentSelectedType = selections[i].properties.type; + if (types[currentSelectedType] === undefined) { + types[currentSelectedType] = 0; numTypes += 1; } - types[curSelectedType]++; + types[currentSelectedType]++; } var type = "Multiple"; @@ -1748,9 +1748,9 @@ function loaded() { span.textContent = options[selectedOption].firstChild.textContent; dt.appendChild(span); - var spanCaratDn = document.createElement("span"); - spanCaratDn.textContent = "5"; // caratDn - dt.appendChild(spanCaratDn); + var spanCaratDown = document.createElement("span"); + spanCaratDown.textContent = "5"; // caratDn + dt.appendChild(spanCaratDown); var dd = document.createElement("dd"); dl.appendChild(dd); From 486cdf14d2a71824b079af5ba0b427787b373a91 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 4 Dec 2017 15:34:53 -0800 Subject: [PATCH 49/70] make sure that NO_BOOTSTRAPPING is disbaled correctly if parent is changed --- libraries/entities/src/EntityItem.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 29b104b421..82e364486b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1605,6 +1605,25 @@ void EntityItem::setParentID(const QUuid& value) { if (tree && !oldParentID.isNull()) { tree->removeFromChildrenOfAvatars(getThisPointer()); } + + bool enableNoBootStrapping = false; + if (!value.isNull() && tree) { + EntityItemPointer entity = tree->findEntityByEntityItemID(value); + if (entity) { + enableNoBootStrapping = (bool)(entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING); + } + } + + enableNoBootStrapping ? markDirtyFlags(Simulation::NO_BOOTSTRAPPING) : clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + enableNoBootStrapping ? entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING) : + entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + } + }); + SpatiallyNestable::setParentID(value); // children are forced to be kinematic // may need to not collide with own avatar From c58286d371ef8145df2f91f832f1a3698afe19fb Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 5 Dec 2017 09:31:35 -0800 Subject: [PATCH 50/70] dont do work if not needed --- libraries/entities/src/EntityItem.cpp | 39 ++++++++++++++++++++------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 82e364486b..453e78b305 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1606,23 +1606,42 @@ void EntityItem::setParentID(const QUuid& value) { tree->removeFromChildrenOfAvatars(getThisPointer()); } - bool enableNoBootStrapping = false; + uint32_t oldParentNoBootstrapping = 0; + uint32_t newParentNoBootstrapping = 0; if (!value.isNull() && tree) { EntityItemPointer entity = tree->findEntityByEntityItemID(value); if (entity) { - enableNoBootStrapping = (bool)(entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING); + newParentNoBootstrapping = entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING; } } - enableNoBootStrapping ? markDirtyFlags(Simulation::NO_BOOTSTRAPPING) : clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); - forEachDescendant([&](SpatiallyNestablePointer object) { - if (object->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(object); - entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - enableNoBootStrapping ? entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING) : - entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + if (!oldParentID.isNull() && tree) { + EntityItemPointer entity = tree->findEntityByEntityItemID(oldParentID); + if (entity) { + oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING; } - }); + } + + if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) { + if ((bool)(newParentNoBootstrapping & Simulation::NO_BOOTSTRAPPING)) { + markDirtyFlags(Simulation::NO_BOOTSTRAPPING); + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP | Simulation::NO_BOOTSTRAPPING); + } + }); + } else { + clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + } + }); + } + } SpatiallyNestable::setParentID(value); // children are forced to be kinematic From d59c9bd73aac2bb802e0c3f29f9e700fb1eb9b32 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 5 Dec 2017 10:26:49 -0800 Subject: [PATCH 51/70] Fix gray/incorrect security image after relog --- .../resources/qml/hifi/commerce/wallet/WalletHome.qml | 1 + interface/src/commerce/Wallet.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index ec7468651d..d23079d3f3 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -68,6 +68,7 @@ Item { Connections { target: GlobalServices onMyUsernameChanged: { + transactionHistoryModel.clear(); usernameText.text = Account.username; } } diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index d4611d3e9a..69914e97a4 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -321,6 +321,16 @@ Wallet::Wallet() { auto accountManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { getWalletStatus(); + _publicKeys.clear(); + + if (_securityImage) { + delete _securityImage; + } + _securityImage = nullptr; + + // tell the provider we got nothing + updateImageProvider(); + _passphrase->clear(); }); } From 4334bf8222935f8e4de987929d3aaa6251b1ee3e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 5 Dec 2017 13:09:09 -0800 Subject: [PATCH 52/70] Add data tracking for purchase and rez --- .../qml/hifi/commerce/checkout/Checkout.qml | 19 ++++++++++++++ .../hifi/commerce/purchases/PurchasedItem.qml | 6 +++++ .../UserActivityLoggerScriptingInterface.cpp | 25 +++++++++++++++++++ .../UserActivityLoggerScriptingInterface.h | 3 +++ 4 files changed, 53 insertions(+) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 6c4e020694..7d3c6e3f12 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -79,10 +79,23 @@ Rectangle { if (result.status !== 'success') { failureErrorText.text = result.message; root.activeView = "checkoutFailure"; + var data = { + "marketplaceID": root.itemId, + "cost": root.itemPrice, + "firstPurchaseOfThisItem": !root.alreadyOwned, + "errorDetails": result.message + } + UserActivityLogger.logAction("commercePurchaseFailure", data); } else { root.itemHref = result.data.download_url; root.isWearable = result.data.categories.indexOf("Wearables") > -1; root.activeView = "checkoutSuccess"; + var data = { + "marketplaceID": root.itemId, + "cost": root.itemPrice, + "firstPurchaseOfThisItem": !root.alreadyOwned + } + UserActivityLogger.logAction("commercePurchaseSuccess", data); } } @@ -599,6 +612,12 @@ Rectangle { sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); + var data = { + "marketplaceID": root.itemId, + "source": "checkout", + "type": root.isWearable ? "rez" : "wear" + } + UserActivityLogger.logAction("commerceEntityRezzed", data); } } RalewaySemiBold { diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 15ebada0c4..17e5b21562 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -349,6 +349,12 @@ Item { sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); + var data = { + "marketplaceID": root.itemId, + "source": "purchases", + "type": root.isWearable ? "rez" : "wear" + } + UserActivityLogger.logAction("commerceEntityRezzed", data); } style: ButtonStyle { diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index 61f2071c5f..f9d8decfa6 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -88,3 +88,28 @@ void UserActivityLoggerScriptingInterface::doLogAction(QString action, QJsonObje Q_ARG(QString, action), Q_ARG(QJsonObject, details)); } + +void UserActivityLoggerScriptingInterface::commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem) { + QJsonObject payload; + payload["marketplaceID"] = marketplaceID; + payload["cost"] = cost; + payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem; + doLogAction("commercePurchaseSuccess", payload); +} + +void UserActivityLoggerScriptingInterface::commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails) { + QJsonObject payload; + payload["marketplaceID"] = marketplaceID; + payload["cost"] = cost; + payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem; + payload["errorDetails"] = errorDetails; + doLogAction("commercePurchaseFailure", payload); +} + +void UserActivityLoggerScriptingInterface::commerceEntityRezzed(QString marketplaceID, QString source, QString type) { + QJsonObject payload; + payload["marketplaceID"] = marketplaceID; + payload["source"] = source; + payload["type"] = type; + doLogAction("commerceEntityRezzed", payload); +} diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 885f637a62..37d3ab4c12 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -33,6 +33,9 @@ public: Q_INVOKABLE void bubbleToggled(bool newValue); Q_INVOKABLE void bubbleActivated(); Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{}); + Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem); + Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails); + Q_INVOKABLE void commerceEntityRezzed(QString marketplaceID, QString source, QString type); private: void doLogAction(QString action, QJsonObject details = {}); }; From 668f6d50b5040855f9b6eba4b952762d8c624706 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 5 Dec 2017 14:13:48 -0800 Subject: [PATCH 53/70] Add data tracking for wallet setup --- .../qml/hifi/commerce/wallet/Wallet.qml | 10 +++++++ .../qml/hifi/commerce/wallet/WalletSetup.qml | 22 ++++++++++++++ .../UserActivityLoggerScriptingInterface.cpp | 29 +++++++++++++++++++ .../UserActivityLoggerScriptingInterface.h | 3 ++ 4 files changed, 64 insertions(+) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 71a73e31db..3578485a01 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -48,6 +48,16 @@ Rectangle { if (root.activeView !== "walletSetup") { root.activeView = "walletSetup"; commerce.resetLocalWalletOnly(); + var timestamp = new Date(); + walletSetup.startingTimestamp = timestamp; + var data = { + "timestamp": timestamp, + "setupAttemptID": guid(), + "setupFlowVersion": walletSetup.setupFlowVersion, + "referrer": walletSetup.referrer, + "currentDomain": (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '') + } + UserActivityLogger.logAction("commerceWalletSetupStarted", data); } } else if (walletStatus === 2) { if (root.activeView !== "passphraseModal") { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index 8de831ef75..773407cf8a 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -31,6 +31,9 @@ Item { property bool hasShownSecurityImageTip: false; property string referrer; property string keyFilePath; + property date startingTimestamp; + readonly property int setupFlowVersion: 1; + readonly property var setupStepNames: [ "Setup Prompt", "Security Image Selection", "Passphrase Selection", "Private Keys Ready" ]; Image { anchors.fill: parent; @@ -67,6 +70,18 @@ Item { anchors.fill: parent; } + onActiveViewChanged: { + var timestamp = new Date(); + var currentStepNumber = root.activeView.substring(5); + var data = { + "timestamp": timestamp, + "secondsElapsed": (root.startingTimestamp - timestamp), + "currentStepNumber": currentStepNumber, + "currentStepName": root.setupStepNames[currentStepNumber] + } + UserActivityLogger.logAction("commerceWalletSetupProgress", data); + } + // // TITLE BAR START // @@ -730,6 +745,13 @@ Item { root.visible = false; root.hasShownSecurityImageTip = false; sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""}); + + var timestamp = new Date(); + var data = { + "timestamp": timestamp, + "secondsToComplete": (root.startingTimestamp - timestamp) + } + UserActivityLogger.logAction("commerceWalletSetupFinished", data); } } } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index f9d8decfa6..ba24d8dcbe 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -113,3 +113,32 @@ void UserActivityLoggerScriptingInterface::commerceEntityRezzed(QString marketpl payload["type"] = type; doLogAction("commerceEntityRezzed", payload); } + +void UserActivityLoggerScriptingInterface::commerceWalletSetupStarted(float timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain) { + QJsonObject payload; + payload["timestamp"] = timestamp; + payload["setupAttemptID"] = setupAttemptID; + payload["setupFlowVersion"] = setupFlowVersion; + payload["referrer"] = referrer; + payload["currentDomain"] = currentDomain; + qDebug() << "ZRF" << payload; + //doLogAction("commerceWalletSetupStarted", payload); +} + +void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(float timestamp, float secondsElapsed, int currentStepNumber, QString currentStepName) { + QJsonObject payload; + payload["timestamp"] = timestamp; + payload["secondsElapsed"] = secondsElapsed; + payload["currentStepNumber"] = currentStepNumber; + payload["currentStepName"] = currentStepName; + qDebug() << "ZRF" << payload; + //doLogAction("commerceWalletSetupProgress", payload); +} + +void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(float timestamp, float secondsToComplete) { + QJsonObject payload; + payload["timestamp"] = timestamp; + payload["secondsToComplete"] = secondsToComplete; + qDebug() << "ZRF" << payload; + //doLogAction("commerceWalletSetupFinished", payload); +} diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 37d3ab4c12..3e8002e0aa 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -36,6 +36,9 @@ public: Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem); Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails); Q_INVOKABLE void commerceEntityRezzed(QString marketplaceID, QString source, QString type); + Q_INVOKABLE void commerceWalletSetupStarted(float timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain); + Q_INVOKABLE void commerceWalletSetupProgress(float timestamp, float secondsElapsed, int currentStepNumber, QString currentStepName); + Q_INVOKABLE void commerceWalletSetupFinished(float timestamp, float secondsToComplete); private: void doLogAction(QString action, QJsonObject details = {}); }; From 7521e4870e82c943eb1afa55eb707ef82640461a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 5 Dec 2017 14:56:47 -0800 Subject: [PATCH 54/70] Bugfixes --- .../qml/hifi/commerce/checkout/Checkout.qml | 22 ++---------- .../hifi/commerce/purchases/PurchasedItem.qml | 7 +--- .../qml/hifi/commerce/wallet/Help.qml | 34 ++++++++++++++++++- .../qml/hifi/commerce/wallet/Wallet.qml | 26 +++++++++----- .../qml/hifi/commerce/wallet/WalletSetup.qml | 14 ++------ .../UserActivityLoggerScriptingInterface.cpp | 6 ++-- .../UserActivityLoggerScriptingInterface.h | 6 ++-- scripts/system/marketplaces/marketplaces.js | 1 + 8 files changed, 64 insertions(+), 52 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 7d3c6e3f12..c8a63a4d2d 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -79,23 +79,12 @@ Rectangle { if (result.status !== 'success') { failureErrorText.text = result.message; root.activeView = "checkoutFailure"; - var data = { - "marketplaceID": root.itemId, - "cost": root.itemPrice, - "firstPurchaseOfThisItem": !root.alreadyOwned, - "errorDetails": result.message - } - UserActivityLogger.logAction("commercePurchaseFailure", data); + UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemPrice, !root.alreadyOwned, result.message); } else { root.itemHref = result.data.download_url; root.isWearable = result.data.categories.indexOf("Wearables") > -1; root.activeView = "checkoutSuccess"; - var data = { - "marketplaceID": root.itemId, - "cost": root.itemPrice, - "firstPurchaseOfThisItem": !root.alreadyOwned - } - UserActivityLogger.logAction("commercePurchaseSuccess", data); + UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemPrice, !root.alreadyOwned); } } @@ -612,12 +601,7 @@ Rectangle { sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); - var data = { - "marketplaceID": root.itemId, - "source": "checkout", - "type": root.isWearable ? "rez" : "wear" - } - UserActivityLogger.logAction("commerceEntityRezzed", data); + UserActivityLogger.commerceEntityRezzed(root.itemId, "checkout", root.isWearable ? "rez" : "wear"); } } RalewaySemiBold { diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 17e5b21562..f7913e5b1e 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -349,12 +349,7 @@ Item { sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); - var data = { - "marketplaceID": root.itemId, - "source": "purchases", - "type": root.isWearable ? "rez" : "wear" - } - UserActivityLogger.logAction("commerceEntityRezzed", data); + UserActivityLogger.commerceEntityRezzed(root.itemId, "purchases", root.isWearable ? "rez" : "wear"); } style: ButtonStyle { diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index ebba2b87c6..8cccb10533 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -55,6 +55,38 @@ Item { // Style color: hifi.colors.blueHighlight; } + + HifiControlsUit.Button { + id: clearCachedPassphraseButton; + visible: root.showDebugButtons; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.top: parent.top; + anchors.left: helpTitleText.right; + anchors.leftMargin: 20; + height: 40; + width: 150; + text: "DBG: Clear Pass"; + onClicked: { + commerce.setPassphrase(""); + sendSignalToWallet({method: 'passphraseReset'}); + } + } + HifiControlsUit.Button { + id: resetButton; + visible: root.showDebugButtons; + color: hifi.buttons.red; + colorScheme: hifi.colorSchemes.dark; + anchors.top: clearCachedPassphraseButton.top; + anchors.left: clearCachedPassphraseButton.right; + height: 40; + width: 150; + text: "DBG: RST Wallet"; + onClicked: { + commerce.reset(); + sendSignalToWallet({method: 'walletReset'}); + } + } ListModel { id: helpModel; @@ -147,7 +179,7 @@ Item { text: model.isExpanded ? "-" : "+"; // Anchors anchors.top: parent.top; - anchors.topMargin: model.isExpanded ? -9 : 0; + anchors.topMargin: model.isExpanded ?9 : 0; anchors.bottom: parent.bottom; anchors.left: parent.left; width: 60; diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 3578485a01..d7e4536fed 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -50,14 +50,8 @@ Rectangle { commerce.resetLocalWalletOnly(); var timestamp = new Date(); walletSetup.startingTimestamp = timestamp; - var data = { - "timestamp": timestamp, - "setupAttemptID": guid(), - "setupFlowVersion": walletSetup.setupFlowVersion, - "referrer": walletSetup.referrer, - "currentDomain": (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '') - } - UserActivityLogger.logAction("commerceWalletSetupStarted", data); + UserActivityLogger.commerceWalletSetupStarted(timestamp, generateUUID(), walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app", + (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '')); } } else if (walletStatus === 2) { if (root.activeView !== "passphraseModal") { @@ -711,12 +705,28 @@ Rectangle { case 'updateWalletReferrer': walletSetup.referrer = message.referrer; break; + case 'inspectionCertificate_resetCert': + // NOP + break; default: console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); } } signal sendToScript(var message); + // generateUUID() taken from: + // https://stackoverflow.com/a/8809472 + function generateUUID() { // Public Domain/MIT + var d = new Date().getTime(); + if (typeof performance !== 'undefined' && typeof performance.now === 'function'){ + d += performance.now(); //use high-precision timer if available + } + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); + } // // FUNCTION DEFINITIONS END // diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index 773407cf8a..9bb578f7d6 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -73,13 +73,7 @@ Item { onActiveViewChanged: { var timestamp = new Date(); var currentStepNumber = root.activeView.substring(5); - var data = { - "timestamp": timestamp, - "secondsElapsed": (root.startingTimestamp - timestamp), - "currentStepNumber": currentStepNumber, - "currentStepName": root.setupStepNames[currentStepNumber] - } - UserActivityLogger.logAction("commerceWalletSetupProgress", data); + UserActivityLogger.commerceWalletSetupProgress(timestamp, Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]); } // @@ -747,11 +741,7 @@ Item { sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""}); var timestamp = new Date(); - var data = { - "timestamp": timestamp, - "secondsToComplete": (root.startingTimestamp - timestamp) - } - UserActivityLogger.logAction("commerceWalletSetupFinished", data); + UserActivityLogger.commerceWalletSetupFinished(timestamp, Math.round((timestamp - root.startingTimestamp)/1000)); } } } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index ba24d8dcbe..79fa7dad0f 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -114,7 +114,7 @@ void UserActivityLoggerScriptingInterface::commerceEntityRezzed(QString marketpl doLogAction("commerceEntityRezzed", payload); } -void UserActivityLoggerScriptingInterface::commerceWalletSetupStarted(float timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain) { +void UserActivityLoggerScriptingInterface::commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain) { QJsonObject payload; payload["timestamp"] = timestamp; payload["setupAttemptID"] = setupAttemptID; @@ -125,7 +125,7 @@ void UserActivityLoggerScriptingInterface::commerceWalletSetupStarted(float time //doLogAction("commerceWalletSetupStarted", payload); } -void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(float timestamp, float secondsElapsed, int currentStepNumber, QString currentStepName) { +void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(int timestamp, int secondsElapsed, int currentStepNumber, QString currentStepName) { QJsonObject payload; payload["timestamp"] = timestamp; payload["secondsElapsed"] = secondsElapsed; @@ -135,7 +135,7 @@ void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(float tim //doLogAction("commerceWalletSetupProgress", payload); } -void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(float timestamp, float secondsToComplete) { +void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(int timestamp, int secondsToComplete) { QJsonObject payload; payload["timestamp"] = timestamp; payload["secondsToComplete"] = secondsToComplete; diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 3e8002e0aa..aafbbb7df0 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -36,9 +36,9 @@ public: Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem); Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails); Q_INVOKABLE void commerceEntityRezzed(QString marketplaceID, QString source, QString type); - Q_INVOKABLE void commerceWalletSetupStarted(float timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain); - Q_INVOKABLE void commerceWalletSetupProgress(float timestamp, float secondsElapsed, int currentStepNumber, QString currentStepName); - Q_INVOKABLE void commerceWalletSetupFinished(float timestamp, float secondsToComplete); + Q_INVOKABLE void commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain); + Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, int secondsElapsed, int currentStepNumber, QString currentStepName); + Q_INVOKABLE void commerceWalletSetupFinished(int timestamp, int secondsToComplete); private: void doLogAction(QString action, QJsonObject details = {}); }; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 646e5452df..24b2947bcf 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -399,6 +399,7 @@ referrer: "purchases" }); openWallet(); + break; case 'checkout_walletNotSetUp': wireEventBridge(true); tablet.sendToQml({ From c807fc80e3a0a24c1831a0f53a7685f013e82423 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 5 Dec 2017 15:25:05 -0800 Subject: [PATCH 55/70] Add marketplace cta referrer --- .../resources/qml/hifi/commerce/wallet/Wallet.qml | 2 +- scripts/system/marketplaces/marketplaces.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index d7e4536fed..22cec52b48 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -177,7 +177,7 @@ Rectangle { Connections { onSendSignalToWallet: { if (msg.method === 'walletSetup_finished') { - if (msg.referrer === '') { + if (msg.referrer === '' || msg.referrer === 'marketplace cta') { root.activeView = "initialize"; commerce.getWalletStatus(); } else if (msg.referrer === 'purchases') { diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 24b2947bcf..cfb0f4cc8e 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -110,8 +110,9 @@ var filterText; // Used for updating Purchases QML function onScreenChanged(type, url) { onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; - onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH_BASE) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1); - wireEventBridge(onCommerceScreen); + onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH_BASE) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH + || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1 || url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1); + wireEventBridge(onMarketplaceScreen || onCommerceScreen); if (url === MARKETPLACE_PURCHASES_QML_PATH) { tablet.sendToQml({ @@ -322,6 +323,11 @@ } else if (parsedJsonMessage.type === "LOGIN") { openLoginWindow(); } else if (parsedJsonMessage.type === "WALLET_SETUP") { + wireEventBridge(true); + tablet.sendToQml({ + method: 'updateWalletReferrer', + referrer: "marketplace cta" + }); openWallet(); } else if (parsedJsonMessage.type === "MY_ITEMS") { referrerURL = MARKETPLACE_URL_INITIAL; From 902064ed373c271eac433d8508bbfe382881538c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 5 Dec 2017 15:26:55 -0800 Subject: [PATCH 56/70] Cleanup debug stuff --- .../qml/hifi/commerce/wallet/Help.qml | 34 +------------------ .../UserActivityLoggerScriptingInterface.cpp | 9 ++--- 2 files changed, 4 insertions(+), 39 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 8cccb10533..ebba2b87c6 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -55,38 +55,6 @@ Item { // Style color: hifi.colors.blueHighlight; } - - HifiControlsUit.Button { - id: clearCachedPassphraseButton; - visible: root.showDebugButtons; - color: hifi.buttons.black; - colorScheme: hifi.colorSchemes.dark; - anchors.top: parent.top; - anchors.left: helpTitleText.right; - anchors.leftMargin: 20; - height: 40; - width: 150; - text: "DBG: Clear Pass"; - onClicked: { - commerce.setPassphrase(""); - sendSignalToWallet({method: 'passphraseReset'}); - } - } - HifiControlsUit.Button { - id: resetButton; - visible: root.showDebugButtons; - color: hifi.buttons.red; - colorScheme: hifi.colorSchemes.dark; - anchors.top: clearCachedPassphraseButton.top; - anchors.left: clearCachedPassphraseButton.right; - height: 40; - width: 150; - text: "DBG: RST Wallet"; - onClicked: { - commerce.reset(); - sendSignalToWallet({method: 'walletReset'}); - } - } ListModel { id: helpModel; @@ -179,7 +147,7 @@ Item { text: model.isExpanded ? "-" : "+"; // Anchors anchors.top: parent.top; - anchors.topMargin: model.isExpanded ?9 : 0; + anchors.topMargin: model.isExpanded ? -9 : 0; anchors.bottom: parent.bottom; anchors.left: parent.left; width: 60; diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index 79fa7dad0f..b58537f8ef 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -121,8 +121,7 @@ void UserActivityLoggerScriptingInterface::commerceWalletSetupStarted(int timest payload["setupFlowVersion"] = setupFlowVersion; payload["referrer"] = referrer; payload["currentDomain"] = currentDomain; - qDebug() << "ZRF" << payload; - //doLogAction("commerceWalletSetupStarted", payload); + doLogAction("commerceWalletSetupStarted", payload); } void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(int timestamp, int secondsElapsed, int currentStepNumber, QString currentStepName) { @@ -131,14 +130,12 @@ void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(int times payload["secondsElapsed"] = secondsElapsed; payload["currentStepNumber"] = currentStepNumber; payload["currentStepName"] = currentStepName; - qDebug() << "ZRF" << payload; - //doLogAction("commerceWalletSetupProgress", payload); + doLogAction("commerceWalletSetupProgress", payload); } void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(int timestamp, int secondsToComplete) { QJsonObject payload; payload["timestamp"] = timestamp; payload["secondsToComplete"] = secondsToComplete; - qDebug() << "ZRF" << payload; - //doLogAction("commerceWalletSetupFinished", payload); + doLogAction("commerceWalletSetupFinished", payload); } From 2752dc7cacfcfb9cdcc62655f57644cfa405377c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 6 Dec 2017 12:28:55 +1300 Subject: [PATCH 57/70] Fix dimensions of cloned cube, sphere, shape, and model overlays --- interface/src/ui/overlays/ModelOverlay.cpp | 5 +++-- interface/src/ui/overlays/Volume3DOverlay.cpp | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 09d9ba574a..2cd9b0f7f5 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -34,8 +34,9 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : _model(std::make_shared(nullptr, this)), _modelTextures(QVariantMap()), _url(modelOverlay->_url), - _updateModel(false), - _loadPriority(modelOverlay->getLoadPriority()) + _scaleToFit(modelOverlay->_scaleToFit), + _loadPriority(modelOverlay->_loadPriority), + _updateModel(false) { _model->init(); _model->setLoadingPriority(_loadPriority); diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index b580545288..49c76e6108 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -14,7 +14,8 @@ #include Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) : - Base3DOverlay(volume3DOverlay) + Base3DOverlay(volume3DOverlay), + _localBoundingBox(volume3DOverlay->_localBoundingBox) { } From e074bd85f9683cab2e21b1df4ec4948917d00b7f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 6 Dec 2017 12:29:09 +1300 Subject: [PATCH 58/70] Fix shape of cloned shape overlay --- interface/src/ui/overlays/Shape3DOverlay.cpp | 5 +++-- interface/src/ui/overlays/Shape3DOverlay.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index ca6446d215..07949d5349 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -18,8 +18,9 @@ QString const Shape3DOverlay::TYPE = "shape"; -Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) : - Volume3DOverlay(Shape3DOverlay) +Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* shape3DOverlay) : + Volume3DOverlay(shape3DOverlay), + _shape(shape3DOverlay->_shape) { } diff --git a/interface/src/ui/overlays/Shape3DOverlay.h b/interface/src/ui/overlays/Shape3DOverlay.h index e9e26e3c94..7fc95ec981 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.h +++ b/interface/src/ui/overlays/Shape3DOverlay.h @@ -23,7 +23,7 @@ public: virtual QString getType() const override { return TYPE; } Shape3DOverlay() {} - Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay); + Shape3DOverlay(const Shape3DOverlay* shape3DOverlay); virtual void render(RenderArgs* args) override; virtual const render::ShapeKey getShapeKey() override; From 8af1d8d8d02c9590d5b0602956c7233d4b4eb545 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 5 Dec 2017 15:48:09 -0800 Subject: [PATCH 59/70] dont run code if not nesscassry --- libraries/entities/src/EntityItem.cpp | 37 +++++++++++++-------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 453e78b305..55b345cff5 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1968,17 +1968,19 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn if (success) { _allActionsDataCache = newDataCache; _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar auto actionType = action->getType(); if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) { - _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; - forEachDescendant([&](SpatiallyNestablePointer child) { - if (child->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); - } - }); + if (!(bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { + _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; + _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); + } + }); + } } } else { qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed"; @@ -2056,7 +2058,6 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi action->setOwnerEntity(nullptr); action->setIsMine(false); - _objectActions.remove(actionID); if (simulation) { action->removeFromSimulation(simulation); @@ -2065,17 +2066,10 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi bool success = true; serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar - if (stillHasGrabActions()) { - _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; - forEachDescendant([&](SpatiallyNestablePointer child) { - if (child->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); - } - }); - } else if ((bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { + auto removedActionType = action->getType(); + if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) { _dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING; + _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); @@ -2083,7 +2077,12 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); } }); + } else { + // NO-OP: we assume NO_BOOTSTRAPPING bits and collision group are correct + // because they should have been set correctly when the action was added + // and/or when children were linked } + _objectActions.remove(actionID); setDynamicDataNeedsTransmit(true); return success; } From cafebf6a7e89653fc6af55ca84ec68902ad4bf76 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 6 Dec 2017 13:38:42 +1300 Subject: [PATCH 60/70] Fix Unix build warnings and tidy initializers --- interface/src/ui/overlays/ModelOverlay.cpp | 4 ++-- interface/src/ui/overlays/ModelOverlay.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 2cd9b0f7f5..f27e26280a 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -34,9 +34,9 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : _model(std::make_shared(nullptr, this)), _modelTextures(QVariantMap()), _url(modelOverlay->_url), + _updateModel(false), _scaleToFit(modelOverlay->_scaleToFit), - _loadPriority(modelOverlay->_loadPriority), - _updateModel(false) + _loadPriority(modelOverlay->_loadPriority) { _model->init(); _model->setLoadingPriority(_loadPriority); diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index ea0eff170c..c4506d9621 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -75,8 +75,8 @@ private: QVariantMap _modelTextures; QUrl _url; - bool _updateModel = { false }; - bool _scaleToFit = { false }; + bool _updateModel { false }; + bool _scaleToFit { false }; float _loadPriority { 0.0f }; AnimationPointer _animation; @@ -87,7 +87,7 @@ private: bool _animationRunning { false }; bool _animationLoop { false }; float _animationFirstFrame { 0.0f }; - float _animationLastFrame = { 0.0f }; + float _animationLastFrame { 0.0f }; bool _animationHold { false }; bool _animationAllowTranslation { false }; uint64_t _lastAnimated { 0 }; From 3183a1a8b983dcf79ef7d7fd5657c92831f4b01c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 5 Dec 2017 16:48:30 -0800 Subject: [PATCH 61/70] Add setupAttemptID to every step (thanks Dave) --- interface/resources/qml/hifi/commerce/wallet/Wallet.qml | 3 ++- .../resources/qml/hifi/commerce/wallet/WalletSetup.qml | 6 ++++-- .../networking/src/UserActivityLoggerScriptingInterface.cpp | 6 ++++-- .../networking/src/UserActivityLoggerScriptingInterface.h | 4 ++-- scripts/system/marketplaces/marketplaces.js | 5 +++-- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 22cec52b48..ac05bf7c84 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -50,7 +50,8 @@ Rectangle { commerce.resetLocalWalletOnly(); var timestamp = new Date(); walletSetup.startingTimestamp = timestamp; - UserActivityLogger.commerceWalletSetupStarted(timestamp, generateUUID(), walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app", + walletSetup.setupAttemptID = generateUUID(); + UserActivityLogger.commerceWalletSetupStarted(timestamp, setupAttemptID, walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app", (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '')); } } else if (walletStatus === 2) { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index 9bb578f7d6..d7859d2800 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -32,6 +32,7 @@ Item { property string referrer; property string keyFilePath; property date startingTimestamp; + property string setupAttemptID; readonly property int setupFlowVersion: 1; readonly property var setupStepNames: [ "Setup Prompt", "Security Image Selection", "Passphrase Selection", "Private Keys Ready" ]; @@ -73,7 +74,8 @@ Item { onActiveViewChanged: { var timestamp = new Date(); var currentStepNumber = root.activeView.substring(5); - UserActivityLogger.commerceWalletSetupProgress(timestamp, Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]); + UserActivityLogger.commerceWalletSetupProgress(timestamp, root.setupAttemptID, + Math.round((timestamp - root.startingTimestamp)/1000), currentStepNumber, root.setupStepNames[currentStepNumber - 1]); } // @@ -741,7 +743,7 @@ Item { sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""}); var timestamp = new Date(); - UserActivityLogger.commerceWalletSetupFinished(timestamp, Math.round((timestamp - root.startingTimestamp)/1000)); + UserActivityLogger.commerceWalletSetupFinished(timestamp, setupAttemptID, Math.round((timestamp - root.startingTimestamp)/1000)); } } } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index b58537f8ef..0965c9834f 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -124,18 +124,20 @@ void UserActivityLoggerScriptingInterface::commerceWalletSetupStarted(int timest doLogAction("commerceWalletSetupStarted", payload); } -void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(int timestamp, int secondsElapsed, int currentStepNumber, QString currentStepName) { +void UserActivityLoggerScriptingInterface::commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName) { QJsonObject payload; payload["timestamp"] = timestamp; + payload["setupAttemptID"] = setupAttemptID; payload["secondsElapsed"] = secondsElapsed; payload["currentStepNumber"] = currentStepNumber; payload["currentStepName"] = currentStepName; doLogAction("commerceWalletSetupProgress", payload); } -void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(int timestamp, int secondsToComplete) { +void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(int timestamp, QString setupAttemptID, int secondsToComplete) { QJsonObject payload; payload["timestamp"] = timestamp; + payload["setupAttemptID"] = setupAttemptID; payload["secondsToComplete"] = secondsToComplete; doLogAction("commerceWalletSetupFinished", payload); } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index aafbbb7df0..e71723f03c 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -37,8 +37,8 @@ public: Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails); Q_INVOKABLE void commerceEntityRezzed(QString marketplaceID, QString source, QString type); Q_INVOKABLE void commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain); - Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, int secondsElapsed, int currentStepNumber, QString currentStepName); - Q_INVOKABLE void commerceWalletSetupFinished(int timestamp, int secondsToComplete); + Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName); + Q_INVOKABLE void commerceWalletSetupFinished(int timestamp, QString setupAttemptID, int secondsToComplete); private: void doLogAction(QString action, QJsonObject details = {}); }; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index cfb0f4cc8e..1893c2d097 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -110,8 +110,9 @@ var filterText; // Used for updating Purchases QML function onScreenChanged(type, url) { onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; + onWalletScreen = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1; onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH_BASE) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH - || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1 || url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1); + || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1 || onWalletScreen); wireEventBridge(onMarketplaceScreen || onCommerceScreen); if (url === MARKETPLACE_PURCHASES_QML_PATH) { @@ -123,7 +124,7 @@ } // for toolbar mode: change button to active when window is first openend, false otherwise. - marketplaceButton.editProperties({ isActive: onMarketplaceScreen || onCommerceScreen }); + marketplaceButton.editProperties({ isActive: (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen }); if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { ContextOverlay.isInMarketplaceInspectionMode = true; } else { From 0f3a70553c624769bb825afa54b3b526eb50af96 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 6 Dec 2017 11:18:15 -0800 Subject: [PATCH 62/70] fix dynamic entities that are parented to your avatar --- libraries/entities/src/EntityItem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 55b345cff5..48370b02fd 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1622,6 +1622,10 @@ void EntityItem::setParentID(const QUuid& value) { } } + if (!value.isNull() && (value == Physics::getSessionUUID() || value == AVATAR_SELF_ID)) { + newParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING; + } + if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) { if ((bool)(newParentNoBootstrapping & Simulation::NO_BOOTSTRAPPING)) { markDirtyFlags(Simulation::NO_BOOTSTRAPPING); From c329a58c1468363a592b9a59c6dddbccaa529b3d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 6 Dec 2017 11:26:34 -0800 Subject: [PATCH 63/70] ignore vcredist when performing fixup_bundle --- cmake/templates/FixupBundlePostBuild.cmake.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmake/templates/FixupBundlePostBuild.cmake.in b/cmake/templates/FixupBundlePostBuild.cmake.in index d4726884c2..57379bb48b 100644 --- a/cmake/templates/FixupBundlePostBuild.cmake.in +++ b/cmake/templates/FixupBundlePostBuild.cmake.in @@ -45,5 +45,4 @@ else() endif() file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}") -fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@") - +fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@" IGNORE_ITEM "vcredist_x86.exe;vcredist_x64.exe") From 008b4285f5a2ca9e4f63c26a57861e3a4ba32586 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 6 Dec 2017 11:35:14 -0800 Subject: [PATCH 64/70] don't use cmake sys library install, grab vcredist as external --- cmake/externals/vcredist/CMakeLists.txt | 21 +++++++++++++++++++++ cmake/macros/GenerateInstallers.cmake | 5 ----- 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 cmake/externals/vcredist/CMakeLists.txt diff --git a/cmake/externals/vcredist/CMakeLists.txt b/cmake/externals/vcredist/CMakeLists.txt new file mode 100644 index 0000000000..ac242cf451 --- /dev/null +++ b/cmake/externals/vcredist/CMakeLists.txt @@ -0,0 +1,21 @@ +if (WIN32) + set(EXTERNAL_NAME vcredist) + string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + + include(ExternalProject) + ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://go.microsoft.com/fwlink/?LinkId=746572 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) + + # Hide this external target (for ide users) + set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + + ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + + set(${EXTERNAL_NAME_UPPER}_PATH ${SOURCE_DIR} CACHE FILEPATH "Location of VC Redistributable") +endif() diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 6c131168d5..20ab32bf62 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -29,10 +29,6 @@ macro(GENERATE_INSTALLERS) if (WIN32) - # Do not install the Visual Studio C runtime libraries. The installer will do this automatically - set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) - - include(InstallRequiredSystemLibraries) set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico") # install and reference the Add/Remove icon @@ -84,4 +80,3 @@ macro(GENERATE_INSTALLERS) include(CPack) endmacro() - From 99b5b8942a24a3c94bac4f9719beda03b04e013f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 6 Dec 2017 12:08:18 -0800 Subject: [PATCH 65/70] add file download command to install step --- cmake/externals/vcredist/CMakeLists.txt | 21 --------------------- cmake/macros/GenerateInstallers.cmake | 4 ++++ 2 files changed, 4 insertions(+), 21 deletions(-) delete mode 100644 cmake/externals/vcredist/CMakeLists.txt diff --git a/cmake/externals/vcredist/CMakeLists.txt b/cmake/externals/vcredist/CMakeLists.txt deleted file mode 100644 index ac242cf451..0000000000 --- a/cmake/externals/vcredist/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -if (WIN32) - set(EXTERNAL_NAME vcredist) - string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - - include(ExternalProject) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://go.microsoft.com/fwlink/?LinkId=746572 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) - - # Hide this external target (for ide users) - set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - - ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - - set(${EXTERNAL_NAME_UPPER}_PATH ${SOURCE_DIR} CACHE FILEPATH "Location of VC Redistributable") -endif() diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 20ab32bf62..702636dd01 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -45,6 +45,10 @@ macro(GENERATE_INSTALLERS) set(_UNINSTALLER_HEADER_BAD_PATH "${HF_CMAKE_DIR}/installer/uninstaller-header.bmp") set(UNINSTALLER_HEADER_IMAGE "") fix_path_for_nsis(${_UNINSTALLER_HEADER_BAD_PATH} UNINSTALLER_HEADER_IMAGE) + + # grab the latest VC redist (2017) and add it to the installer, our NSIS template + # will call it during the install + install(CODE "file(DOWNLOAD https://go.microsoft.com/fwlink/?LinkId=746572 \"\${CMAKE_INSTALL_PREFIX}/vcredist_x64.exe\")") elseif (APPLE) # produce a drag and drop DMG on OS X set(CPACK_GENERATOR "DragNDrop") From 5f3b148baaefd327890504c11d584373633da317 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 6 Dec 2017 13:01:23 -0800 Subject: [PATCH 66/70] Fix marketplace icon incorrectly appearing active --- scripts/system/marketplaces/marketplaces.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 1893c2d097..80ef79a543 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -112,8 +112,8 @@ onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; onWalletScreen = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1; onCommerceScreen = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH_BASE) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH - || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1 || onWalletScreen); - wireEventBridge(onMarketplaceScreen || onCommerceScreen); + || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1); + wireEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen); if (url === MARKETPLACE_PURCHASES_QML_PATH) { tablet.sendToQml({ From e626ca3ccde903681977e6390f8f9c73ecbe11e6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 3 Dec 2017 13:40:00 -0800 Subject: [PATCH 67/70] Working on scene threading optimizations --- libraries/render/src/render/Scene.cpp | 162 ++++++++++++++++++++------ libraries/render/src/render/Scene.h | 14 ++- 2 files changed, 137 insertions(+), 39 deletions(-) diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 88e25b6d27..508cd6cefb 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -32,23 +32,23 @@ void Transaction::removeItem(ItemID id) { } void Transaction::addTransitionToItem(ItemID id, Transition::Type transition, ItemID boundId) { - _addedTransitions.emplace_back(TransitionAdd{ id, transition, boundId }); + _addedTransitions.emplace_back(id, transition, boundId); } void Transaction::removeTransitionFromItem(ItemID id) { - _addedTransitions.emplace_back(TransitionAdd{ id, Transition::NONE, render::Item::INVALID_ITEM_ID }); + _addedTransitions.emplace_back(id, Transition::NONE, render::Item::INVALID_ITEM_ID); } void Transaction::reApplyTransitionToItem(ItemID id) { - _reAppliedTransitions.emplace_back(TransitionReApply{ id }); + _reAppliedTransitions.emplace_back(id); } void Transaction::queryTransitionOnItem(ItemID id, TransitionQueryFunc func) { - _queriedTransitions.emplace_back(TransitionQuery{ id, func }); + _queriedTransitions.emplace_back(id, func); } void Transaction::updateItem(ItemID id, const UpdateFunctorPointer& functor) { - _updatedItems.emplace_back(Update{ id, functor }); + _updatedItems.emplace_back(id, functor); } void Transaction::resetSelection(const Selection& selection) { @@ -56,28 +56,122 @@ void Transaction::resetSelection(const Selection& selection) { } void Transaction::resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style) { - _highlightResets.emplace_back(HighlightReset{ selectionName, style }); + _highlightResets.emplace_back(selectionName, style ); } void Transaction::removeHighlightFromSelection(const std::string& selectionName) { _highlightRemoves.emplace_back(selectionName); } -void Transaction::querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func) { - _highlightQueries.emplace_back(HighlightQuery{ selectionName, func }); +void Transaction::querySelectionHighlight(const std::string& selectionName, const SelectionHighlightQueryFunc& func) { + _highlightQueries.emplace_back(selectionName, func); +} + +void Transaction::reserve(const std::vector& transactionContainer) { + size_t resetItemsCount = 0; + size_t removedItemsCount = 0; + size_t updatedItemsCount = 0; + size_t resetSelectionsCount = 0; + size_t addedTransitionsCount = 0; + size_t queriedTransitionsCount = 0; + size_t reAppliedTransitionsCount = 0; + size_t highlightResetsCount = 0; + size_t highlightRemovesCount = 0; + size_t highlightQueriesCount = 0; + + for (const auto& transaction : transactionContainer) { + resetItemsCount += transaction._resetItems.size(); + removedItemsCount += transaction._removedItems.size(); + updatedItemsCount += transaction._updatedItems.size(); + resetSelectionsCount += transaction._resetSelections.size(); + addedTransitionsCount += transaction._addedTransitions.size(); + queriedTransitionsCount += transaction._queriedTransitions.size(); + reAppliedTransitionsCount += transaction._reAppliedTransitions.size(); + highlightResetsCount += transaction._highlightResets.size(); + highlightRemovesCount += transaction._highlightRemoves.size(); + highlightQueriesCount += transaction._highlightQueries.size(); + } + + _resetItems.reserve(resetItemsCount); + _removedItems.reserve(removedItemsCount); + _updatedItems.reserve(updatedItemsCount); + _resetSelections.reserve(resetSelectionsCount); + _addedTransitions.reserve(addedTransitionsCount); + _queriedTransitions.reserve(queriedTransitionsCount); + _reAppliedTransitions.reserve(reAppliedTransitionsCount); + _highlightResets.reserve(highlightResetsCount); + _highlightRemoves.reserve(highlightRemovesCount); + _highlightQueries.reserve(highlightQueriesCount); +} + +void Transaction::merge(const std::vector& transactionContainer) { + reserve(transactionContainer); + for (const auto& transaction : transactionContainer) { + merge(transaction); + } +} + + +void Transaction::merge(std::vector&& transactionContainer) { + reserve(transactionContainer); + auto begin = std::make_move_iterator(transactionContainer.begin()); + auto end = std::make_move_iterator(transactionContainer.end()); + for (auto itr = begin; itr != end; ++itr) { + merge(*itr); + } + transactionContainer.clear(); +} + + +template +void moveElements(T& target, T& source) { + target.insert(target.end(), std::make_move_iterator(source.begin()), std::make_move_iterator(source.begin())); + source.clear(); +} + +template +void copyElements(T& target, const T& source) { + target.insert(target.end(), source.begin(), source.begin()); +} + + +void Transaction::merge(Transaction&& transaction) { + moveElements(_resetItems, transaction._resetItems); + moveElements(_removedItems, transaction._removedItems); + moveElements(_updatedItems, transaction._updatedItems); + moveElements(_resetSelections, transaction._resetSelections); + moveElements(_addedTransitions, transaction._addedTransitions); + moveElements(_queriedTransitions, transaction._queriedTransitions); + moveElements(_reAppliedTransitions, transaction._reAppliedTransitions); + moveElements(_highlightResets, transaction._highlightResets); + moveElements(_highlightRemoves, transaction._highlightRemoves); + moveElements(_highlightQueries, transaction._highlightQueries); } void Transaction::merge(const Transaction& transaction) { - _resetItems.insert(_resetItems.end(), transaction._resetItems.begin(), transaction._resetItems.end()); - _removedItems.insert(_removedItems.end(), transaction._removedItems.begin(), transaction._removedItems.end()); - _updatedItems.insert(_updatedItems.end(), transaction._updatedItems.begin(), transaction._updatedItems.end()); - _resetSelections.insert(_resetSelections.end(), transaction._resetSelections.begin(), transaction._resetSelections.end()); - _addedTransitions.insert(_addedTransitions.end(), transaction._addedTransitions.begin(), transaction._addedTransitions.end()); - _queriedTransitions.insert(_queriedTransitions.end(), transaction._queriedTransitions.begin(), transaction._queriedTransitions.end()); - _reAppliedTransitions.insert(_reAppliedTransitions.end(), transaction._reAppliedTransitions.begin(), transaction._reAppliedTransitions.end()); - _highlightResets.insert(_highlightResets.end(), transaction._highlightResets.begin(), transaction._highlightResets.end()); - _highlightRemoves.insert(_highlightRemoves.end(), transaction._highlightRemoves.begin(), transaction._highlightRemoves.end()); - _highlightQueries.insert(_highlightQueries.end(), transaction._highlightQueries.begin(), transaction._highlightQueries.end()); + copyElements(_resetItems, transaction._resetItems); + copyElements(_removedItems, transaction._removedItems); + copyElements(_updatedItems, transaction._updatedItems); + copyElements(_resetSelections, transaction._resetSelections); + copyElements(_addedTransitions, transaction._addedTransitions); + copyElements(_queriedTransitions, transaction._queriedTransitions); + copyElements(_reAppliedTransitions, transaction._reAppliedTransitions); + copyElements(_highlightResets, transaction._highlightResets); + copyElements(_highlightRemoves, transaction._highlightRemoves); + copyElements(_highlightQueries, transaction._highlightQueries); +} + +void Transaction::clear() { + _resetItems.clear(); + _removedItems.clear(); + _updatedItems.clear(); + _resetSelections.clear(); + _addedTransitions.clear(); + _queriedTransitions.clear(); + _reAppliedTransitions.clear(); + _highlightResets.clear(); + _highlightRemoves.clear(); + _highlightQueries.clear(); } @@ -102,54 +196,50 @@ bool Scene::isAllocatedID(const ItemID& id) const { /// Enqueue change batch to the scene void Scene::enqueueTransaction(const Transaction& transaction) { - _transactionQueueMutex.lock(); - _transactionQueue.push(transaction); - _transactionQueueMutex.unlock(); + std::unique_lock lock(_transactionQueueMutex); + _transactionQueue.emplace_back(transaction); } -void consolidateTransaction(TransactionQueue& queue, Transaction& singleBatch) { - while (!queue.empty()) { - const auto& transaction = queue.front(); - singleBatch.merge(transaction); - queue.pop(); - }; +void Scene::enqueueTransaction(Transaction&& transaction) { + std::unique_lock lock(_transactionQueueMutex); + _transactionQueue.emplace_back(std::move(transaction)); } uint32_t Scene::enqueueFrame() { PROFILE_RANGE(render, __FUNCTION__); - Transaction consolidatedTransaction; + TransactionQueue localTransactionQueue; { std::unique_lock lock(_transactionQueueMutex); - consolidateTransaction(_transactionQueue, consolidatedTransaction); + localTransactionQueue.swap(_transactionQueue); } - uint32_t frameNumber = 0; + Transaction consolidatedTransaction; + consolidatedTransaction.merge(std::move(localTransactionQueue)); { std::unique_lock lock(_transactionFramesMutex); _transactionFrames.push_back(consolidatedTransaction); - _transactionFrameNumber++; - frameNumber = _transactionFrameNumber; } - return frameNumber; + return ++_transactionFrameNumber; } void Scene::processTransactionQueue() { PROFILE_RANGE(render, __FUNCTION__); - TransactionFrames queuedFrames; + static TransactionFrames queuedFrames; { // capture the queued frames and clear the queue std::unique_lock lock(_transactionFramesMutex); - queuedFrames = _transactionFrames; - _transactionFrames.clear(); + queuedFrames.swap(_transactionFrames); } // go through the queue of frames and process them for (auto& frame : queuedFrames) { processTransactionFrame(frame); } + + queuedFrames.clear(); } void Scene::processTransactionFrame(const Transaction& transaction) { diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index af6204acb4..2d8bc7f4dd 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -65,9 +65,14 @@ public: void resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle()); void removeHighlightFromSelection(const std::string& selectionName); - void querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func); + void querySelectionHighlight(const std::string& selectionName, const SelectionHighlightQueryFunc& func); + void reserve(const std::vector& transactionContainer); + void merge(const std::vector& transactionContainer); + void merge(std::vector&& transactionContainer); void merge(const Transaction& transaction); + void merge(Transaction&& transaction); + void clear(); // Checkers if there is work to do when processing the transaction bool touchTransactions() const { return !_resetSelections.empty(); } @@ -107,7 +112,7 @@ protected: HighlightRemoves _highlightRemoves; HighlightQueries _highlightQueries; }; -typedef std::queue TransactionQueue; +typedef std::vector TransactionQueue; // Scene is a container for Items @@ -133,6 +138,9 @@ public: // Enqueue transaction to the scene void enqueueTransaction(const Transaction& transaction); + // Enqueue transaction to the scene + void enqueueTransaction(Transaction&& transaction); + // Enqueue end of frame transactions boundary uint32_t enqueueFrame(); @@ -187,7 +195,7 @@ protected: std::mutex _transactionFramesMutex; - using TransactionFrames = std::list; + using TransactionFrames = std::vector; TransactionFrames _transactionFrames; uint32_t _transactionFrameNumber{ 0 }; From 2a91e98813f7eb2dd0c4988fd499e7ea85c0e8f0 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 6 Dec 2017 14:25:03 -0800 Subject: [PATCH 68/70] Fix iterator names --- libraries/render/src/render/Scene.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 508cd6cefb..4b337a1046 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -125,13 +125,13 @@ void Transaction::merge(std::vector&& transactionContainer) { template void moveElements(T& target, T& source) { - target.insert(target.end(), std::make_move_iterator(source.begin()), std::make_move_iterator(source.begin())); + target.insert(target.end(), std::make_move_iterator(source.begin()), std::make_move_iterator(source.end())); source.clear(); } template void copyElements(T& target, const T& source) { - target.insert(target.end(), source.begin(), source.begin()); + target.insert(target.end(), source.begin(), source.end()); } From efd72be96fd73068820973a88fd0d78a30b5078b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 7 Dec 2017 12:05:46 +1300 Subject: [PATCH 69/70] Fix image overlay's subImage property's width and height support --- .../qml/hifi/overlays/ImageOverlay.qml | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/interface/resources/qml/hifi/overlays/ImageOverlay.qml b/interface/resources/qml/hifi/overlays/ImageOverlay.qml index 8c638679f9..6899c38e67 100644 --- a/interface/resources/qml/hifi/overlays/ImageOverlay.qml +++ b/interface/resources/qml/hifi/overlays/ImageOverlay.qml @@ -9,33 +9,30 @@ Overlay { Image { id: image - property bool scaleFix: true; - property real xOffset: 0 - property real yOffset: 0 + property bool scaleFix: true + property real xStart: 0 + property real yStart: 0 + property real xSize: 0 + property real ySize: 0 property real imageScale: 1.0 property var resizer: Timer { interval: 50 repeat: false running: false onTriggered: { - var targetAspect = root.width / root.height; - var sourceAspect = image.sourceSize.width / image.sourceSize.height; - if (sourceAspect <= targetAspect) { - if (root.width === image.sourceSize.width) { - return; - } - image.imageScale = root.width / image.sourceSize.width; - } else if (sourceAspect > targetAspect){ - if (root.height === image.sourceSize.height) { - return; - } - image.imageScale = root.height / image.sourceSize.height; + if (image.xSize === 0) { + image.xSize = image.sourceSize.width - image.xStart; } - image.sourceSize = Qt.size(image.sourceSize.width * image.imageScale, image.sourceSize.height * image.imageScale); + if (image.ySize === 0) { + image.ySize = image.sourceSize.height - image.yStart; + } + + image.anchors.leftMargin = -image.xStart * root.width / image.xSize; + image.anchors.topMargin = -image.yStart * root.height / image.ySize; + image.anchors.rightMargin = (image.xStart + image.xSize - image.sourceSize.width) * root.width / image.xSize; + image.anchors.bottomMargin = (image.yStart + image.ySize - image.sourceSize.height) * root.height / image.ySize; } } - x: -1 * xOffset * imageScale - y: -1 * yOffset * imageScale onSourceSizeChanged: { if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) { @@ -43,6 +40,8 @@ Overlay { resizer.start(); } } + + anchors.fill: parent } ColorOverlay { @@ -57,8 +56,10 @@ Overlay { var key = keys[i]; var value = subImage[key]; switch (key) { - case "x": image.xOffset = value; break; - case "y": image.yOffset = value; break; + case "x": image.xStart = value; break; + case "y": image.yStart = value; break; + case "width": image.xSize = value; break; + case "height": image.ySize = value; break; } } } From d61f0584c3f5f5ec42b5a4684a87718f17b5a0e9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 7 Dec 2017 12:26:29 +1300 Subject: [PATCH 70/70] Fix diectory.js script's icon URL --- scripts/system/directory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/directory.js b/scripts/system/directory.js index 8b9ec17f05..f84429ab95 100644 --- a/scripts/system/directory.js +++ b/scripts/system/directory.js @@ -63,7 +63,7 @@ var toolBar = (function() { y: -TOOLBAR_MARGIN_Y - toolHeight }); browseDirectoryButton = toolBar.addTool({ - imageURL: toolIconUrl + "directory-01.svg", + imageURL: toolIconUrl + "directory.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH,