From 05d896f3de1945fd3713dbf335ba06f0c0442c6f Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 10 Jul 2019 17:45:35 -0700
Subject: [PATCH 1/4] fix SafeLanding failure mode when first sequence number
 not 0

---
 interface/src/Application.cpp                 |  3 +-
 .../src/octree/OctreePacketProcessor.cpp      | 21 ++++--
 interface/src/octree/OctreePacketProcessor.h  |  2 +
 interface/src/octree/SafeLanding.cpp          | 68 +++++++++++--------
 interface/src/octree/SafeLanding.h            | 26 ++++---
 5 files changed, 75 insertions(+), 45 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index d20574bdaa..cd7bb36763 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -6245,6 +6245,7 @@ void Application::tryToEnablePhysics() {
         // We keep physics disabled until we've received a full scene and everything near the avatar in that
         // scene is ready to compute its collision shape.
         if (getMyAvatar()->isReadyForPhysics()) {
+            _octreeProcessor.resetSafeLanding();
             _physicsEnabled = true;
             setIsInterstitialMode(false);
             getMyAvatar()->updateMotionBehaviorFromMenu();
@@ -7316,7 +7317,7 @@ void Application::nodeKilled(SharedNodePointer node) {
     _octreeProcessor.nodeKilled(node);
 
     _entityEditSender.nodeKilled(node);
-     
+
     if (node->getType() == NodeType::AudioMixer) {
         QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled");
     } else if (node->getType() == NodeType::EntityServer) {
diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp
index c6f908e039..a44774540a 100644
--- a/interface/src/octree/OctreePacketProcessor.cpp
+++ b/interface/src/octree/OctreePacketProcessor.cpp
@@ -114,7 +114,11 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
                 if (renderer) {
                     renderer->processDatagram(*message, sendingNode);
                     if (_safeLanding && _safeLanding->isTracking()) {
-                        _safeLanding->addToSequence(renderer->getLastOctreeMessageSequence());
+                        OCTREE_PACKET_SEQUENCE thisSequence = renderer->getLastOctreeMessageSequence();
+                        _safeLanding->addToSequence(thisSequence);
+                        if (_safeLandingSequenceStart == SafeLanding::INVALID_SEQUENCE) {
+                            _safeLandingSequenceStart = thisSequence;
+                        }
                     }
                 }
             }
@@ -122,10 +126,10 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
 
         case PacketType::EntityQueryInitialResultsComplete: {
             // Read sequence #
-            OCTREE_PACKET_SEQUENCE completionNumber;
-            message->readPrimitive(&completionNumber);
-            if (_safeLanding) {
-                _safeLanding->finishSequence(0, completionNumber);
+            if (_safeLanding && _safeLanding->isTracking()) {
+                OCTREE_PACKET_SEQUENCE completionNumber;
+                message->readPrimitive(&completionNumber);
+                _safeLanding->finishSequence(_safeLandingSequenceStart, completionNumber);
             }
         } break;
 
@@ -153,6 +157,13 @@ void OctreePacketProcessor::stopSafeLanding() {
     }
 }
 
+void OctreePacketProcessor::resetSafeLanding() {
+    if (_safeLanding) {
+        _safeLanding->reset();
+    }
+    _safeLandingSequenceStart = SafeLanding::INVALID_SEQUENCE;
+}
+
 bool OctreePacketProcessor::safeLandingIsActive() const {
     return _safeLanding && _safeLanding->isTracking();
 }
diff --git a/interface/src/octree/OctreePacketProcessor.h b/interface/src/octree/OctreePacketProcessor.h
index eacc15f62d..d56b7e0e35 100644
--- a/interface/src/octree/OctreePacketProcessor.h
+++ b/interface/src/octree/OctreePacketProcessor.h
@@ -28,6 +28,7 @@ public:
     void startSafeLanding();
     void updateSafeLanding();
     void stopSafeLanding();
+    void resetSafeLanding();
     bool safeLandingIsActive() const;
     bool safeLandingIsComplete() const;
 
@@ -43,6 +44,7 @@ private slots:
     void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
 
 private:
+    OCTREE_PACKET_SEQUENCE _safeLandingSequenceStart { SafeLanding::INVALID_SEQUENCE };
     std::unique_ptr<SafeLanding> _safeLanding;
 };
 #endif  // hifi_OctreePacketProcessor_h
diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp
index 80ca0e5398..2e2a43859c 100644
--- a/interface/src/octree/SafeLanding.cpp
+++ b/interface/src/octree/SafeLanding.cpp
@@ -17,7 +17,6 @@
 #include "InterfaceLogging.h"
 #include "Application.h"
 
-const int SafeLanding::SEQUENCE_MODULO = std::numeric_limits<OCTREE_PACKET_SEQUENCE>::max() + 1;
 
 CalculateEntityLoadingPriority SafeLanding::entityLoadingOperatorElevateCollidables = [](const EntityItem& entityItem) {
     const int COLLIDABLE_ENTITY_PRIORITY = 10.0f;
@@ -25,8 +24,8 @@ CalculateEntityLoadingPriority SafeLanding::entityLoadingOperatorElevateCollidab
 };
 
 namespace {
-    template<typename T> bool lessThanWraparound(int a, int b) {
-        constexpr int MAX_T_VALUE = std::numeric_limits<T>::max();
+    template<typename T> bool lessThanWraparound(int32_t a, int32_t b) {
+        constexpr int32_t MAX_T_VALUE = std::numeric_limits<T>::max();
         if (b <= a) {
             b += MAX_T_VALUE;
         }
@@ -34,7 +33,7 @@ namespace {
     }
 }
 
-bool SafeLanding::SequenceLessThan::operator()(const int& a, const int& b) const {
+bool SafeLanding::SequenceLessThan::operator()(const OCTREE_PACKET_SEQUENCE& a, const OCTREE_PACKET_SEQUENCE& b) const {
     return lessThanWraparound<OCTREE_PACKET_SEQUENCE>(a, b);
 }
 
@@ -46,8 +45,8 @@ void SafeLanding::startTracking(QSharedPointer<EntityTreeRenderer> entityTreeRen
             _entityTreeRenderer = entityTreeRenderer;
             _trackedEntities.clear();
             _maxTrackedEntityCount = 0;
-            _initialStart = INVALID_SEQUENCE;
-            _initialEnd = INVALID_SEQUENCE;
+            _sequenceStart = SafeLanding::INVALID_SEQUENCE;
+            _sequenceEnd = SafeLanding::INVALID_SEQUENCE;
             _sequenceNumbers.clear();
             _trackingEntities = true;
             _startTime = usecTimestampNow();
@@ -72,7 +71,7 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) {
             if (entity && !entity->isLocalEntity() && entity->getCreated() < _startTime) {
                 _trackedEntities.emplace(entityID, entity);
 
-                int trackedEntityCount = (int)_trackedEntities.size();
+                int32_t trackedEntityCount = (int32_t)_trackedEntities.size();
                 if (trackedEntityCount > _maxTrackedEntityCount) {
                     _maxTrackedEntityCount = trackedEntityCount;
                     _trackedEntityStabilityCount = 0;
@@ -87,15 +86,15 @@ void SafeLanding::deleteTrackedEntity(const EntityItemID& entityID) {
     _trackedEntities.erase(entityID);
 }
 
-void SafeLanding::finishSequence(int first, int last) {
+void SafeLanding::finishSequence(OCTREE_PACKET_SEQUENCE first, OCTREE_PACKET_SEQUENCE last) {
     Locker lock(_lock);
     if (_trackingEntities) {
-        _initialStart = first;
-        _initialEnd = last;
+        _sequenceStart = first;
+        _sequenceEnd = last;
     }
 }
 
-void SafeLanding::addToSequence(int sequenceNumber) {
+void SafeLanding::addToSequence(OCTREE_PACKET_SEQUENCE sequenceNumber) {
     Locker lock(_lock);
     _sequenceNumbers.insert(sequenceNumber);
 }
@@ -135,14 +134,15 @@ void SafeLanding::updateTracking() {
 
     if (_trackedEntities.empty()) {
         // no more tracked entities --> check sequenceNumbers
-        if (_initialStart != INVALID_SEQUENCE) {
+        if (_sequenceStart != SafeLanding::INVALID_SEQUENCE) {
             bool shouldStop = false;
             {
                 Locker lock(_lock);
-                int sequenceSize = _initialStart <= _initialEnd ? _initialEnd - _initialStart:
-                    _initialEnd + SEQUENCE_MODULO - _initialStart;
-                auto startIter = _sequenceNumbers.find(_initialStart);
-                auto endIter = _sequenceNumbers.find(_initialEnd - 1);
+                int32_t sequenceSize = _sequenceStart < _sequenceEnd ?
+                    (int32_t)(_sequenceEnd - _sequenceStart) :
+                    (int32_t)((SafeLanding::MAX_SEQUENCE - _sequenceStart) + _sequenceEnd + 1); // with rollover
+                auto startIter = _sequenceNumbers.find(_sequenceStart);
+                auto endIter = _sequenceNumbers.find(_sequenceEnd - 1);
 
                 bool missingSequenceNumbers = qApp->isMissingSequenceNumbers();
                 shouldStop = (sequenceSize == 0 ||
@@ -171,19 +171,27 @@ void SafeLanding::stopTracking() {
     EntityTreeRenderer::setEntityLoadingPriorityFunction(_prevEntityLoadingPriorityOperator);
 }
 
+void SafeLanding::reset() {
+    _trackingEntities = false;
+    _trackedEntities.clear();
+    _maxTrackedEntityCount = 0;
+    _sequenceStart = SafeLanding::INVALID_SEQUENCE;
+    _sequenceEnd = SafeLanding::INVALID_SEQUENCE;
+}
+
 bool SafeLanding::trackingIsComplete() const {
-    return !_trackingEntities && (_initialStart != INVALID_SEQUENCE);
+    return !_trackingEntities && (_sequenceStart != SafeLanding::INVALID_SEQUENCE);
 }
 
 float SafeLanding::loadingProgressPercentage() {
     Locker lock(_lock);
-    static const int MINIMUM_TRACKED_ENTITY_STABILITY_COUNT = 15;
 
     float entityReadyPercentage = 0.0f;
     if (_maxTrackedEntityCount > 0) {
         entityReadyPercentage = ((_maxTrackedEntityCount - _trackedEntities.size()) / (float)_maxTrackedEntityCount);
     }
 
+    constexpr int32_t MINIMUM_TRACKED_ENTITY_STABILITY_COUNT = 15;
     if (_trackedEntityStabilityCount < MINIMUM_TRACKED_ENTITY_STABILITY_COUNT) {
         entityReadyPercentage *= 0.20f;
     }
@@ -226,20 +234,24 @@ bool SafeLanding::isEntityPhysicsReady(const EntityItemPointer& entity) {
 }
 
 void SafeLanding::debugDumpSequenceIDs() const {
-    int p = -1;
     qCDebug(interfaceapp) << "Sequence set size:" << _sequenceNumbers.size();
-    for (auto s: _sequenceNumbers) {
-        if (p == -1) {
-            p = s;
-            qCDebug(interfaceapp) << "First:" << s;
-        } else {
+
+    auto itr = _sequenceNumbers.begin();
+    OCTREE_PACKET_SEQUENCE p = SafeLanding::INVALID_SEQUENCE;
+    if (itr != _sequenceNumbers.end()) {
+        p = (*itr);
+        qCDebug(interfaceapp) << "First:" << (int32_t)p;
+        ++itr;
+        while (itr != _sequenceNumbers.end()) {
+            OCTREE_PACKET_SEQUENCE s = *itr;
             if (s != p + 1) {
-                qCDebug(interfaceapp) << "Gap from" << p << "to" << s << "(exclusive)";
+                qCDebug(interfaceapp) << "Gap from" << (int32_t)p << "to" << (int32_t)s << "(exclusive)";
                 p = s;
             }
+            ++itr;
+        }
+        if (p != SafeLanding::INVALID_SEQUENCE) {
+            qCDebug(interfaceapp) << "Last:" << p;
         }
     }
-    if (p != -1) {
-        qCDebug(interfaceapp) << "Last:" << p;
-    }
 }
diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h
index 87dac16ba3..00473f6fc8 100644
--- a/interface/src/octree/SafeLanding.h
+++ b/interface/src/octree/SafeLanding.h
@@ -17,6 +17,8 @@
 #include <QtCore/QObject>
 #include <QtCore/QSharedPointer>
 
+#include <set>
+
 #include "EntityItem.h"
 #include "EntityDynamicInterface.h"
 
@@ -27,14 +29,18 @@ class EntityItemID;
 
 class SafeLanding : public QObject {
 public:
+    static constexpr OCTREE_PACKET_SEQUENCE MAX_SEQUENCE = std::numeric_limits<OCTREE_PACKET_SEQUENCE>::max();
+    static constexpr OCTREE_PACKET_SEQUENCE INVALID_SEQUENCE = MAX_SEQUENCE; // not technically invalid, but close enough
+
     void startTracking(QSharedPointer<EntityTreeRenderer> entityTreeRenderer);
     void updateTracking();
     void stopTracking();
+    void reset();
     bool isTracking() const { return _trackingEntities; }
     bool trackingIsComplete() const;
 
-    void finishSequence(int first, int last);  // 'last' exclusive.
-    void addToSequence(int sequenceNumber);
+    void finishSequence(OCTREE_PACKET_SEQUENCE first, OCTREE_PACKET_SEQUENCE last);  // 'last' exclusive.
+    void addToSequence(OCTREE_PACKET_SEQUENCE sequenceNumber);
     float loadingProgressPercentage();
 
 private slots:
@@ -52,24 +58,22 @@ private:
     using EntityMap = std::map<EntityItemID, EntityItemPointer>;
     EntityMap _trackedEntities;
 
-    static constexpr int INVALID_SEQUENCE = -1;
-    int _initialStart { INVALID_SEQUENCE };
-    int _initialEnd { INVALID_SEQUENCE };
-    int _maxTrackedEntityCount { 0 };
-    int _trackedEntityStabilityCount { 0 };
+    OCTREE_PACKET_SEQUENCE _sequenceStart { INVALID_SEQUENCE };
+    OCTREE_PACKET_SEQUENCE _sequenceEnd { INVALID_SEQUENCE };
+    int32_t _maxTrackedEntityCount { 0 };
+    int32_t _trackedEntityStabilityCount { 0 };
 
     quint64 _startTime { 0 };
 
     struct SequenceLessThan {
-        bool operator()(const int& a, const int& b) const;
+        bool operator()(const OCTREE_PACKET_SEQUENCE& a, const OCTREE_PACKET_SEQUENCE& b) const;
     };
 
-    std::set<int, SequenceLessThan> _sequenceNumbers;
+    using SequenceSet = std::set<OCTREE_PACKET_SEQUENCE, SequenceLessThan>;
+    SequenceSet _sequenceNumbers;
 
     static CalculateEntityLoadingPriority entityLoadingOperatorElevateCollidables;
     CalculateEntityLoadingPriority _prevEntityLoadingPriorityOperator { nullptr };
-
-    static const int SEQUENCE_MODULO;
 };
 
 #endif  // hifi_SafeLanding_h

From b0f297e696a9d49280710bd8e1522ff05900333b Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 17 Jul 2019 11:20:38 -0700
Subject: [PATCH 2/4] wait for SafeLanding to start before starting octreeQuery

---
 interface/src/Application.cpp        | 12 +++++++-----
 interface/src/octree/SafeLanding.cpp | 20 +++++++++++---------
 2 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index cd7bb36763..ad38599dcf 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5986,6 +5986,7 @@ void Application::resetPhysicsReadyInformation() {
     _gpuTextureMemSizeStabilityCount = 0;
     _gpuTextureMemSizeAtLastCheck = 0;
     _physicsEnabled = false;
+    _octreeProcessor.stopSafeLanding();
 }
 
 void Application::reloadResourceCaches() {
@@ -6981,13 +6982,16 @@ int Application::sendNackPackets() {
 }
 
 void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
-
     if (!_settingsLoaded) {
         return; // bail early if settings are not loaded
     }
 
     const bool isModifiedQuery = !_physicsEnabled;
     if (isModifiedQuery) {
+        if (!_octreeProcessor.safeLandingIsActive()) {
+            // don't send the octreeQuery until SafeLanding knows it has started
+            return;
+        }
         // Create modified view that is a simple sphere.
         bool interstitialModeEnabled = DependencyManager::get<NodeList>()->getDomainHandler().getInterstitialModeEnabled();
 
@@ -7210,7 +7214,6 @@ void Application::clearDomainOctreeDetails(bool clearAll) {
 
 void Application::domainURLChanged(QUrl domainURL) {
     // disable physics until we have enough information about our new location to not cause craziness.
-    resetPhysicsReadyInformation();
     setIsServerlessMode(domainURL.scheme() != URL_SCHEME_HIFI);
     if (isServerlessMode()) {
         loadServerlessDomain(domainURL);
@@ -7220,7 +7223,6 @@ void Application::domainURLChanged(QUrl domainURL) {
 
 void Application::goToErrorDomainURL(QUrl errorDomainURL) {
     // disable physics until we have enough information about our new location to not cause craziness.
-    resetPhysicsReadyInformation();
     setIsServerlessMode(errorDomainURL.scheme() != URL_SCHEME_HIFI);
     if (isServerlessMode()) {
         loadErrorDomain(errorDomainURL);
@@ -7237,12 +7239,12 @@ void Application::resettingDomain() {
 void Application::nodeAdded(SharedNodePointer node) {
     if (node->getType() == NodeType::EntityServer) {
         if (_failedToConnectToEntityServer && !_entityServerConnectionTimer.isActive()) {
-            _failedToConnectToEntityServer = false;
             _octreeProcessor.stopSafeLanding();
-            _octreeProcessor.startSafeLanding();
+            _failedToConnectToEntityServer = false;
         } else if (_entityServerConnectionTimer.isActive()) {
             _entityServerConnectionTimer.stop();
         }
+        _octreeProcessor.startSafeLanding();
         _entityServerConnectionTimer.setInterval(ENTITY_SERVER_CONNECTION_TIMEOUT);
         _entityServerConnectionTimer.start();
     }
diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp
index 2e2a43859c..6f715ea033 100644
--- a/interface/src/octree/SafeLanding.cpp
+++ b/interface/src/octree/SafeLanding.cpp
@@ -159,16 +159,18 @@ void SafeLanding::updateTracking() {
 
 void SafeLanding::stopTracking() {
     Locker lock(_lock);
-    _trackingEntities = false;
-    if (_entityTreeRenderer) {
-        auto entityTree = _entityTreeRenderer->getTree();
-        disconnect(std::const_pointer_cast<EntityTree>(entityTree).get(),
-            &EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity);
-        disconnect(std::const_pointer_cast<EntityTree>(entityTree).get(),
-            &EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity);
-        _entityTreeRenderer.reset();
+    if (_trackingEntities) {
+        _trackingEntities = false;
+        if (_entityTreeRenderer) {
+            auto entityTree = _entityTreeRenderer->getTree();
+            disconnect(std::const_pointer_cast<EntityTree>(entityTree).get(),
+                &EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity);
+            disconnect(std::const_pointer_cast<EntityTree>(entityTree).get(),
+                &EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity);
+            _entityTreeRenderer.reset();
+        }
+        EntityTreeRenderer::setEntityLoadingPriorityFunction(_prevEntityLoadingPriorityOperator);
     }
-    EntityTreeRenderer::setEntityLoadingPriorityFunction(_prevEntityLoadingPriorityOperator);
 }
 
 void SafeLanding::reset() {

From 63b3d666f950dcdde24bf74fef304a28ea24350f Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 17 Jul 2019 13:57:08 -0700
Subject: [PATCH 3/4] simplify rollover logic for SafeLanding sequence size
 calculation

---
 interface/src/octree/SafeLanding.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp
index 6f715ea033..ed779787c9 100644
--- a/interface/src/octree/SafeLanding.cpp
+++ b/interface/src/octree/SafeLanding.cpp
@@ -138,9 +138,7 @@ void SafeLanding::updateTracking() {
             bool shouldStop = false;
             {
                 Locker lock(_lock);
-                int32_t sequenceSize = _sequenceStart < _sequenceEnd ?
-                    (int32_t)(_sequenceEnd - _sequenceStart) :
-                    (int32_t)((SafeLanding::MAX_SEQUENCE - _sequenceStart) + _sequenceEnd + 1); // with rollover
+                auto sequenceSize = _sequenceEnd - _sequenceStart; // this works even in rollover case
                 auto startIter = _sequenceNumbers.find(_sequenceStart);
                 auto endIter = _sequenceNumbers.find(_sequenceEnd - 1);
 

From 7b7414dead363a6d130e134654cd9ee5c1855012 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Wed, 17 Jul 2019 17:04:12 -0700
Subject: [PATCH 4/4] revert optimization that would skip read from packet

---
 interface/src/octree/OctreePacketProcessor.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp
index a44774540a..bc3c1afdd5 100644
--- a/interface/src/octree/OctreePacketProcessor.cpp
+++ b/interface/src/octree/OctreePacketProcessor.cpp
@@ -126,9 +126,9 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
 
         case PacketType::EntityQueryInitialResultsComplete: {
             // Read sequence #
+            OCTREE_PACKET_SEQUENCE completionNumber;
+            message->readPrimitive(&completionNumber);
             if (_safeLanding && _safeLanding->isTracking()) {
-                OCTREE_PACKET_SEQUENCE completionNumber;
-                message->readPrimitive(&completionNumber);
                 _safeLanding->finishSequence(_safeLandingSequenceStart, completionNumber);
             }
         } break;