diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 11e4d533fb..b9f1ca4daf 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -23,6 +23,16 @@ EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedN { connect(std::static_pointer_cast(myServer->getOctree()).get(), &EntityTree::editingEntityPointer, this, &EntityTreeSendThread::editingEntityPointer, Qt::QueuedConnection); connect(std::static_pointer_cast(myServer->getOctree()).get(), &EntityTree::deletingEntityPointer, this, &EntityTreeSendThread::deletingEntityPointer, Qt::QueuedConnection); + + // connect to connection ID change on EntityNodeData so we can clear state for this receiver + auto nodeData = static_cast(node->getLinkedData()); + connect(nodeData, &EntityNodeData::incomingConnectionIDChanged, this, &EntityTreeSendThread::resetKnownState); +} + +void EntityTreeSendThread::resetKnownState() { + qCDebug(entities) << "Clearing known EntityTreeSendThread state for" << _nodeUuid; + + _knownState.clear(); } void EntityTreeSendThread::preDistributionProcessing() { diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index a96a18494d..37d9ab720d 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -33,6 +33,9 @@ protected: void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) override; +private slots: + void resetKnownState(); // clears our known state forcing entities to appear unsent + private: // 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); diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index bc7d2c2588..220952e209 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -59,7 +59,8 @@ protected: OctreePacketData _packetData; QWeakPointer _node; OctreeServer* _myServer { nullptr }; - + QUuid _nodeUuid; + private: /// Called before a packetDistributor pass to allow for pre-distribution processing virtual void preDistributionProcessing() {}; @@ -71,8 +72,6 @@ private: virtual void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene); virtual bool shouldTraverseAndSend(OctreeQueryNode* nodeData) { return hasSomethingToSend(nodeData); } - QUuid _nodeUuid; - int _truePacketsSent { 0 }; // available for debug stats int _trueBytesSent { 0 }; // available for debug stats int _packetsSentThisInterval { 0 }; // used for bandwidth throttle condition diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c53b2c993c..2624aa376f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1392,7 +1392,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Make sure we don't time out during slow operations at startup updateHeartbeat(); - QTimer* settingsTimer = new QTimer(); moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{ connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{ @@ -4482,8 +4481,11 @@ void Application::resetPhysicsReadyInformation() { void Application::reloadResourceCaches() { resetPhysicsReadyInformation(); + // Query the octree to refresh everything in view _lastQueriedTime = 0; + _octreeQuery.incrementConnectionID(); + queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions); DependencyManager::get()->clearCache(); @@ -5544,6 +5546,7 @@ void Application::nodeActivated(SharedNodePointer node) { // so we will do a proper query during update if (node->getType() == NodeType::EntityServer) { _lastQueriedTime = 0; + _octreeQuery.incrementConnectionID(); } if (node->getType() == NodeType::AudioMixer) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 19a6dfdac1..c709571204 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -543,7 +543,7 @@ private: ViewFrustum _displayViewFrustum; quint64 _lastQueriedTime; - OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers + OctreeQuery _octreeQuery { true }; // NodeData derived class for querying octee cells from octree servers std::shared_ptr _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session std::shared_ptr _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 9a98393fa1..c2c1d75726 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { return static_cast(EntityVersion::HazeEffect); case PacketType::EntityQuery: - return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); + return static_cast(EntityQueryPacketVersion::ConnectionIdentifier); 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 21b4ae8878..c4c1758ed2 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -209,7 +209,8 @@ enum class EntityScriptCallMethodVersion : PacketVersion { enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, - JSONFilterWithFamilyTree = 19 + JSONFilterWithFamilyTree = 19, + ConnectionIdentifier = 20 }; enum class AssetServerPacketVersion: PacketVersion { diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp index a88f730a50..18766dd7f6 100644 --- a/libraries/octree/src/OctreeQuery.cpp +++ b/libraries/octree/src/OctreeQuery.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include @@ -22,7 +24,7 @@ const float DEFAULT_ASPECT_RATIO = 1.0f; const float DEFAULT_NEAR_CLIP = 0.1f; const float DEFAULT_FAR_CLIP = 3.0f; -OctreeQuery::OctreeQuery() : +OctreeQuery::OctreeQuery(bool randomizeConnectionID) : _cameraFov(DEFAULT_FOV), _cameraAspectRatio(DEFAULT_ASPECT_RATIO), _cameraNearClip(DEFAULT_NEAR_CLIP), @@ -30,10 +32,21 @@ OctreeQuery::OctreeQuery() : _cameraCenterRadius(DEFAULT_FAR_CLIP) { _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS; + + if (randomizeConnectionID) { + // randomize our initial octree query connection ID using random_device + // the connection ID is 16 bits so we take a generated 32 bit value from random device and chop off the top + std::random_device randomDevice; + _connectionID = randomDevice(); + } } int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) { unsigned char* bufferStart = destinationBuffer; + + // pack the connection ID so the server can detect when we start a new connection + memcpy(destinationBuffer, &_connectionID, sizeof(_connectionID)); + destinationBuffer += sizeof(_connectionID); // back a boolean (cut to 1 byte) to designate if this query uses the sent view frustum memcpy(destinationBuffer, &_usesFrustum, sizeof(_usesFrustum)); @@ -98,7 +111,27 @@ int OctreeQuery::parseData(ReceivedMessage& message) { const unsigned char* startPosition = reinterpret_cast(message.getRawMessage()); const unsigned char* sourceBuffer = startPosition; - + + // unpack the connection ID + uint16_t newConnectionID; + memcpy(&newConnectionID, sourceBuffer, sizeof(newConnectionID)); + sourceBuffer += sizeof(newConnectionID); + + if (!_hasReceivedFirstQuery) { + // set our flag to indicate that we've parsed for this query at least once + _hasReceivedFirstQuery = true; + + // set the incoming connection ID as the current + _connectionID = newConnectionID; + } else { + if (newConnectionID != _connectionID) { + // the connection ID has changed - emit our signal so the server + // knows that the client is starting a new session + _connectionID = newConnectionID; + emit incomingConnectionIDChanged(); + } + } + // check if this query uses a view frustum memcpy(&_usesFrustum, sourceBuffer, sizeof(_usesFrustum)); sourceBuffer += sizeof(_usesFrustum); diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h index bbd0350baf..21ce2e7fac 100644 --- a/libraries/octree/src/OctreeQuery.h +++ b/libraries/octree/src/OctreeQuery.h @@ -27,11 +27,11 @@ class OctreeQuery : public NodeData { Q_OBJECT public: - OctreeQuery(); + OctreeQuery(bool randomizeConnectionID = false); virtual ~OctreeQuery() {} int getBroadcastData(unsigned char* destinationBuffer); - virtual int parseData(ReceivedMessage& message) override; + int parseData(ReceivedMessage& message) override; // getters for camera details const glm::vec3& getCameraPosition() const { return _cameraPosition; } @@ -68,6 +68,13 @@ public: bool getUsesFrustum() { return _usesFrustum; } void setUsesFrustum(bool usesFrustum) { _usesFrustum = usesFrustum; } + void incrementConnectionID() { ++_connectionID; } + + bool hasReceivedFirstQuery() const { return _hasReceivedFirstQuery; } + +signals: + void incomingConnectionIDChanged(); + public slots: void setMaxQueryPacketsPerSecond(int maxQueryPPS) { _maxQueryPPS = maxQueryPPS; } void setOctreeSizeScale(float octreeSizeScale) { _octreeElementSizeScale = octreeSizeScale; } @@ -90,9 +97,12 @@ protected: int _boundaryLevelAdjust = 0; /// used for LOD calculations uint8_t _usesFrustum = true; + uint16_t _connectionID; // query connection ID, randomized to start, increments with each new connection to server QJsonObject _jsonParameters; QReadWriteLock _jsonParametersLock; + + bool _hasReceivedFirstQuery { false }; private: // privatize the copy constructor and assignment operator so they cannot be called diff --git a/libraries/octree/src/OctreeQueryNode.cpp b/libraries/octree/src/OctreeQueryNode.cpp index 941bb6b536..f0c9027493 100644 --- a/libraries/octree/src/OctreeQueryNode.cpp +++ b/libraries/octree/src/OctreeQueryNode.cpp @@ -18,13 +18,6 @@ #include #include -int OctreeQueryNode::parseData(ReceivedMessage& message) { - // set our flag to indicate that we've parsed for this query at least once - _hasReceivedFirstQuery = true; - - return OctreeQuery::parseData(message); -} - void OctreeQueryNode::nodeKilled() { _isShuttingDown = true; } diff --git a/libraries/octree/src/OctreeQueryNode.h b/libraries/octree/src/OctreeQueryNode.h index fac118c628..fd89a89949 100644 --- a/libraries/octree/src/OctreeQueryNode.h +++ b/libraries/octree/src/OctreeQueryNode.h @@ -35,8 +35,6 @@ public: void init(); // called after creation to set up some virtual items virtual PacketType getMyPacketType() const = 0; - virtual int parseData(ReceivedMessage& message) override; - void resetOctreePacket(); // resets octree packet to after "V" header void writeToPacket(const unsigned char* buffer, unsigned int bytes); // writes to end of packet @@ -108,8 +106,6 @@ public: bool shouldForceFullScene() const { return _shouldForceFullScene; } void setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; } - bool hasReceivedFirstQuery() const { return _hasReceivedFirstQuery; } - private: OctreeQueryNode(const OctreeQueryNode &); OctreeQueryNode& operator= (const OctreeQueryNode&); @@ -157,8 +153,6 @@ private: QJsonObject _lastCheckJSONParameters; bool _shouldForceFullScene { false }; - - bool _hasReceivedFirstQuery { false }; }; #endif // hifi_OctreeQueryNode_h