From 2a05ec650b0bd1a878a620115b4c7876552fa7f0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 14:17:48 -0800 Subject: [PATCH 01/22] fix locking in AvatarHashMap/AvatarManager --- interface/src/avatar/AvatarManager.cpp | 62 ++++++++++++++----------- interface/src/avatar/AvatarManager.h | 4 +- libraries/avatars/src/AvatarHashMap.cpp | 59 ++++++++++++++--------- libraries/avatars/src/AvatarHashMap.h | 5 +- 4 files changed, 78 insertions(+), 52 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4b9bfd21a3..ae4c978877 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -119,6 +119,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceTimer perfTimer("otherAvatars"); // simulate avatars + QWriteLocker writeLock(&_hashLock); + AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { auto avatar = std::dynamic_pointer_cast(avatarIterator.value()); @@ -128,10 +130,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // DO NOT update or fade out uninitialized Avatars ++avatarIterator; } else if (avatar->shouldDie()) { - removeAvatarMotionState(avatar); - _avatarFades.push_back(avatarIterator.value()); - QWriteLocker locker(&_hashLock); + auto removedAvatar = avatarIterator.value(); avatarIterator = _avatarHash.erase(avatarIterator); + handleRemovedAvatar(removedAvatar); } else { avatar->startUpdate(); avatar->simulate(deltaTime); @@ -139,6 +140,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { ++avatarIterator; } } + + writeLock.unlock(); // simulate avatar fades simulateAvatarFades(deltaTime); @@ -173,8 +176,8 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { } // virtual -AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { - auto avatar = std::dynamic_pointer_cast(AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer)); +AvatarSharedPointer AvatarManager::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { + auto avatar = std::dynamic_pointer_cast(AvatarHashMap::newOrExistingAvatar(sessionUUID, mixerWeakPointer)); render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; if (DependencyManager::get()->shouldRenderAvatars()) { @@ -200,20 +203,28 @@ void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) { // virtual void AvatarManager::removeAvatar(const QUuid& sessionUUID) { - AvatarHash::iterator avatarIterator = _avatarHash.find(sessionUUID); - if (avatarIterator != _avatarHash.end()) { - std::shared_ptr avatar = std::dynamic_pointer_cast(avatarIterator.value()); - if (avatar != _myAvatar && avatar->isInitialized()) { - removeAvatarMotionState(avatar); - _avatarFades.push_back(avatarIterator.value()); - QWriteLocker locker(&_hashLock); - _avatarHash.erase(avatarIterator); - } + QWriteLocker locker(&_hashLock); + + auto removedAvatar = _avatarHash.take(sessionUUID); + if (removedAvatar) { + handleRemovedAvatar(removedAvatar); } } +void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar) { + qDebug() << "Removed avatar with UUID" << uuidStringWithoutCurlyBraces(removedAvatar->getSessionUUID()) + << "from AvatarManager"; + + AvatarHashMap::handleRemovedAvatar(removedAvatar); + + removeAvatarMotionState(removedAvatar); + _avatarFades.push_back(removedAvatar); +} + void AvatarManager::clearOtherAvatars() { // clear any avatars that came from an avatar-mixer + QWriteLocker locker(&_hashLock); + AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { auto avatar = std::static_pointer_cast(avatarIterator.value()); @@ -221,10 +232,10 @@ void AvatarManager::clearOtherAvatars() { // don't remove myAvatar or uninitialized avatars from the list ++avatarIterator; } else { - removeAvatarMotionState(avatar); - _avatarFades.push_back(avatarIterator.value()); - QWriteLocker locker(&_hashLock); + auto removedAvatar = avatarIterator.value(); avatarIterator = _avatarHash.erase(avatarIterator); + + handleRemovedAvatar(removedAvatar); } } _myAvatar->clearLookAtTargetAvatar(); @@ -318,9 +329,11 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents } void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { - AvatarHash::iterator avatarItr = _avatarHash.find(id); - if (avatarItr != _avatarHash.end()) { - auto avatar = std::static_pointer_cast(avatarItr.value()); + auto avatarData = findAvatar(id); + + if (avatarData) { + auto avatar = std::dynamic_pointer_cast(avatarData); + AvatarMotionState* motionState = avatar->getMotionState(); if (motionState) { motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); @@ -363,11 +376,6 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) if (sessionID == _myAvatar->getSessionUUID()) { return std::static_pointer_cast(_myAvatar); } - QReadLocker locker(&_hashLock); - auto iter = _avatarHash.find(sessionID); - if (iter != _avatarHash.end()) { - return iter.value(); - } else { - return AvatarSharedPointer(); - } + + return findAvatar(sessionID); } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index fa0593368b..71f579abc2 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -77,9 +77,11 @@ private: // virtual overrides virtual AvatarSharedPointer newSharedAvatar(); - virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); + virtual AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); void removeAvatarMotionState(AvatarSharedPointer avatar); + virtual void removeAvatar(const QUuid& sessionUUID); + virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar); QVector _avatarFades; std::shared_ptr _myAvatar; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 342f995de7..cc83ddf678 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -42,18 +42,30 @@ AvatarSharedPointer AvatarHashMap::newSharedAvatar() { return std::make_shared(); } -AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { - qCDebug(avatars) << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap."; - - AvatarSharedPointer avatar = newSharedAvatar(); - avatar->setSessionUUID(sessionUUID); - avatar->setOwningAvatarMixer(mixerWeakPointer); +AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { QWriteLocker locker(&_hashLock); - _avatarHash.insert(sessionUUID, avatar); - emit avatarAddedEvent(sessionUUID); + + auto avatar = _avatarHash.value(sessionUUID); + + if (!avatar) { + qCDebug(avatars) << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap."; + + avatar = newSharedAvatar(); + avatar->setSessionUUID(sessionUUID); + avatar->setOwningAvatarMixer(mixerWeakPointer); + + _avatarHash.insert(sessionUUID, avatar); + emit avatarAddedEvent(sessionUUID); + } + return avatar; } +AvatarSharedPointer AvatarHashMap::findAvatar(const QUuid& sessionUUID) { + QReadLocker locker(&_hashLock); + return _avatarHash.value(sessionUUID); +} + void AvatarHashMap::processAvatarDataPacket(QSharedPointer packet, SharedNodePointer sendingNode) { // enumerate over all of the avatars in this packet @@ -66,10 +78,7 @@ void AvatarHashMap::processAvatarDataPacket(QSharedPointer packet, Sha QByteArray byteArray = packet->readWithoutCopy(packet->bytesLeftToRead()); if (sessionUUID != _lastOwnerSessionUUID) { - AvatarSharedPointer avatar = _avatarHash.value(sessionUUID); - if (!avatar) { - avatar = addAvatar(sessionUUID, sendingNode); - } + auto avatar = newOrExistingAvatar(sessionUUID, sendingNode); // have the matching (or new) avatar parse the data from the packet int bytesRead = avatar->parseDataFromBuffer(byteArray); @@ -97,10 +106,8 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer packet, identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName; // mesh URL for a UUID, find avatar in our list - AvatarSharedPointer avatar = _avatarHash.value(sessionUUID); - if (!avatar) { - avatar = addAvatar(sessionUUID, sendingNode); - } + auto avatar = newOrExistingAvatar(sessionUUID, sendingNode); + if (avatar->getFaceModelURL() != faceMeshURL) { avatar->setFaceModelURL(faceMeshURL); } @@ -122,10 +129,7 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer packet, void AvatarHashMap::processAvatarBillboardPacket(QSharedPointer packet, SharedNodePointer sendingNode) { QUuid sessionUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - AvatarSharedPointer avatar = _avatarHash.value(sessionUUID); - if (!avatar) { - avatar = addAvatar(sessionUUID, sendingNode); - } + auto avatar = newOrExistingAvatar(sessionUUID, sendingNode); QByteArray billboard = packet->read(packet->bytesLeftToRead()); if (avatar->getBillboard() != billboard) { @@ -137,13 +141,22 @@ void AvatarHashMap::processKillAvatar(QSharedPointer packet, SharedNod // read the node id QUuid sessionUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); removeAvatar(sessionUUID); - } void AvatarHashMap::removeAvatar(const QUuid& sessionUUID) { QWriteLocker locker(&_hashLock); - _avatarHash.remove(sessionUUID); - emit avatarRemovedEvent(sessionUUID); + + auto removedAvatar = _avatarHash.take(sessionUUID); + + if (removedAvatar) { + handleRemovedAvatar(removedAvatar); + } +} + +void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar) { + qDebug() << "Removed avatar with UUID" << uuidStringWithoutCurlyBraces(removedAvatar->getSessionUUID()) + << "from AvatarHashMap"; + emit avatarRemovedEvent(removedAvatar->getSessionUUID()); } void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index a125784cd8..0f230a7022 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -54,8 +54,11 @@ protected: AvatarHashMap(); virtual AvatarSharedPointer newSharedAvatar(); - virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); + virtual AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); + virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID); // uses a QReadLocker on the hashLock virtual void removeAvatar(const QUuid& sessionUUID); + + virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar); AvatarHash _avatarHash; // "Case-based safety": Most access to the _avatarHash is on the same thread. Write access is protected by a write-lock. From 5140a480a15556bbd7b52ea77810fef74dd6d396 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 15:35:41 -0800 Subject: [PATCH 02/22] fix for recursive write then read lock --- interface/src/avatar/Avatar.cpp | 2 +- interface/src/avatar/AvatarManager.cpp | 32 +++++++++++--------------- interface/src/avatar/AvatarManager.h | 2 +- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b979334383..bfa32b8223 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1187,7 +1187,7 @@ void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { // virtual void Avatar::rebuildSkeletonBody() { - DependencyManager::get()->updateAvatarPhysicsShape(getSessionUUID()); + DependencyManager::get()->updateAvatarPhysicsShape(this); } glm::vec3 Avatar::getLeftPalmPosition() { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ae4c978877..af5937d3eb 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -328,25 +328,19 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents } } -void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { - auto avatarData = findAvatar(id); - - if (avatarData) { - auto avatar = std::dynamic_pointer_cast(avatarData); - - AvatarMotionState* motionState = avatar->getMotionState(); - if (motionState) { - motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); - } else { - ShapeInfo shapeInfo; - avatar->computeShapeInfo(shapeInfo); - btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); - if (shape) { - AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape); - avatar->setMotionState(motionState); - _motionStatesToAdd.insert(motionState); - _avatarMotionStates.insert(motionState); - } +void AvatarManager::updateAvatarPhysicsShape(Avatar* avatar) { + AvatarMotionState* motionState = avatar->getMotionState(); + if (motionState) { + motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); + } else { + ShapeInfo shapeInfo; + avatar->computeShapeInfo(shapeInfo); + btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); + if (shape) { + AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); + avatar->setMotionState(motionState); + _motionStatesToAdd.insert(motionState); + _avatarMotionStates.insert(motionState); } } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 71f579abc2..35e37656d8 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -63,7 +63,7 @@ public: void handleOutgoingChanges(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); - void updateAvatarPhysicsShape(const QUuid& id); + void updateAvatarPhysicsShape(Avatar* avatar); public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } From 6398a922c656316c001c3990cb71f1f450cb9776 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 15:43:29 -0800 Subject: [PATCH 03/22] leverage COW for AvatarHash --- interface/src/avatar/AvatarManager.cpp | 14 ++++++-------- interface/src/avatar/MyAvatar.cpp | 6 ++---- libraries/avatars/src/AvatarHashMap.cpp | 4 ---- libraries/avatars/src/AvatarHashMap.h | 2 +- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index af5937d3eb..4a69e9010a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -119,10 +119,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceTimer perfTimer("otherAvatars"); // simulate avatars - QWriteLocker writeLock(&_hashLock); + auto hashCopy = _avatarHash; - AvatarHash::iterator avatarIterator = _avatarHash.begin(); - while (avatarIterator != _avatarHash.end()) { + AvatarHash::iterator avatarIterator = hashCopy.begin(); + while (avatarIterator != hashCopy.end()) { auto avatar = std::dynamic_pointer_cast(avatarIterator.value()); if (avatar == _myAvatar || !avatar->isInitialized()) { @@ -130,9 +130,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // DO NOT update or fade out uninitialized Avatars ++avatarIterator; } else if (avatar->shouldDie()) { - auto removedAvatar = avatarIterator.value(); - avatarIterator = _avatarHash.erase(avatarIterator); - handleRemovedAvatar(removedAvatar); + removeAvatar(avatarIterator.key()); + ++avatarIterator; } else { avatar->startUpdate(); avatar->simulate(deltaTime); @@ -140,8 +139,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { ++avatarIterator; } } - - writeLock.unlock(); // simulate avatar fades simulateAvatarFades(deltaTime); @@ -329,6 +326,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents } void AvatarManager::updateAvatarPhysicsShape(Avatar* avatar) { + qDebug() << "Update physics state called for" << avatar; AvatarMotionState* motionState = avatar->getMotionState(); if (motionState) { motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1c151bcd3f..6521a0c891 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -982,10 +982,8 @@ void MyAvatar::updateLookAtTargetAvatar() { const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; - AvatarHash hash; - DependencyManager::get()->withAvatarHash([&] (const AvatarHash& locked) { - hash = locked; // make a shallow copy and operate on that, to minimize lock time - }); + AvatarHash hash = DependencyManager::get()->getCopy(); + foreach (const AvatarSharedPointer& avatarPointer, hash) { auto avatar = static_pointer_cast(avatarPointer); bool isCurrentTarget = avatar->getIsLookAtTarget(); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index cc83ddf678..1da9abacdd 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -22,10 +22,6 @@ AvatarHashMap::AvatarHashMap() { connect(DependencyManager::get().data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged); } -void AvatarHashMap::withAvatarHash(std::function callback) { - QReadLocker locker(&_hashLock); - callback(_avatarHash); -} bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) { QReadLocker locker(&_hashLock); foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 0f230a7022..c3c9160f12 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -31,7 +31,7 @@ class AvatarHashMap : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - void withAvatarHash(std::function); + AvatarHash getCopy() { return _avatarHash; } int size() { return _avatarHash.size(); } signals: From 40397add433f25c0dfa4baf39b711adf2dd673bc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 16:15:21 -0800 Subject: [PATCH 04/22] fix for extra adds in AvatarManager --- interface/src/avatar/AvatarManager.cpp | 24 +++++++++++------------- interface/src/avatar/AvatarManager.h | 2 +- libraries/avatars/src/AvatarHashMap.cpp | 22 ++++++++++++++-------- libraries/avatars/src/AvatarHashMap.h | 3 ++- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4a69e9010a..a64263aec2 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -123,7 +123,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { AvatarHash::iterator avatarIterator = hashCopy.begin(); while (avatarIterator != hashCopy.end()) { - auto avatar = std::dynamic_pointer_cast(avatarIterator.value()); + auto avatar = std::static_pointer_cast(avatarIterator.value()); if (avatar == _myAvatar || !avatar->isInitialized()) { // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. @@ -169,19 +169,21 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return AvatarSharedPointer(std::make_shared(std::make_shared())); + return std::make_shared(std::make_shared()); } -// virtual -AvatarSharedPointer AvatarManager::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { - auto avatar = std::dynamic_pointer_cast(AvatarHashMap::newOrExistingAvatar(sessionUUID, mixerWeakPointer)); +AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { + auto newAvatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); + auto rawRenderableAvatar = std::static_pointer_cast(newAvatar); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; if (DependencyManager::get()->shouldRenderAvatars()) { - avatar->addToScene(avatar, scene, pendingChanges); + rawRenderableAvatar->addToScene(rawRenderableAvatar, scene, pendingChanges); } scene->enqueuePendingChanges(pendingChanges); - return avatar; + + return newAvatar; } // protected @@ -209,9 +211,6 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) { } void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar) { - qDebug() << "Removed avatar with UUID" << uuidStringWithoutCurlyBraces(removedAvatar->getSessionUUID()) - << "from AvatarManager"; - AvatarHashMap::handleRemovedAvatar(removedAvatar); removeAvatarMotionState(removedAvatar); @@ -326,7 +325,6 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents } void AvatarManager::updateAvatarPhysicsShape(Avatar* avatar) { - qDebug() << "Update physics state called for" << avatar; AvatarMotionState* motionState = avatar->getMotionState(); if (motionState) { motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); @@ -346,7 +344,7 @@ void AvatarManager::updateAvatarPhysicsShape(Avatar* avatar) { void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) { if (DependencyManager::get()->shouldRenderAvatars()) { for (auto avatarData : _avatarHash) { - auto avatar = std::dynamic_pointer_cast(avatarData); + auto avatar = std::static_pointer_cast(avatarData); render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; avatar->addToScene(avatar, scene, pendingChanges); @@ -354,7 +352,7 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) { } } else { for (auto avatarData : _avatarHash) { - auto avatar = std::dynamic_pointer_cast(avatarData); + auto avatar = std::static_pointer_cast(avatarData); render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; avatar->removeFromScene(avatar, scene, pendingChanges); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 35e37656d8..f911dacc4d 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -77,7 +77,7 @@ private: // virtual overrides virtual AvatarSharedPointer newSharedAvatar(); - virtual AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); + virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); void removeAvatarMotionState(AvatarSharedPointer avatar); virtual void removeAvatar(const QUuid& sessionUUID); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 1da9abacdd..8f2ea82373 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -38,20 +38,26 @@ AvatarSharedPointer AvatarHashMap::newSharedAvatar() { return std::make_shared(); } +AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { + qCDebug(avatars) << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap."; + + auto avatar = newSharedAvatar(); + avatar->setSessionUUID(sessionUUID); + avatar->setOwningAvatarMixer(mixerWeakPointer); + + _avatarHash.insert(sessionUUID, avatar); + emit avatarAddedEvent(sessionUUID); + + return avatar; +} + AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { QWriteLocker locker(&_hashLock); auto avatar = _avatarHash.value(sessionUUID); if (!avatar) { - qCDebug(avatars) << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap."; - - avatar = newSharedAvatar(); - avatar->setSessionUUID(sessionUUID); - avatar->setOwningAvatarMixer(mixerWeakPointer); - - _avatarHash.insert(sessionUUID, avatar); - emit avatarAddedEvent(sessionUUID); + avatar = addAvatar(sessionUUID, mixerWeakPointer); } return avatar; diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index c3c9160f12..c2fecf5bd9 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -54,7 +54,8 @@ protected: AvatarHashMap(); virtual AvatarSharedPointer newSharedAvatar(); - virtual AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); + virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); + AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID); // uses a QReadLocker on the hashLock virtual void removeAvatar(const QUuid& sessionUUID); From 430cb7876dd99cfb7832620b2da65e40f0bf004f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 16:22:56 -0800 Subject: [PATCH 05/22] remove a couple of read lockers for AvatarHashMap --- interface/src/avatar/AvatarManager.cpp | 1 - libraries/avatars/src/AvatarHashMap.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index a64263aec2..bd45561b38 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -256,7 +256,6 @@ QVector AvatarManager::getLocalLights() const { } QVector AvatarManager::getAvatarIdentifiers() { - QReadLocker locker(&_hashLock); return _avatarHash.keys().toVector(); } AvatarData* AvatarManager::getAvatar(QUuid avatarID) { diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 8f2ea82373..035d29f344 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -23,8 +23,8 @@ AvatarHashMap::AvatarHashMap() { } bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) { - QReadLocker locker(&_hashLock); - foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) { + auto hashCopy = _avatarHash; + foreach(const AvatarSharedPointer& sharedAvatar, hashCopy) { glm::vec3 avatarPosition = sharedAvatar->getPosition(); float distance = glm::distance(avatarPosition, position); if (distance < range) { From 22f5d4df6dece4e1c9f3c42d1cdced28e4b2dfa9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 16:25:33 -0800 Subject: [PATCH 06/22] change signature of copy return --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/avatars/src/AvatarHashMap.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6521a0c891..a2102bd010 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -982,7 +982,7 @@ void MyAvatar::updateLookAtTargetAvatar() { const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; - AvatarHash hash = DependencyManager::get()->getCopy(); + AvatarHash hash = DependencyManager::get()->getHashCopy(); foreach (const AvatarSharedPointer& avatarPointer, hash) { auto avatar = static_pointer_cast(avatarPointer); diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index c2fecf5bd9..7795072ec2 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -31,7 +31,7 @@ class AvatarHashMap : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - AvatarHash getCopy() { return _avatarHash; } + AvatarHash getHashCopy() { return _avatarHash; } int size() { return _avatarHash.size(); } signals: From 7f8274f58f928d92ed7813716c5696819dd3f939 Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Thu, 19 Nov 2015 18:08:12 -0800 Subject: [PATCH 07/22] removed omnitool removed omnitool --- examples/defaultScripts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 6efe6edef3..f06af70fb3 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -19,4 +19,3 @@ Script.load("controllers/handControllerGrab.js"); Script.load("grab.js"); Script.load("directory.js"); Script.load("dialTone.js"); -Script.load("libraries/omniTool.js"); From 5c6cd9b06c2f653830f7bc51f0d4386e84b0f66e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 19 Nov 2015 18:53:12 -0800 Subject: [PATCH 08/22] fix thread safety and crash with no asset server --- libraries/networking/src/AssetClient.cpp | 31 +++++++++++++++--------- libraries/networking/src/AssetClient.h | 1 + 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 75b4ca04e8..4dc08962c5 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -369,16 +369,18 @@ void AssetClient::handleNodeKilled(SharedNodePointer node) { void AssetScriptingInterface::uploadData(QString data, QString extension, QScriptValue callback) { QByteArray dataByteArray = data.toUtf8(); auto upload = DependencyManager::get()->createUpload(dataByteArray, extension); - QObject::connect(upload, &AssetUpload::finished, this, [callback, extension](AssetUpload* upload, const QString& hash) mutable { - if (callback.isFunction()) { - QString url = "atp://" + hash + "." + extension; - QScriptValueList args { url }; - callback.call(QScriptValue(), args); - } - }); + if (upload) { + QObject::connect(upload, &AssetUpload::finished, this, [callback, extension](AssetUpload* upload, const QString& hash) mutable { + if (callback.isFunction()) { + QString url = "atp://" + hash + "." + extension; + QScriptValueList args { url }; + callback.call(QScriptValue(), args); + } + }); - // start the upload now - upload->start(); + // start the upload now + upload->start(); + } } void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) { @@ -405,7 +407,10 @@ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callb return; } - _pendingRequests << assetRequest; + { + QWriteLocker locker(&_lock); + _pendingRequests << assetRequest; + } connect(assetRequest, &AssetRequest::finished, [this, callback](AssetRequest* request) mutable { Q_ASSERT(request->getState() == AssetRequest::Finished); @@ -419,7 +424,11 @@ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callb } request->deleteLater(); - _pendingRequests.remove(request); + + { + QWriteLocker locker(&_lock); + _pendingRequests.remove(request); + } }); assetRequest->start(); diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index f1bb210614..a194db225b 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -77,6 +77,7 @@ public: Q_INVOKABLE void uploadData(QString data, QString extension, QScriptValue callback); Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete); protected: + mutable QReadWriteLock _lock; QSet _pendingRequests; }; From fb90ffec5c6fdebb9997c507819e65be9455c766 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Tue, 17 Nov 2015 19:42:18 -0800 Subject: [PATCH 09/22] Migration of AC playback --- examples/acScripts/playbackAgents.js | 141 +++++++++++++++ examples/acScripts/playbackMaster.js | 259 +++++++++++++++++++++++++++ 2 files changed, 400 insertions(+) create mode 100644 examples/acScripts/playbackAgents.js create mode 100644 examples/acScripts/playbackMaster.js diff --git a/examples/acScripts/playbackAgents.js b/examples/acScripts/playbackAgents.js new file mode 100644 index 0000000000..9912e00010 --- /dev/null +++ b/examples/acScripts/playbackAgents.js @@ -0,0 +1,141 @@ +// +// playbackAgents.js +// acScripts +// +// Created by Edgar Pironti on 11/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Set the following variables to the values needed +var channel = "PlaybackChannel1"; +var clip_url = null; +var playFromCurrentLocation = true; +var useDisplayName = true; +var useAttachments = true; +var useAvatarModel = true; + +// ID of the agent. Two agents can't have the same ID. +var id = 0; + +// Set position/orientation/scale here if playFromCurrentLocation is true +Avatar.position = { x:0, y: 0, z: 0 }; +Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0); +Avatar.scale = 1.0; + +var totalTime = 0; +var subscribed = false; +var WAIT_FOR_AUDIO_MIXER = 1; + +// Script. DO NOT MODIFY BEYOND THIS LINE. +var DO_NOTHING = 0; +var PLAY = 1; +var PLAY_LOOP = 2; +var STOP = 3; +var SHOW = 4; +var HIDE = 5; +var LOAD = 6; + +Recording.setPlayFromCurrentLocation(playFromCurrentLocation); +Recording.setPlayerUseDisplayName(useDisplayName); +Recording.setPlayerUseAttachments(useAttachments); +Recording.setPlayerUseHeadModel(false); +Recording.setPlayerUseSkeletonModel(useAvatarModel); + +function getAction(channel, message, senderID) { + + if(subscribed) { + var command = JSON.parse(message); + print("I'm the agent " + id + " and I received this: ID: " + command.id_key + " Action: " + command.action_key + " URL: " + command.clip_url_key); + + if (command.id_key == id || command.id_key == -1) { + if (command.action_key === 6) + clip_url = command.clip_url_key; + + // If the id is -1 (broadcast) and the action is 6, in the url should be the performance file + // with all the clips recorded in a session (not just the single clip url). + // It has to be computed here in order to retrieve the url for the single agent. + // Checking the id we can assign the correct url to the correct agent. + + action = command.action_key; + } else { + action = DO_NOTHING; + } + + switch(action) { + case PLAY: + print("Play"); + if (!Agent.isAvatar) { + Agent.isAvatar = true; + } + if (!Recording.isPlaying()) { + Recording.startPlaying(); + } + Recording.setPlayerLoop(false); + break; + case PLAY_LOOP: + print("Play loop"); + if (!Agent.isAvatar) { + Agent.isAvatar = true; + } + if (!Recording.isPlaying()) { + Recording.startPlaying(); + } + Recording.setPlayerLoop(true); + break; + case STOP: + print("Stop"); + if (Recording.isPlaying()) { + Recording.stopPlaying(); + } + break; + case SHOW: + print("Show"); + if (!Agent.isAvatar) { + Agent.isAvatar = true; + } + break; + case HIDE: + print("Hide"); + if (Recording.isPlaying()) { + Recording.stopPlaying(); + } + Agent.isAvatar = false; + break; + case LOAD: + print("Load"); + if(clip_url !== null) { + Recording.loadRecording(clip_url); + } + break; + case DO_NOTHING: + break; + default: + print("Unknown action: " + action); + break; + + } + + if (Recording.isPlaying()) { + Recording.play(); + } + } +} + + +function update(deltaTime) { + + totalTime += deltaTime; + + if (totalTime > WAIT_FOR_AUDIO_MIXER && !subscribed) { + Messages.subscribe(channel); + subscribed = true; + print("I'm the agent and I am ready to receive!") + } +} + +Script.update.connect(update); +Messages.messageReceived.connect(getAction); + diff --git a/examples/acScripts/playbackMaster.js b/examples/acScripts/playbackMaster.js new file mode 100644 index 0000000000..f1f1b44768 --- /dev/null +++ b/examples/acScripts/playbackMaster.js @@ -0,0 +1,259 @@ +// +// playbackMaster.js +// acScripts +// +// Created by Edgar Pironti on 11/17/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + + +var ac_number = 1; // This is the default number of ACs. Their ID need to be unique and between 0 (included) and ac_number (excluded) +var names = new Array(); // It is possible to specify the name of the ACs in this array. ACs names ordered by IDs (Default name is "ACx", x = ID + 1)) +var channel = "PlaybackChannel1"; +var subscribed = false; +var clip_url = null; +var input_text = null; + +// Script. DO NOT MODIFY BEYOND THIS LINE. +Script.include("../libraries/toolBars.js"); + +var DO_NOTHING = 0; +var PLAY = 1; +var PLAY_LOOP = 2; +var STOP = 3; +var SHOW = 4; +var HIDE = 5; +var LOAD = 6; + +var windowDimensions = Controller.getViewportDimensions(); +var TOOL_ICON_URL = HIFI_PUBLIC_BUCKET + "images/tools/"; +var ALPHA_ON = 1.0; +var ALPHA_OFF = 0.7; +var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 }; +var COLOR_MASTER = { red: 0, green: 0, blue: 0 }; +var TEXT_HEIGHT = 12; +var TEXT_MARGIN = 3; + +var toolBars = new Array(); +var nameOverlays = new Array(); +var onOffIcon = new Array(); +var playIcon = new Array(); +var playLoopIcon = new Array(); +var stopIcon = new Array(); +var loadIcon = new Array(); + +setupPlayback(); + +function setupPlayback() { + ac_number = Window.prompt("Insert number of agents: ","1"); + if (ac_number === "" || ac_number === null) + ac_number = 1; + Messages.subscribe(channel); + subscribed = true; + setupToolBars(); +} + +function setupToolBars() { + if (toolBars.length > 0) { + print("Multiple calls to Recorder.js:setupToolBars()"); + return; + } + Tool.IMAGE_HEIGHT /= 2; + Tool.IMAGE_WIDTH /= 2; + + for (i = 0; i <= ac_number; i++) { + toolBars.push(new ToolBar(0, 0, ToolBar.HORIZONTAL)); + toolBars[i].setBack((i == ac_number) ? COLOR_MASTER : COLOR_TOOL_BAR, ALPHA_OFF); + + onOffIcon.push(toolBars[i].addTool({ + imageURL: TOOL_ICON_URL + "ac-on-off.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + x: 0, y: 0, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_ON, + visible: true + }, true, true)); + + playIcon[i] = toolBars[i].addTool({ + imageURL: TOOL_ICON_URL + "play.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_OFF, + visible: true + }, false); + + var playLoopWidthFactor = 1.65; + playLoopIcon[i] = toolBars[i].addTool({ + imageURL: TOOL_ICON_URL + "play-and-loop.svg", + subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: playLoopWidthFactor * Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_OFF, + visible: true + }, false); + + stopIcon[i] = toolBars[i].addTool({ + imageURL: TOOL_ICON_URL + "recording-stop.svg", + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_OFF, + visible: true + }, false); + + loadIcon[i] = toolBars[i].addTool({ + imageURL: TOOL_ICON_URL + "recording-upload.svg", + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_OFF, + visible: true + }, false); + + nameOverlays.push(Overlays.addOverlay("text", { + backgroundColor: { red: 0, green: 0, blue: 0 }, + font: { size: TEXT_HEIGHT }, + text: (i == ac_number) ? "Master" : i + ". " + + ((i < names.length) ? names[i] : + "AC" + i), + x: 0, y: 0, + width: toolBars[i].width + ToolBar.SPACING, + height: TEXT_HEIGHT + TEXT_MARGIN, + leftMargin: TEXT_MARGIN, + topMargin: TEXT_MARGIN, + alpha: ALPHA_OFF, + backgroundAlpha: ALPHA_OFF, + visible: true + })); + } +} + +function sendCommand(id, action) { + + if (action === SHOW) { + toolBars[id].selectTool(onOffIcon[id], false); + toolBars[id].setAlpha(ALPHA_ON, playIcon[id]); + toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]); + toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]); + toolBars[id].setAlpha(ALPHA_ON, loadIcon[id]); + } else if (action === HIDE) { + toolBars[id].selectTool(onOffIcon[id], true); + toolBars[id].setAlpha(ALPHA_OFF, playIcon[id]); + toolBars[id].setAlpha(ALPHA_OFF, playLoopIcon[id]); + toolBars[id].setAlpha(ALPHA_OFF, stopIcon[id]); + toolBars[id].setAlpha(ALPHA_OFF, loadIcon[id]); + } else if (toolBars[id].toolSelected(onOffIcon[id])) { + return; + } + + if (id == (toolBars.length - 1)) + id = -1; // Master command becomes broadcast. + + var message = { + id_key: id, + action_key: action, + clip_url_key: clip_url + }; + + if(subscribed){ + Messages.sendMessage(channel, JSON.stringify(message)); + print("Message sent!"); + } +} + +function mousePressEvent(event) { + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + + // Check master control + var i = toolBars.length - 1; + if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + if (toolBars[i].toolSelected(onOffIcon[i])) { + sendCommand(i, SHOW); + } else { + sendCommand(i, HIDE); + } + } else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, PLAY); + } else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, PLAY_LOOP); + } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, STOP); + } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + input_text = Window.prompt("Insert the url of the clip: ",""); + if (!(input_text === "" || input_text === null)) { + clip_url = input_text; + sendCommand(i, LOAD); + } + } else { + // Check individual controls + for (i = 0; i < ac_number; i++) { + if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + if (toolBars[i].toolSelected(onOffIcon[i], false)) { + sendCommand(i, SHOW); + } else { + sendCommand(i, HIDE); + } + } else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, PLAY); + } else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, PLAY_LOOP); + } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, STOP); + } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + input_text = Window.prompt("Insert the url of the clip: ",""); + if (!(input_text === "" || input_text === null)) { + clip_url = input_text; + sendCommand(i, LOAD); + } + } else { + + } + } + } +} + +function moveUI() { + var textSize = TEXT_HEIGHT + 2 * TEXT_MARGIN; + var relative = { x: 70, y: 75 + (ac_number) * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize) }; + + for (i = 0; i <= ac_number; i++) { + toolBars[i].move(relative.x, + windowDimensions.y - relative.y + + i * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize)); + + Overlays.editOverlay(nameOverlays[i], { + x: toolBars[i].x - ToolBar.SPACING, + y: toolBars[i].y - textSize + }); + } +} + +function update() { + var newDimensions = Controller.getViewportDimensions(); + if (windowDimensions.x != newDimensions.x || + windowDimensions.y != newDimensions.y) { + windowDimensions = newDimensions; + moveUI(); + } +} + +function scriptEnding() { + for (i = 0; i <= ac_number; i++) { + toolBars[i].cleanup(); + Overlays.deleteOverlay(nameOverlays[i]); + } + + if(subscribed) + Messages.unsubscribe(channel); +} + +Controller.mousePressEvent.connect(mousePressEvent); +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + +moveUI(); \ No newline at end of file From d75209cb3783c071c18d097db199345612e86ea2 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Wed, 18 Nov 2015 12:30:39 -0800 Subject: [PATCH 10/22] Fixes to clip_url overwriting --- examples/acScripts/playbackAgents.js | 2 ++ examples/acScripts/playbackMaster.js | 1 + 2 files changed, 3 insertions(+) diff --git a/examples/acScripts/playbackAgents.js b/examples/acScripts/playbackAgents.js index 9912e00010..93b563d497 100644 --- a/examples/acScripts/playbackAgents.js +++ b/examples/acScripts/playbackAgents.js @@ -60,6 +60,8 @@ function getAction(channel, message, senderID) { // Checking the id we can assign the correct url to the correct agent. action = command.action_key; + print("That command was for me!"); + print("My clip is: " + clip_url); } else { action = DO_NOTHING; } diff --git a/examples/acScripts/playbackMaster.js b/examples/acScripts/playbackMaster.js index f1f1b44768..e3448c0256 100644 --- a/examples/acScripts/playbackMaster.js +++ b/examples/acScripts/playbackMaster.js @@ -163,6 +163,7 @@ function sendCommand(id, action) { if(subscribed){ Messages.sendMessage(channel, JSON.stringify(message)); print("Message sent!"); + clip_url = null; } } From e267926725fa9e03f6b398398eb3212946d3588b Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Thu, 19 Nov 2015 20:03:27 -0800 Subject: [PATCH 11/22] JSON computing --- examples/acScripts/playbackAgents.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/examples/acScripts/playbackAgents.js b/examples/acScripts/playbackAgents.js index 93b563d497..c50c727277 100644 --- a/examples/acScripts/playbackAgents.js +++ b/examples/acScripts/playbackAgents.js @@ -51,13 +51,25 @@ function getAction(channel, message, senderID) { print("I'm the agent " + id + " and I received this: ID: " + command.id_key + " Action: " + command.action_key + " URL: " + command.clip_url_key); if (command.id_key == id || command.id_key == -1) { - if (command.action_key === 6) + if (command.action_key === 6) { clip_url = command.clip_url_key; - - // If the id is -1 (broadcast) and the action is 6, in the url should be the performance file - // with all the clips recorded in a session (not just the single clip url). - // It has to be computed here in order to retrieve the url for the single agent. - // Checking the id we can assign the correct url to the correct agent. + + // If the id is -1 (broadcast) and the action is 6, in the url should be the performance file + // with all the clips recorded in a session (not just the single clip url). + // It has to be computed here in order to retrieve the url for the single agent. + // Checking the id we can assign the correct url to the correct agent. + + if (command.id_key == -1) { + Assets.downloadData(clip_url, function (data) { + var myJSONObject = JSON.parse(data); + var hash = myJSONObject.results[id].hashATP; + }); + + Assets.downloadData(hash, function (data) { + clip_url = JSON.parse(data); + }); + } + } action = command.action_key; print("That command was for me!"); From 5adcbcaf5f91228fcd6881f143c3a39e2f389752 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 19 Nov 2015 20:23:50 -0800 Subject: [PATCH 12/22] Support writing a clip to a qbytearray --- libraries/recording/src/recording/Clip.cpp | 82 ++++++++++++++++++- libraries/recording/src/recording/Clip.h | 6 ++ .../recording/src/recording/impl/FileClip.cpp | 64 +-------------- .../src/recording/impl/PointerClip.cpp | 7 +- .../src/recording/impl/PointerClip.h | 3 - 5 files changed, 90 insertions(+), 72 deletions(-) diff --git a/libraries/recording/src/recording/Clip.cpp b/libraries/recording/src/recording/Clip.cpp index abe66ccb2e..1451724f23 100644 --- a/libraries/recording/src/recording/Clip.cpp +++ b/libraries/recording/src/recording/Clip.cpp @@ -13,6 +13,11 @@ #include "impl/FileClip.h" #include "impl/BufferClip.h" +#include +#include +#include +#include + using namespace recording; Clip::Pointer Clip::fromFile(const QString& filePath) { @@ -27,6 +32,15 @@ void Clip::toFile(const QString& filePath, const Clip::ConstPointer& clip) { FileClip::write(filePath, clip->duplicate()); } +QByteArray Clip::toBuffer(const Clip::ConstPointer& clip) { + QBuffer buffer; + if (buffer.open(QFile::Truncate | QFile::WriteOnly)) { + clip->duplicate()->write(buffer); + buffer.close(); + } + return buffer.data(); +} + Clip::Pointer Clip::newClip() { return std::make_shared(); } @@ -37,4 +51,70 @@ void Clip::seek(float offset) { float Clip::position() const { return Frame::frameTimeToSeconds(positionFrameTime()); -}; +} + +// FIXME move to frame? +bool writeFrame(QIODevice& output, const Frame& frame, bool compressed = true) { + if (frame.type == Frame::TYPE_INVALID) { + qWarning() << "Attempting to write invalid frame"; + return true; + } + + auto written = output.write((char*)&(frame.type), sizeof(FrameType)); + if (written != sizeof(FrameType)) { + return false; + } + //qDebug() << "Writing frame with time offset " << frame.timeOffset; + written = output.write((char*)&(frame.timeOffset), sizeof(Frame::Time)); + if (written != sizeof(Frame::Time)) { + return false; + } + QByteArray frameData = frame.data; + if (compressed) { + frameData = qCompress(frameData); + } + + uint16_t dataSize = frameData.size(); + written = output.write((char*)&dataSize, sizeof(FrameSize)); + if (written != sizeof(uint16_t)) { + return false; + } + + if (dataSize != 0) { + written = output.write(frameData); + if (written != dataSize) { + return false; + } + } + return true; +} + +const QString Clip::FRAME_TYPE_MAP = QStringLiteral("frameTypes"); +const QString Clip::FRAME_COMREPSSION_FLAG = QStringLiteral("compressed"); + +bool Clip::write(QIODevice& output) { + auto frameTypes = Frame::getFrameTypes(); + QJsonObject frameTypeObj; + for (const auto& frameTypeName : frameTypes.keys()) { + frameTypeObj[frameTypeName] = frameTypes[frameTypeName]; + } + + QJsonObject rootObject; + rootObject.insert(FRAME_TYPE_MAP, frameTypeObj); + // Always mark new files as compressed + rootObject.insert(FRAME_COMREPSSION_FLAG, true); + QByteArray headerFrameData = QJsonDocument(rootObject).toBinaryData(); + // Never compress the header frame + if (!writeFrame(output, Frame({ Frame::TYPE_HEADER, 0, headerFrameData }), false)) { + return false; + } + + seek(0); + + for (auto frame = nextFrame(); frame; frame = nextFrame()) { + if (!writeFrame(output, *frame)) { + return false; + } + } + return true; +} diff --git a/libraries/recording/src/recording/Clip.h b/libraries/recording/src/recording/Clip.h index 722fadf0b2..1fdad39da7 100644 --- a/libraries/recording/src/recording/Clip.h +++ b/libraries/recording/src/recording/Clip.h @@ -47,10 +47,16 @@ public: virtual void skipFrame() = 0; virtual void addFrame(FrameConstPointer) = 0; + bool write(QIODevice& output); + static Pointer fromFile(const QString& filePath); static void toFile(const QString& filePath, const ConstPointer& clip); + static QByteArray toBuffer(const ConstPointer& clip); static Pointer newClip(); + static const QString FRAME_TYPE_MAP; + static const QString FRAME_COMREPSSION_FLAG; + protected: friend class WrapperClip; using Mutex = std::recursive_mutex; diff --git a/libraries/recording/src/recording/impl/FileClip.cpp b/libraries/recording/src/recording/impl/FileClip.cpp index ce2705a76c..b153f5aa4a 100644 --- a/libraries/recording/src/recording/impl/FileClip.cpp +++ b/libraries/recording/src/recording/impl/FileClip.cpp @@ -11,8 +11,6 @@ #include #include -#include -#include #include @@ -40,41 +38,7 @@ QString FileClip::getName() const { return _file.fileName(); } -// FIXME move to frame? -bool writeFrame(QIODevice& output, const Frame& frame, bool compressed = true) { - if (frame.type == Frame::TYPE_INVALID) { - qWarning() << "Attempting to write invalid frame"; - return true; - } - auto written = output.write((char*)&(frame.type), sizeof(FrameType)); - if (written != sizeof(FrameType)) { - return false; - } - //qDebug() << "Writing frame with time offset " << frame.timeOffset; - written = output.write((char*)&(frame.timeOffset), sizeof(Frame::Time)); - if (written != sizeof(Frame::Time)) { - return false; - } - QByteArray frameData = frame.data; - if (compressed) { - frameData = qCompress(frameData); - } - - uint16_t dataSize = frameData.size(); - written = output.write((char*)&dataSize, sizeof(FrameSize)); - if (written != sizeof(uint16_t)) { - return false; - } - - if (dataSize != 0) { - written = output.write(frameData); - if (written != dataSize) { - return false; - } - } - return true; -} bool FileClip::write(const QString& fileName, Clip::Pointer clip) { // FIXME need to move this to a different thread @@ -90,33 +54,7 @@ bool FileClip::write(const QString& fileName, Clip::Pointer clip) { } Finally closer([&] { outputFile.close(); }); - { - auto frameTypes = Frame::getFrameTypes(); - QJsonObject frameTypeObj; - for (const auto& frameTypeName : frameTypes.keys()) { - frameTypeObj[frameTypeName] = frameTypes[frameTypeName]; - } - - QJsonObject rootObject; - rootObject.insert(FRAME_TYPE_MAP, frameTypeObj); - // Always mark new files as compressed - rootObject.insert(FRAME_COMREPSSION_FLAG, true); - QByteArray headerFrameData = QJsonDocument(rootObject).toBinaryData(); - // Never compress the header frame - if (!writeFrame(outputFile, Frame({ Frame::TYPE_HEADER, 0, headerFrameData }), false)) { - return false; - } - - } - - clip->seek(0); - for (auto frame = clip->nextFrame(); frame; frame = clip->nextFrame()) { - if (!writeFrame(outputFile, *frame)) { - return false; - } - } - outputFile.close(); - return true; + return clip->write(outputFile); } FileClip::~FileClip() { diff --git a/libraries/recording/src/recording/impl/PointerClip.cpp b/libraries/recording/src/recording/impl/PointerClip.cpp index 48132c066d..6f74391b4b 100644 --- a/libraries/recording/src/recording/impl/PointerClip.cpp +++ b/libraries/recording/src/recording/impl/PointerClip.cpp @@ -23,16 +23,13 @@ using namespace recording; -const QString PointerClip::FRAME_TYPE_MAP = QStringLiteral("frameTypes"); -const QString PointerClip::FRAME_COMREPSSION_FLAG = QStringLiteral("compressed"); - using FrameTranslationMap = QMap; FrameTranslationMap parseTranslationMap(const QJsonDocument& doc) { FrameTranslationMap results; auto headerObj = doc.object(); - if (headerObj.contains(PointerClip::FRAME_TYPE_MAP)) { - auto frameTypeObj = headerObj[PointerClip::FRAME_TYPE_MAP].toObject(); + if (headerObj.contains(Clip::FRAME_TYPE_MAP)) { + auto frameTypeObj = headerObj[Clip::FRAME_TYPE_MAP].toObject(); auto currentFrameTypes = Frame::getFrameTypes(); for (auto frameTypeName : frameTypeObj.keys()) { qDebug() << frameTypeName; diff --git a/libraries/recording/src/recording/impl/PointerClip.h b/libraries/recording/src/recording/impl/PointerClip.h index 5a7a3499fe..f5c0dd6bc4 100644 --- a/libraries/recording/src/recording/impl/PointerClip.h +++ b/libraries/recording/src/recording/impl/PointerClip.h @@ -44,9 +44,6 @@ public: // FIXME move to frame? static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(Frame::Time) + sizeof(FrameSize); - static const QString FRAME_TYPE_MAP; - static const QString FRAME_COMREPSSION_FLAG; - protected: void reset(); virtual FrameConstPointer readFrame(size_t index) const override; From abf169ebd9fe7328883efc07a6af6dad7e599ced Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 20 Nov 2015 09:03:21 -0800 Subject: [PATCH 13/22] fix senderID in messages, dry up code --- .../src/messages/MessagesMixer.cpp | 43 +++------------ libraries/networking/src/MessagesClient.cpp | 55 ++++++++++++------- libraries/networking/src/MessagesClient.h | 12 ++-- 3 files changed, 51 insertions(+), 59 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index d3662f3fb5..a811631617 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -24,9 +25,7 @@ const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer"; MessagesMixer::MessagesMixer(NLPacket& packet) : ThreadedAssignment(packet) { - // make sure we hear about node kills so we can tell the other nodes connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled); - auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessages"); packetReceiver.registerMessageListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe"); @@ -45,42 +44,20 @@ void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { void MessagesMixer::handleMessages(QSharedPointer packetList, SharedNodePointer senderNode) { Q_ASSERT(packetList->getType() == PacketType::MessagesData); - QByteArray packetData = packetList->getMessage(); - QBuffer packet{ &packetData }; - packet.open(QIODevice::ReadOnly); + QString channel, message; + QUuid senderID; + MessagesClient::decodeMessagesPacket(packetList, channel, message, senderID); + Q_ASSERT(senderNode->getUUID() == senderID); // NOTE: do we want to reject messages that come from bogus senders - quint16 channelLength; - packet.read(reinterpret_cast(&channelLength), sizeof(channelLength)); - auto channelData = packet.read(channelLength); - QString channel = QString::fromUtf8(channelData); - - quint16 messageLength; - packet.read(reinterpret_cast(&messageLength), sizeof(messageLength)); - auto messageData = packet.read(messageLength); - QString message = QString::fromUtf8(messageData); - auto nodeList = DependencyManager::get(); nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { - return node->getType() == NodeType::Agent && node->getActiveSocket() && _channelSubscribers[channel].contains(node->getUUID()); }, [&](const SharedNodePointer& node) { - - auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); - - auto channelUtf8 = channel.toUtf8(); - quint16 channelLength = channelUtf8.length(); - packetList->writePrimitive(channelLength); - packetList->write(channelUtf8); - - auto messageUtf8 = message.toUtf8(); - quint16 messageLength = messageUtf8.length(); - packetList->writePrimitive(messageLength); - packetList->write(messageUtf8); - + auto packetList = MessagesClient::encodeMessagesPacket(channel, message, senderID); nodeList->sendPacketList(std::move(packetList), *node); }); } @@ -107,15 +84,13 @@ void MessagesMixer::sendStatsPacket() { QJsonObject statsObject; QJsonObject messagesObject; auto nodeList = DependencyManager::get(); + // add stats for each listerner nodeList->eachNode([&](const SharedNodePointer& node) { QJsonObject messagesStats; - - // add the key to ask the domain-server for a username replacement, if it has it messagesStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); messagesStats["outbound_kbps"] = node->getOutboundBandwidth(); messagesStats["inbound_kbps"] = node->getInboundBandwidth(); - messagesObject[uuidStringWithoutCurlyBraces(node->getUUID())] = messagesStats; }); @@ -125,10 +100,6 @@ void MessagesMixer::sendStatsPacket() { void MessagesMixer::run() { ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); - auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - - // The messages-mixer currently does currently have any domain settings. If it did, they would be - // synchronously grabbed here. } diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 2e8b3bbb09..fc1729b4c5 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -36,7 +36,7 @@ void MessagesClient::init() { } } -void MessagesClient::handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode) { +void MessagesClient::decodeMessagesPacket(QSharedPointer packetList, QString& channel, QString& message, QUuid& senderID) { QByteArray packetData = packetList->getMessage(); QBuffer packet{ &packetData }; packet.open(QIODevice::ReadOnly); @@ -44,38 +44,55 @@ void MessagesClient::handleMessagesPacket(QSharedPointer packetLis quint16 channelLength; packet.read(reinterpret_cast(&channelLength), sizeof(channelLength)); auto channelData = packet.read(channelLength); - QString channel = QString::fromUtf8(channelData); + channel = QString::fromUtf8(channelData); quint16 messageLength; packet.read(reinterpret_cast(&messageLength), sizeof(messageLength)); auto messageData = packet.read(messageLength); - QString message = QString::fromUtf8(messageData); + message = QString::fromUtf8(messageData); - emit messageReceived(channel, message, senderNode->getUUID()); + QByteArray bytesSenderID = packet.read(NUM_BYTES_RFC4122_UUID); + senderID = QUuid::fromRfc4122(bytesSenderID); } -void MessagesClient::sendMessage(const QString& channel, const QString& message) { +std::unique_ptr MessagesClient::encodeMessagesPacket(QString channel, QString message, QUuid senderID) { + auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); + + auto channelUtf8 = channel.toUtf8(); + quint16 channelLength = channelUtf8.length(); + packetList->writePrimitive(channelLength); + packetList->write(channelUtf8); + + auto messageUtf8 = message.toUtf8(); + quint16 messageLength = messageUtf8.length(); + packetList->writePrimitive(messageLength); + packetList->write(messageUtf8); + + packetList->write(senderID.toRfc4122()); + + return packetList; +} + + +void MessagesClient::handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode) { + QString channel, message; + QUuid senderID; + decodeMessagesPacket(packetList, channel, message, senderID); + emit messageReceived(channel, message, senderID); +} + +void MessagesClient::sendMessage(QString channel, QString message) { auto nodeList = DependencyManager::get(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); if (messagesMixer) { - auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); - - auto channelUtf8 = channel.toUtf8(); - quint16 channelLength = channelUtf8.length(); - packetList->writePrimitive(channelLength); - packetList->write(channelUtf8); - - auto messageUtf8 = message.toUtf8(); - quint16 messageLength = messageUtf8.length(); - packetList->writePrimitive(messageLength); - packetList->write(messageUtf8); - + QUuid senderID = nodeList->getSessionUUID(); + auto packetList = encodeMessagesPacket(channel, message, senderID); nodeList->sendPacketList(std::move(packetList), *messagesMixer); } } -void MessagesClient::subscribe(const QString& channel) { +void MessagesClient::subscribe(QString channel) { _subscribedChannels << channel; auto nodeList = DependencyManager::get(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); @@ -87,7 +104,7 @@ void MessagesClient::subscribe(const QString& channel) { } } -void MessagesClient::unsubscribe(const QString& channel) { +void MessagesClient::unsubscribe(QString channel) { _subscribedChannels.remove(channel); auto nodeList = DependencyManager::get(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index a1ae4cb5ba..d5d94f62c4 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -28,12 +28,16 @@ public: Q_INVOKABLE void init(); - Q_INVOKABLE void sendMessage(const QString& channel, const QString& message); - Q_INVOKABLE void subscribe(const QString& channel); - Q_INVOKABLE void unsubscribe(const QString& channel); + Q_INVOKABLE void sendMessage(QString channel, QString message); + Q_INVOKABLE void subscribe(QString channel); + Q_INVOKABLE void unsubscribe(QString channel); + + static void decodeMessagesPacket(QSharedPointer packetList, QString& channel, QString& message, QUuid& senderID); + static std::unique_ptr encodeMessagesPacket(QString channel, QString message, QUuid senderID); + signals: - void messageReceived(const QString& channel, const QString& message, const QUuid& senderUUID); + void messageReceived(QString channel, QString message, QUuid senderUUID); private slots: void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); From 8ba6dfe721cc9c0369e0ceec677daba77a3dfd1b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 20 Nov 2015 09:29:36 -0800 Subject: [PATCH 14/22] add deprication warning about binary SVOs --- libraries/octree/src/Octree.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index fe92fe7745..6f73be360f 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1841,6 +1841,7 @@ bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStream) { + qWarning() << "SVO file format depricated. Support for reading SVO files is no longer support and will be removed soon."; bool fileOk = false; @@ -2062,6 +2063,8 @@ void Octree::writeToJSONFile(const char* fileName, OctreeElementPointer element, } void Octree::writeToSVOFile(const char* fileName, OctreeElementPointer element) { + qWarning() << "SVO file format depricated. Support for reading SVO files is no longer support and will be removed soon."; + std::ofstream file(fileName, std::ios::out|std::ios::binary); if(file.is_open()) { From 0e5e33446e0fcf26d746c6b0734cfc0c1c85614e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 20 Nov 2015 09:57:30 -0800 Subject: [PATCH 15/22] handle old protocol case --- libraries/networking/src/MessagesClient.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index fc1729b4c5..95b61cf8d7 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -52,7 +52,11 @@ void MessagesClient::decodeMessagesPacket(QSharedPointer packetLis message = QString::fromUtf8(messageData); QByteArray bytesSenderID = packet.read(NUM_BYTES_RFC4122_UUID); - senderID = QUuid::fromRfc4122(bytesSenderID); + if (bytesSenderID.length() == NUM_BYTES_RFC4122_UUID) { + senderID = QUuid::fromRfc4122(bytesSenderID); + } else { + senderID = QUuid::QUuid(); // packet was missing UUID use default instead + } } std::unique_ptr MessagesClient::encodeMessagesPacket(QString channel, QString message, QUuid senderID) { From e530aa6545b925f35e1651a9c5def71f9f0bc38d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 20 Nov 2015 09:58:28 -0800 Subject: [PATCH 16/22] don't assert on sender mismatch --- assignment-client/src/messages/MessagesMixer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index a811631617..684c5f262c 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -47,7 +47,6 @@ void MessagesMixer::handleMessages(QSharedPointer packetList, Shar QString channel, message; QUuid senderID; MessagesClient::decodeMessagesPacket(packetList, channel, message, senderID); - Q_ASSERT(senderNode->getUUID() == senderID); // NOTE: do we want to reject messages that come from bogus senders auto nodeList = DependencyManager::get(); From 19d3d80ff97ec6e2aafff260fcc14b47482beaff Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Nov 2015 11:03:52 -0800 Subject: [PATCH 17/22] fixes for some incorrect assumptions --- interface/src/avatar/AvatarManager.cpp | 10 +++++++++- libraries/avatars/src/AvatarHashMap.cpp | 2 +- libraries/avatars/src/AvatarHashMap.h | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index bd45561b38..c17590c4ac 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -110,16 +110,22 @@ void AvatarManager::updateMyAvatar(float deltaTime) { } void AvatarManager::updateOtherAvatars(float deltaTime) { + // lock the hash for read to check the size + QReadLocker lock(&_hashLock); + if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) { return; } + + lock.unlock(); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); PerformanceTimer perfTimer("otherAvatars"); // simulate avatars - auto hashCopy = _avatarHash; + auto hashCopy = getHashCopy(); AvatarHash::iterator avatarIterator = hashCopy.begin(); while (avatarIterator != hashCopy.end()) { @@ -256,8 +262,10 @@ QVector AvatarManager::getLocalLights() const { } QVector AvatarManager::getAvatarIdentifiers() { + QReadLocker lock(&_hashLock); return _avatarHash.keys().toVector(); } + AvatarData* AvatarManager::getAvatar(QUuid avatarID) { QReadLocker locker(&_hashLock); return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar. diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 035d29f344..c195ab4c32 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -23,7 +23,7 @@ AvatarHashMap::AvatarHashMap() { } bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) { - auto hashCopy = _avatarHash; + auto hashCopy = getHashCopy(); foreach(const AvatarSharedPointer& sharedAvatar, hashCopy) { glm::vec3 avatarPosition = sharedAvatar->getPosition(); float distance = glm::distance(avatarPosition, position); diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 7795072ec2..5881b779a1 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -31,7 +31,7 @@ class AvatarHashMap : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - AvatarHash getHashCopy() { return _avatarHash; } + AvatarHash getHashCopy() { QReadLocker lock(&_hashLock); return _avatarHash; } int size() { return _avatarHash.size(); } signals: From 9b5bfd45bc4a822ff365d646b4d65c449655f649 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Nov 2015 11:04:46 -0800 Subject: [PATCH 18/22] change lock back to locker to remove change --- interface/src/avatar/AvatarManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c17590c4ac..769b1d56a2 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -262,7 +262,7 @@ QVector AvatarManager::getLocalLights() const { } QVector AvatarManager::getAvatarIdentifiers() { - QReadLocker lock(&_hashLock); + QReadLocker locker(&_hashLock); return _avatarHash.keys().toVector(); } From caa8b0b5b6ff02dca0a7d71bd10cbeb26ccbdc1d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 20 Nov 2015 11:07:01 -0800 Subject: [PATCH 19/22] fix unix build --- libraries/networking/src/MessagesClient.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 95b61cf8d7..ef8ecb534c 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -55,7 +55,8 @@ void MessagesClient::decodeMessagesPacket(QSharedPointer packetLis if (bytesSenderID.length() == NUM_BYTES_RFC4122_UUID) { senderID = QUuid::fromRfc4122(bytesSenderID); } else { - senderID = QUuid::QUuid(); // packet was missing UUID use default instead + QUuid emptyUUID; + senderID = emptyUUID; // packet was missing UUID use default instead } } From 7441e20f5842ab2b7d1fe8ff175a05feafd41d5d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 20 Nov 2015 11:20:36 -0800 Subject: [PATCH 20/22] cleanup --- .../src/messages/MessagesMixer.cpp | 39 ++++++------------- .../src/messages/MessagesMixer.h | 1 - 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 684c5f262c..8f2b8a5475 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -12,18 +12,15 @@ #include #include #include - #include #include #include #include - #include "MessagesMixer.h" const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer"; -MessagesMixer::MessagesMixer(NLPacket& packet) : - ThreadedAssignment(packet) +MessagesMixer::MessagesMixer(NLPacket& packet) : ThreadedAssignment(packet) { connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); @@ -32,9 +29,6 @@ MessagesMixer::MessagesMixer(NLPacket& packet) : packetReceiver.registerMessageListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe"); } -MessagesMixer::~MessagesMixer() { -} - void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { for (auto& channel : _channelSubscribers) { channel.remove(killedNode->getUUID()); @@ -42,8 +36,6 @@ void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { } void MessagesMixer::handleMessages(QSharedPointer packetList, SharedNodePointer senderNode) { - Q_ASSERT(packetList->getType() == PacketType::MessagesData); - QString channel, message; QUuid senderID; MessagesClient::decodeMessagesPacket(packetList, channel, message, senderID); @@ -62,43 +54,34 @@ void MessagesMixer::handleMessages(QSharedPointer packetList, Shar } void MessagesMixer::handleMessagesSubscribe(QSharedPointer packetList, SharedNodePointer senderNode) { - Q_ASSERT(packetList->getType() == PacketType::MessagesSubscribe); QString channel = QString::fromUtf8(packetList->getMessage()); - qDebug() << "Node [" << senderNode->getUUID() << "] subscribed to channel:" << channel; _channelSubscribers[channel] << senderNode->getUUID(); } void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer packetList, SharedNodePointer senderNode) { - Q_ASSERT(packetList->getType() == PacketType::MessagesUnsubscribe); QString channel = QString::fromUtf8(packetList->getMessage()); - qDebug() << "Node [" << senderNode->getUUID() << "] unsubscribed from channel:" << channel; - if (_channelSubscribers.contains(channel)) { _channelSubscribers[channel].remove(senderNode->getUUID()); } } -// FIXME - make these stats relevant void MessagesMixer::sendStatsPacket() { - QJsonObject statsObject; - QJsonObject messagesObject; - auto nodeList = DependencyManager::get(); + QJsonObject statsObject, messagesMixerObject; // add stats for each listerner - nodeList->eachNode([&](const SharedNodePointer& node) { - QJsonObject messagesStats; - messagesStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); - messagesStats["outbound_kbps"] = node->getOutboundBandwidth(); - messagesStats["inbound_kbps"] = node->getInboundBandwidth(); - messagesObject[uuidStringWithoutCurlyBraces(node->getUUID())] = messagesStats; + DependencyManager::get()->eachNode([&](const SharedNodePointer& node) { + QJsonObject clientStats; + clientStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); + clientStats["outbound_kbps"] = node->getOutboundBandwidth(); + clientStats["inbound_kbps"] = node->getInboundBandwidth(); + messagesMixerObject[uuidStringWithoutCurlyBraces(node->getUUID())] = clientStats; }); - statsObject["messages"] = messagesObject; + statsObject["messages"] = messagesMixerObject; ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); } void MessagesMixer::run() { ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); - auto nodeList = DependencyManager::get(); - nodeList->addNodeTypeToInterestSet(NodeType::Agent); -} + DependencyManager::get()->addNodeTypeToInterestSet(NodeType::Agent); +} \ No newline at end of file diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 65419a8ca6..cf5fc79e17 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -22,7 +22,6 @@ class MessagesMixer : public ThreadedAssignment { Q_OBJECT public: MessagesMixer(NLPacket& packet); - ~MessagesMixer(); public slots: void run(); From f1badc017b3c79b11d70fcc583fe78dd2f2911dd Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 20 Nov 2015 11:43:50 -0800 Subject: [PATCH 21/22] CR feedback --- libraries/networking/src/AssetClient.cpp | 39 +++++++++--------------- libraries/networking/src/AssetClient.h | 1 - 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 4dc08962c5..8ac019ff56 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -369,18 +369,19 @@ void AssetClient::handleNodeKilled(SharedNodePointer node) { void AssetScriptingInterface::uploadData(QString data, QString extension, QScriptValue callback) { QByteArray dataByteArray = data.toUtf8(); auto upload = DependencyManager::get()->createUpload(dataByteArray, extension); - if (upload) { - QObject::connect(upload, &AssetUpload::finished, this, [callback, extension](AssetUpload* upload, const QString& hash) mutable { - if (callback.isFunction()) { - QString url = "atp://" + hash + "." + extension; - QScriptValueList args { url }; - callback.call(QScriptValue(), args); - } - }); - - // start the upload now - upload->start(); + if (!upload) { + qCWarning(asset_client) << "Error uploading file to asset server"; + return; } + + QObject::connect(upload, &AssetUpload::finished, this, [callback, extension](AssetUpload* upload, const QString& hash) mutable { + if (callback.isFunction()) { + QString url = "atp://" + hash + "." + extension; + QScriptValueList args { url }; + callback.call(QScriptValue(), args); + } + }); + upload->start(); } void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) { @@ -407,10 +408,7 @@ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callb return; } - { - QWriteLocker locker(&_lock); - _pendingRequests << assetRequest; - } + _pendingRequests << assetRequest; connect(assetRequest, &AssetRequest::finished, [this, callback](AssetRequest* request) mutable { Q_ASSERT(request->getState() == AssetRequest::Finished); @@ -424,15 +422,8 @@ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callb } request->deleteLater(); - - { - QWriteLocker locker(&_lock); - _pendingRequests.remove(request); - } + _pendingRequests.remove(request); }); assetRequest->start(); -} - - - +} \ No newline at end of file diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index a194db225b..f1bb210614 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -77,7 +77,6 @@ public: Q_INVOKABLE void uploadData(QString data, QString extension, QScriptValue callback); Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete); protected: - mutable QReadWriteLock _lock; QSet _pendingRequests; }; From cc2a7cbda891c823af9123987bf657c5f793b9ec Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 20 Nov 2015 11:51:08 -0800 Subject: [PATCH 22/22] Force synchronous loading of clips in JS --- .../src/RecordingScriptingInterface.cpp | 20 ++++++++++++------- .../src/RecordingScriptingInterface.h | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index d82d471d79..83e44ef173 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -44,19 +44,25 @@ float RecordingScriptingInterface::playerLength() const { return _player->length(); } -void RecordingScriptingInterface::loadRecording(const QString& url) { +bool RecordingScriptingInterface::loadRecording(const QString& url) { using namespace recording; - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection, - Q_ARG(QString, url)); - return; + auto loader = ClipCache::instance().getClipLoader(url); + QEventLoop loop; + QObject::connect(loader.data(), &Resource::loaded, &loop, &QEventLoop::quit); + QObject::connect(loader.data(), &Resource::failed, &loop, &QEventLoop::quit); + loop.exec(); + + if (!loader->isLoaded()) { + qWarning() << "Clip failed to load from " << url; + return false; } - // FIXME make blocking and force off main thread? - _player->queueClip(ClipCache::instance().getClipLoader(url)->getClip()); + _player->queueClip(loader->getClip()); + return true; } + void RecordingScriptingInterface::startPlaying() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection); diff --git a/libraries/script-engine/src/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h index 3834089177..b39485d75c 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.h +++ b/libraries/script-engine/src/RecordingScriptingInterface.h @@ -25,7 +25,7 @@ public: RecordingScriptingInterface(); public slots: - void loadRecording(const QString& url); + bool loadRecording(const QString& url); void startPlaying(); void pausePlayer();