From 0da81efd93eaed495464ca08112922c103dd6ea3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 15 Feb 2017 13:59:42 -0800 Subject: [PATCH 1/9] Add includeDescendants and includeParents to ESS entity query --- assignment-client/src/scripts/EntityScriptServer.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 930b3946c6..3a03cde5d2 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -266,6 +266,14 @@ void EntityScriptServer::run() { QJsonObject queryJSONParameters; static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts"; queryJSONParameters[SERVER_SCRIPTS_PROPERTY] = EntityQueryFilterSymbol::NonDefault; + + QJsonObject queryFlags; + static const QString INCLUDE_DESCENDANTS_PROPERTY = "includeDescendants"; + static const QString INCLUDE_PARENTS_PROPERTY = "includeParents"; + queryFlags[INCLUDE_DESCENDANTS_PROPERTY] = true; + queryFlags[INCLUDE_PARENTS_PROPERTY] = true; + + queryJSONParameters["flags"] = queryFlags; // setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter _entityViewer.getOctreeQuery().setUsesFrustum(false); From a3883a746cfe788b07df188fcda0a2b80b43f9dd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 15 Feb 2017 18:55:24 -0800 Subject: [PATCH 2/9] add the basic structure for inclusion of ancestors/descendants in ESS queries --- .../src/entities/EntityServer.cpp | 9 +- assignment-client/src/entities/EntityServer.h | 1 + .../src/entities/EntityTreeSendThread.cpp | 107 ++++++++++++++++++ .../src/entities/EntityTreeSendThread.h | 34 ++++++ .../src/octree/OctreeSendThread.cpp | 3 + .../src/octree/OctreeSendThread.h | 12 +- assignment-client/src/octree/OctreeServer.cpp | 6 +- assignment-client/src/octree/OctreeServer.h | 1 + .../src/scripts/EntityScriptServer.cpp | 13 +-- libraries/entities/src/EntityNodeData.cpp | 25 ++++ libraries/entities/src/EntityNodeData.h | 24 +++- libraries/entities/src/EntityTreeElement.cpp | 21 ++-- 12 files changed, 228 insertions(+), 28 deletions(-) create mode 100644 assignment-client/src/entities/EntityTreeSendThread.cpp create mode 100644 assignment-client/src/entities/EntityTreeSendThread.h create mode 100644 libraries/entities/src/EntityNodeData.cpp diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 425bea2c38..dc0a2add3a 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -17,10 +17,11 @@ #include #include +#include "AssignmentParentFinder.h" +#include "EntityNodeData.h" #include "EntityServer.h" #include "EntityServerConsts.h" -#include "EntityNodeData.h" -#include "AssignmentParentFinder.h" +#include "EntityTreeSendThread.h" const char* MODEL_SERVER_NAME = "Entity"; const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server"; @@ -77,6 +78,10 @@ OctreePointer EntityServer::createTree() { return tree; } +OctreeServer::UniqueSendThread EntityServer::newSendThread(const SharedNodePointer& node) { + return std::unique_ptr(new EntityTreeSendThread(this, node)); +} + void EntityServer::beforeRun() { _pruneDeletedEntitiesTimer = new QTimer(); connect(_pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities())); diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 325435fe7e..40676e79bd 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -67,6 +67,7 @@ public slots: protected: virtual OctreePointer createTree() override; + virtual UniqueSendThread newSendThread(const SharedNodePointer& node) override; private slots: void handleEntityPacket(QSharedPointer message, SharedNodePointer senderNode); diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp new file mode 100644 index 0000000000..11a7ddf038 --- /dev/null +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -0,0 +1,107 @@ +// +// EntityTreeSendThread.cpp +// assignment-client/src/entities +// +// Created by Stephen Birarda on 2/15/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "EntityTreeSendThread.h" + +#include +#include + +#include "EntityServer.h" + +void EntityTreeSendThread::preDistributionProcessing() { + auto node = _node.toStrongRef(); + auto nodeData = static_cast(node->getLinkedData()); + + if (nodeData) { + auto jsonQuery = nodeData->getJSONParameters(); + + // check if we have a JSON query with flags + auto flags = jsonQuery[EntityJSONQueryProperties::FLAGS_PROPERTY].toObject(); + if (!flags.isEmpty()) { + // check the flags object for specific flags that require special pre-processing + + bool includeAncestors = flags[EntityJSONQueryProperties::INCLUDE_ANCESTORS_PROPERTY].toBool(); + bool includeDescendants = flags[EntityJSONQueryProperties::INCLUDE_DESCENDANTS_PROPERTY].toBool(); + + if (includeAncestors || includeDescendants) { + // we need to either include the ancestors, descendants, or both for entities matching the filter + // included in the JSON query + + auto entityTree = std::static_pointer_cast(_myServer->getOctree()); + + // enumerate the set of entity IDs we know currently match the filter + foreach(const QUuid& entityID, nodeData->getSentFilteredEntities()) { + if (includeAncestors) { + // we need to include ancestors - recurse up to reach them all and add their IDs + // to the set of extra entities to include for this node + entityTree->withReadLock([&]{ + auto filteredEntity = entityTree->findEntityByID(entityID); + if (filteredEntity) { + addAncestorsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData); + } + }); + } + + if (includeDescendants) { + // we need to include descendants - recurse down to reach them all and add their IDs + // to the set of extra entities to include for this node + entityTree->withReadLock([&]{ + auto filteredEntity = entityTree->findEntityByID(entityID); + if (filteredEntity) { + addDescendantsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData); + } + }); + } + } + } + + } + } +} + +void EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, + EntityItem& entityItem, EntityNodeData& nodeData) { + // check if this entity has a parent that is also an entity + bool success = false; + auto entityParent = entityItem.getParentPointer(success); + + if (success && entityParent && entityParent->getNestableType() == NestableType::Entity) { + // we found a parent that is an entity item + + // first add it to the extra list of things we need to send + nodeData.insertFlaggedExtraEntity(filteredEntityID, entityParent->getID()); + +// qDebug() << "Adding" << entityParent->getID() << "which is an ancestor of" << filteredEntityID; + + // now recursively call ourselves to get its ancestors added too + auto parentEntityItem = std::static_pointer_cast(entityParent); + addAncestorsToExtraFlaggedEntities(filteredEntityID, *parentEntityItem, nodeData); + } +} + +void EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, + EntityItem& entityItem, EntityNodeData& nodeData) { + // enumerate the immediate children of this entity + foreach (SpatiallyNestablePointer child, entityItem.getChildren()) { + if (child && child->getNestableType() == NestableType::Entity) { + // this is a child that is an entity + + // first add it to the extra list of things we need to send + nodeData.insertFlaggedExtraEntity(filteredEntityID, child->getID()); + +// qDebug() << "Adding" << child->getID() << "which is a descendant of" << filteredEntityID; + + // now recursively call ourselves to get its descendants added too + auto childEntityItem = std::static_pointer_cast(child); + addDescendantsToExtraFlaggedEntities(filteredEntityID, *childEntityItem, nodeData); + } + } +} diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h new file mode 100644 index 0000000000..8273b0379f --- /dev/null +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -0,0 +1,34 @@ +// +// EntityTreeSendThread.h +// assignment-client/src/entities +// +// Created by Stephen Birarda on 2/15/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_EntityTreeSendThread_h +#define hifi_EntityTreeSendThread_h + +#include "../octree/OctreeSendThread.h" + +class EntityNodeData; +class EntityItem; + +class EntityTreeSendThread : public OctreeSendThread { + +public: + EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) : OctreeSendThread(myServer, node) {}; + +protected: + virtual void preDistributionProcessing() override; + +private: + void addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); + void addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); + +}; + +#endif // hifi_EntityTreeSendThread_h diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index afc17d71aa..c3bdb3752d 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -309,6 +309,9 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* return 0; } + // give our pre-distribution processing a chance to do what it needs + preDistributionProcessing(); + // calculate max number of packets that can be sent during this interval int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND)); int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 7efe5b3a86..06c9b5f1d6 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -17,6 +17,8 @@ #include #include +#include +#include class OctreeQueryNode; class OctreeServer; @@ -49,13 +51,17 @@ protected: /// Implements generic processing behavior for this thread. virtual bool process() override; + /// Called before a packetDistributor pass to allow for pre-distribution processing + virtual void preDistributionProcessing() {}; + + OctreeServer* _myServer { nullptr }; + QWeakPointer _node; + private: int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent, bool dontSuppressDuplicate = false); int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged); - - OctreeServer* _myServer { nullptr }; - QWeakPointer _node; + QUuid _nodeUuid; OctreePacketData _packetData; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index c36a9be050..2eee2ee229 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -872,8 +872,12 @@ void OctreeServer::parsePayload() { } } +OctreeServer::UniqueSendThread OctreeServer::newSendThread(const SharedNodePointer& node) { + return std::unique_ptr(new OctreeSendThread(this, node)); +} + OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) { - auto sendThread = std::unique_ptr(new OctreeSendThread(this, node)); + auto sendThread = newSendThread(node); // we want to be notified when the thread finishes connect(sendThread.get(), &GenericThread::finished, this, &OctreeServer::removeSendThread); diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 2bcf36628d..3bb327eb06 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -158,6 +158,7 @@ protected: QString getStatusLink(); UniqueSendThread createSendThread(const SharedNodePointer& node); + virtual UniqueSendThread newSendThread(const SharedNodePointer& node); int _argc; const char** _argv; diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 3a03cde5d2..06df255968 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -264,16 +265,14 @@ void EntityScriptServer::run() { // setup the JSON filter that asks for entities with a non-default serverScripts property QJsonObject queryJSONParameters; - static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts"; - queryJSONParameters[SERVER_SCRIPTS_PROPERTY] = EntityQueryFilterSymbol::NonDefault; + queryJSONParameters[EntityJSONQueryProperties::SERVER_SCRIPTS_PROPERTY] = EntityQueryFilterSymbol::NonDefault; QJsonObject queryFlags; - static const QString INCLUDE_DESCENDANTS_PROPERTY = "includeDescendants"; - static const QString INCLUDE_PARENTS_PROPERTY = "includeParents"; - queryFlags[INCLUDE_DESCENDANTS_PROPERTY] = true; - queryFlags[INCLUDE_PARENTS_PROPERTY] = true; - queryJSONParameters["flags"] = queryFlags; + queryFlags[EntityJSONQueryProperties::INCLUDE_ANCESTORS_PROPERTY] = true; + queryFlags[EntityJSONQueryProperties::INCLUDE_DESCENDANTS_PROPERTY] = true; + + queryJSONParameters[EntityJSONQueryProperties::FLAGS_PROPERTY] = queryFlags; // setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter _entityViewer.getOctreeQuery().setUsesFrustum(false); diff --git a/libraries/entities/src/EntityNodeData.cpp b/libraries/entities/src/EntityNodeData.cpp new file mode 100644 index 0000000000..3e28bbcce4 --- /dev/null +++ b/libraries/entities/src/EntityNodeData.cpp @@ -0,0 +1,25 @@ +// +// EntityNodeData.cpp +// libraries/entities/src +// +// Created by Stephen Birarda on 2/15/17 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "EntityNodeData.h" + +bool EntityNodeData::isEntityFlaggedAsExtra(const QUuid& entityID) const { + + // enumerate each of the sets for the entities that matched our filter + // and immediately return true if any of them contain this entity ID + foreach(QSet entitySet, _flaggedExtraEntities) { + if (entitySet.contains(entityID)) { + return true; + } + } + + return false; +} diff --git a/libraries/entities/src/EntityNodeData.h b/libraries/entities/src/EntityNodeData.h index b3a576b1ad..681d72f2d4 100644 --- a/libraries/entities/src/EntityNodeData.h +++ b/libraries/entities/src/EntityNodeData.h @@ -1,6 +1,6 @@ // // EntityNodeData.h -// assignment-client/src/entities +// libraries/entities/src // // Created by Brad Hefta-Gaub on 4/29/14 // Copyright 2014 High Fidelity, Inc. @@ -16,6 +16,13 @@ #include +namespace EntityJSONQueryProperties { + static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts"; + static const QString FLAGS_PROPERTY = "flags"; + static const QString INCLUDE_ANCESTORS_PROPERTY = "includeAncestors"; + static const QString INCLUDE_DESCENDANTS_PROPERTY = "includeDescendants"; +} + class EntityNodeData : public OctreeQueryNode { public: virtual PacketType getMyPacketType() const override { return PacketType::EntityData; } @@ -24,13 +31,20 @@ public: void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; } // these can only be called from the OctreeSendThread for the given Node - void insertEntitySentLastFrame(const QUuid& entityID) { _entitiesSentLastFrame.insert(entityID); } - void removeEntitySentLastFrame(const QUuid& entityID) { _entitiesSentLastFrame.remove(entityID); } - bool sentEntityLastFrame(const QUuid& entityID) { return _entitiesSentLastFrame.contains(entityID); } + void insertSentFilteredEntity(const QUuid& entityID) { _sentFilteredEntities.insert(entityID); } + void removeSentFilteredEntity(const QUuid& entityID) { _sentFilteredEntities.remove(entityID); } + bool sentFilteredEntity(const QUuid& entityID) { return _sentFilteredEntities.contains(entityID); } + QSet getSentFilteredEntities() { return _sentFilteredEntities; } + + // these can only be called from the OctreeSendThread for the given Node + void insertFlaggedExtraEntity(const QUuid& filteredEntityID, const QUuid& extraEntityID) + { _flaggedExtraEntities[filteredEntityID].insert(extraEntityID); } + bool isEntityFlaggedAsExtra(const QUuid& entityID) const; private: quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() }; - QSet _entitiesSentLastFrame; + QSet _sentFilteredEntities; + QHash> _flaggedExtraEntities; }; #endif // hifi_EntityNodeData_h diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 525c1ec65f..755c19e625 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -310,16 +310,17 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData if (entityMatchesFilters) { // make sure this entity is in the set of entities sent last frame - entityNodeData->insertEntitySentLastFrame(entity->getID()); - - } else { - // we might include this entity if it matched in the previous frame - if (entityNodeData->sentEntityLastFrame(entity->getID())) { - - entityNodeData->removeEntitySentLastFrame(entity->getID()); - } else { - includeThisEntity = false; - } + entityNodeData->insertSentFilteredEntity(entity->getID()); + } else if (entityNodeData->sentFilteredEntity(entity->getID())) { + // this entity matched in the previous frame - we send it still so the client realizes it just + // fell outside of their filter + entityNodeData->removeSentFilteredEntity(entity->getID()); + } else if (!entityNodeData->isEntityFlaggedAsExtra(entity->getID())) { + // we don't send this entity because + // (1) it didn't match our filter + // (2) it didn't match our filter last frame + // (3) it isn't one the JSON query flags told us we should still include + includeThisEntity = false; } } From 9d336a84ab3214d5774c22e59a7d9834ab47accb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 16 Feb 2017 13:05:33 -0800 Subject: [PATCH 3/9] force a full scene send for new ancestors/descendants --- .../src/entities/EntityTreeSendThread.cpp | 47 +++++++++++++++---- .../src/entities/EntityTreeSendThread.h | 5 +- .../src/octree/OctreeSendThread.cpp | 21 +++++++-- libraries/entities/src/EntityNodeData.cpp | 5 ++ libraries/entities/src/EntityNodeData.h | 10 ++-- libraries/octree/src/OctreeQueryNode.h | 5 ++ 6 files changed, 75 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 11a7ddf038..76c56bdf37 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -35,8 +35,13 @@ void EntityTreeSendThread::preDistributionProcessing() { // we need to either include the ancestors, descendants, or both for entities matching the filter // included in the JSON query + // first reset our flagged extra entities so we start with an empty set + nodeData->resetFlaggedExtraEntities(); + auto entityTree = std::static_pointer_cast(_myServer->getOctree()); + bool requiresFullScene = false; + // enumerate the set of entity IDs we know currently match the filter foreach(const QUuid& entityID, nodeData->getSentFilteredEntities()) { if (includeAncestors) { @@ -45,7 +50,7 @@ void EntityTreeSendThread::preDistributionProcessing() { entityTree->withReadLock([&]{ auto filteredEntity = entityTree->findEntityByID(entityID); if (filteredEntity) { - addAncestorsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData); + requiresFullScene |= addAncestorsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData); } }); } @@ -56,18 +61,27 @@ void EntityTreeSendThread::preDistributionProcessing() { entityTree->withReadLock([&]{ auto filteredEntity = entityTree->findEntityByID(entityID); if (filteredEntity) { - addDescendantsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData); + requiresFullScene |= addDescendantsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData); } }); } } + + if (requiresFullScene) { + // for one or more of the entities matching our filter we found new extra entities to include + + // because it is possible that one of these entities hasn't changed since our last send + // and therefore would not be recursed to, we need to force a full traversal for this pass + // of the tree to allow it to grab all of the extra entities we're asking it to include + nodeData->setShouldForceFullScene(requiresFullScene); + } } } } } -void EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, +bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData) { // check if this entity has a parent that is also an entity bool success = false; @@ -77,31 +91,48 @@ void EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filte // we found a parent that is an entity item // first add it to the extra list of things we need to send - nodeData.insertFlaggedExtraEntity(filteredEntityID, entityParent->getID()); + bool parentWasNew = nodeData.insertFlaggedExtraEntity(filteredEntityID, entityParent->getID()); // qDebug() << "Adding" << entityParent->getID() << "which is an ancestor of" << filteredEntityID; // now recursively call ourselves to get its ancestors added too auto parentEntityItem = std::static_pointer_cast(entityParent); - addAncestorsToExtraFlaggedEntities(filteredEntityID, *parentEntityItem, nodeData); + bool ancestorsWereNew = addAncestorsToExtraFlaggedEntities(filteredEntityID, *parentEntityItem, nodeData); + + // return boolean if our parent or any of our ancestors were new additions (via insertFlaggedExtraEntity) + return parentWasNew || ancestorsWereNew; } + + // since we didn't have a parent niether of our parents or ancestors could be new additions + return false; } -void EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, +bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData) { + bool hasNewChild = false; + bool hasNewDescendants = false; + // enumerate the immediate children of this entity foreach (SpatiallyNestablePointer child, entityItem.getChildren()) { + + if (child && child->getNestableType() == NestableType::Entity) { // this is a child that is an entity // first add it to the extra list of things we need to send - nodeData.insertFlaggedExtraEntity(filteredEntityID, child->getID()); + hasNewChild |= nodeData.insertFlaggedExtraEntity(filteredEntityID, child->getID()); // qDebug() << "Adding" << child->getID() << "which is a descendant of" << filteredEntityID; // now recursively call ourselves to get its descendants added too auto childEntityItem = std::static_pointer_cast(child); - addDescendantsToExtraFlaggedEntities(filteredEntityID, *childEntityItem, nodeData); + hasNewDescendants |= addDescendantsToExtraFlaggedEntities(filteredEntityID, *childEntityItem, nodeData); } } + + // return our boolean indicating if we added new children or descendants as extra entities to send + // (via insertFlaggedExtraEntity) + return hasNewChild || hasNewDescendants; } + + diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index 8273b0379f..bfb4c743f1 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -26,8 +26,9 @@ protected: virtual void preDistributionProcessing() override; private: - void addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); - void addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); + // the following two methods return booleans to indicate if any extra flagged entities were new additions to set + bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); + bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); }; diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index c3bdb3752d..7922da8af4 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -309,8 +309,11 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* return 0; } - // give our pre-distribution processing a chance to do what it needs - preDistributionProcessing(); + if (nodeData->elementBag.isEmpty()) { + // if we're about to do a fresh pass, + // give our pre-distribution processing a chance to do what it needs + preDistributionProcessing(); + } // calculate max number of packets that can be sent during this interval int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND)); @@ -319,9 +322,17 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; - bool isFullScene = nodeData->haveJSONParametersChanged() || - (nodeData->getUsesFrustum() - && ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged())); + + bool isFullScene = nodeData->shouldForceFullScene(); + if (isFullScene) { + // we're forcing a full scene, clear the force in OctreeQueryNode so we don't force it next time again + nodeData->setShouldForceFullScene(false); + } else { + // we aren't forcing a full scene, check if something else suggests we should + isFullScene = nodeData->haveJSONParametersChanged() || + (nodeData->getUsesFrustum() + && ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged())); + } bool somethingToSend = true; // assume we have something diff --git a/libraries/entities/src/EntityNodeData.cpp b/libraries/entities/src/EntityNodeData.cpp index 3e28bbcce4..2f28f69565 100644 --- a/libraries/entities/src/EntityNodeData.cpp +++ b/libraries/entities/src/EntityNodeData.cpp @@ -11,6 +11,11 @@ #include "EntityNodeData.h" +bool EntityNodeData::insertFlaggedExtraEntity(const QUuid& filteredEntityID, const QUuid& extraEntityID) { + _flaggedExtraEntities[filteredEntityID].insert(extraEntityID); + return !_previousFlaggedExtraEntities[filteredEntityID].contains(extraEntityID); +} + bool EntityNodeData::isEntityFlaggedAsExtra(const QUuid& entityID) const { // enumerate each of the sets for the entities that matched our filter diff --git a/libraries/entities/src/EntityNodeData.h b/libraries/entities/src/EntityNodeData.h index 681d72f2d4..eb5a1610cc 100644 --- a/libraries/entities/src/EntityNodeData.h +++ b/libraries/entities/src/EntityNodeData.h @@ -36,15 +36,19 @@ public: bool sentFilteredEntity(const QUuid& entityID) { return _sentFilteredEntities.contains(entityID); } QSet getSentFilteredEntities() { return _sentFilteredEntities; } - // these can only be called from the OctreeSendThread for the given Node - void insertFlaggedExtraEntity(const QUuid& filteredEntityID, const QUuid& extraEntityID) - { _flaggedExtraEntities[filteredEntityID].insert(extraEntityID); } + // the following flagged extra entity methods can only be called from the OctreeSendThread for the given Node + + // inserts the extra entity and returns a boolean indicating wether the extraEntityID was a new addition + bool insertFlaggedExtraEntity(const QUuid& filteredEntityID, const QUuid& extraEntityID); + bool isEntityFlaggedAsExtra(const QUuid& entityID) const; + void resetFlaggedExtraEntities() { _previousFlaggedExtraEntities = _flaggedExtraEntities; _flaggedExtraEntities.clear(); } private: quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() }; QSet _sentFilteredEntities; QHash> _flaggedExtraEntities; + QHash> _previousFlaggedExtraEntities; }; #endif // hifi_EntityNodeData_h diff --git a/libraries/octree/src/OctreeQueryNode.h b/libraries/octree/src/OctreeQueryNode.h index 10c5598b30..305f43d0f8 100644 --- a/libraries/octree/src/OctreeQueryNode.h +++ b/libraries/octree/src/OctreeQueryNode.h @@ -103,6 +103,9 @@ public: // call only from OctreeSendThread for the given node bool haveJSONParametersChanged(); + bool shouldForceFullScene() const { return _shouldForceFullScene; } + bool setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; } + private: OctreeQueryNode(const OctreeQueryNode &); OctreeQueryNode& operator= (const OctreeQueryNode&); @@ -148,6 +151,8 @@ private: std::array _lastOctreePayload; QJsonObject _lastCheckJSONParameters; + + bool _shouldForceFullScene { false }; }; #endif // hifi_OctreeQueryNode_h From fe11a945ef4deb04150602e77f6ac6310fe69b29 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 16 Feb 2017 13:38:09 -0800 Subject: [PATCH 4/9] remove debug from EntityTreeSendThread --- assignment-client/src/entities/EntityTreeSendThread.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 76c56bdf37..e049647428 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -93,8 +93,6 @@ bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filte // first add it to the extra list of things we need to send bool parentWasNew = nodeData.insertFlaggedExtraEntity(filteredEntityID, entityParent->getID()); -// qDebug() << "Adding" << entityParent->getID() << "which is an ancestor of" << filteredEntityID; - // now recursively call ourselves to get its ancestors added too auto parentEntityItem = std::static_pointer_cast(entityParent); bool ancestorsWereNew = addAncestorsToExtraFlaggedEntities(filteredEntityID, *parentEntityItem, nodeData); @@ -122,8 +120,6 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil // first add it to the extra list of things we need to send hasNewChild |= nodeData.insertFlaggedExtraEntity(filteredEntityID, child->getID()); -// qDebug() << "Adding" << child->getID() << "which is a descendant of" << filteredEntityID; - // now recursively call ourselves to get its descendants added too auto childEntityItem = std::static_pointer_cast(child); hasNewDescendants |= addDescendantsToExtraFlaggedEntities(filteredEntityID, *childEntityItem, nodeData); From 4ece56b6739d805e29507c9fe21811869746d0fe Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 16 Feb 2017 13:39:21 -0800 Subject: [PATCH 5/9] spacing cleanup in EntityTreeSendThread --- assignment-client/src/entities/EntityTreeSendThread.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index e049647428..7febdc67e1 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -76,7 +76,6 @@ void EntityTreeSendThread::preDistributionProcessing() { nodeData->setShouldForceFullScene(requiresFullScene); } } - } } } @@ -113,7 +112,6 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil // enumerate the immediate children of this entity foreach (SpatiallyNestablePointer child, entityItem.getChildren()) { - if (child && child->getNestableType() == NestableType::Entity) { // this is a child that is an entity From d4119d5676958e46526d13f976cff978febac83c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 16 Feb 2017 13:41:04 -0800 Subject: [PATCH 6/9] fix for non-void set of should force scene --- libraries/octree/src/OctreeQueryNode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/octree/src/OctreeQueryNode.h b/libraries/octree/src/OctreeQueryNode.h index 305f43d0f8..fd89a89949 100644 --- a/libraries/octree/src/OctreeQueryNode.h +++ b/libraries/octree/src/OctreeQueryNode.h @@ -104,7 +104,7 @@ public: bool haveJSONParametersChanged(); bool shouldForceFullScene() const { return _shouldForceFullScene; } - bool setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; } + void setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; } private: OctreeQueryNode(const OctreeQueryNode &); From 9f9fc03751b5a5eab37d58f45a5d0185536315c6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 16 Feb 2017 15:08:42 -0800 Subject: [PATCH 7/9] call EntityTree update when ScriptEngine update occurs --- assignment-client/src/scripts/EntityScriptServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 06df255968..f8f728f834 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -419,6 +419,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() { connect(newEngine.data(), &ScriptEngine::update, this, [this] { _entityViewer.queryOctree(); + _entityViewer.getTree()->update(); }); From 78301fd9477f0363767ab9d8d40849ac9b6308c5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 16 Feb 2017 15:59:45 -0800 Subject: [PATCH 8/9] call getParentPointer when parent ID changes to update children --- libraries/shared/src/SpatiallyNestable.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index ddc3f416e0..75574967e4 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -70,6 +70,9 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) { _parentKnowsMe = false; } }); + + bool success = false; + getParentPointer(success); } Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const { From 3b996f2b7fdcab5e968e9651e41db26cc63372b6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Feb 2017 09:29:07 -0800 Subject: [PATCH 9/9] bump packet version for new JSON query options --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index ddbc30d020..a6718402a3 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -51,7 +51,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityPhysics: return VERSION_ENTITIES_ZONE_FILTERS; case PacketType::EntityQuery: - return static_cast(EntityQueryPacketVersion::JsonFilter); + return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); case PacketType::AvatarIdentity: case PacketType::AvatarData: case PacketType::BulkAvatarData: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index de3d0369b5..050e3088f8 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -207,7 +207,8 @@ const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67; const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68; enum class EntityQueryPacketVersion: PacketVersion { - JsonFilter = 18 + JSONFilter = 18, + JSONFilterWithFamilyTree = 19 }; enum class AssetServerPacketVersion: PacketVersion {