From c1e2653526ee334e83cf8eee6b7e132abb893c47 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Sat, 8 Dec 2018 07:42:03 -0800
Subject: [PATCH] check for and break parenting loops in hasAncestorOfType,
 findAncestorOfType, isParentPathComplete

---
 libraries/shared/src/SpatiallyNestable.cpp | 52 +++++++++++++++-------
 libraries/shared/src/SpatiallyNestable.h   |  8 ++--
 2 files changed, 40 insertions(+), 20 deletions(-)

diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp
index 97e20f5627..d704498143 100644
--- a/libraries/shared/src/SpatiallyNestable.cpp
+++ b/libraries/shared/src/SpatiallyNestable.cpp
@@ -748,6 +748,18 @@ const Transform SpatiallyNestable::getTransform() const {
     return result;
 }
 
+void SpatiallyNestable::breakParentingLoop() const {
+    // someone created a loop.  break it...
+    qCDebug(shared) << "Parenting loop detected: " << getID();
+    SpatiallyNestablePointer _this = getThisPointer();
+    _this->setParentID(QUuid());
+    bool setPositionSuccess;
+    AACube aaCube = getQueryAACube(setPositionSuccess);
+    if (setPositionSuccess) {
+        _this->setWorldPosition(aaCube.calcCenter());
+    }
+}
+
 const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success, int depth) const {
     // this returns the world-space transform for this object.  It finds its parent's transform (which may
     // cause this object's parent to query its parent, etc) and multiplies this object's local transform onto it.
@@ -755,15 +767,7 @@ const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success, i
 
     if (depth > MAX_PARENTING_CHAIN_SIZE) {
         success = false;
-        // someone created a loop.  break it...
-        qCDebug(shared) << "Parenting loop detected: " << getID();
-        SpatiallyNestablePointer _this = getThisPointer();
-        _this->setParentID(QUuid());
-        bool setPositionSuccess;
-        AACube aaCube = getQueryAACube(setPositionSuccess);
-        if (setPositionSuccess) {
-            _this->setWorldPosition(aaCube.calcCenter());
-        }
+        breakParentingLoop();
         return jointInWorldFrame;
     }
 
@@ -1208,8 +1212,12 @@ AACube SpatiallyNestable::getQueryAACube() const {
     return result;
 }
 
-bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const {
-    bool success;
+bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType, int depth) const {
+    if (depth > MAX_PARENTING_CHAIN_SIZE) {
+        breakParentingLoop();
+        return false;
+    }
+
     if (nestableType == NestableType::Avatar) {
         QUuid parentID = getParentID();
         if (parentID == AVATAR_SELF_ID) {
@@ -1217,6 +1225,7 @@ bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const {
         }
     }
 
+    bool success;
     SpatiallyNestablePointer parent = getParentPointer(success);
     if (!success || !parent) {
         return false;
@@ -1226,11 +1235,14 @@ bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const {
         return true;
     }
 
-    return parent->hasAncestorOfType(nestableType);
+    return parent->hasAncestorOfType(nestableType, depth + 1);
 }
 
-const QUuid SpatiallyNestable::findAncestorOfType(NestableType nestableType) const {
-    bool success;
+const QUuid SpatiallyNestable::findAncestorOfType(NestableType nestableType, int depth) const {
+    if (depth > MAX_PARENTING_CHAIN_SIZE) {
+        breakParentingLoop();
+        return QUuid();
+    }
 
     if (nestableType == NestableType::Avatar) {
         QUuid parentID = getParentID();
@@ -1239,6 +1251,7 @@ const QUuid SpatiallyNestable::findAncestorOfType(NestableType nestableType) con
         }
     }
 
+    bool success;
     SpatiallyNestablePointer parent = getParentPointer(success);
     if (!success || !parent) {
         return QUuid();
@@ -1248,7 +1261,7 @@ const QUuid SpatiallyNestable::findAncestorOfType(NestableType nestableType) con
         return parent->getID();
     }
 
-    return parent->findAncestorOfType(nestableType);
+    return parent->findAncestorOfType(nestableType, depth + 1);
 }
 
 void SpatiallyNestable::getLocalTransformAndVelocities(
@@ -1336,7 +1349,12 @@ void SpatiallyNestable::dump(const QString& prefix) const {
     }
 }
 
-bool SpatiallyNestable::isParentPathComplete() const {
+bool SpatiallyNestable::isParentPathComplete(int depth) const {
+    if (depth > MAX_PARENTING_CHAIN_SIZE) {
+        breakParentingLoop();
+        return false;
+    }
+
     static const QUuid IDENTITY;
     QUuid parentID = getParentID();
     if (parentID.isNull() || parentID == IDENTITY) {
@@ -1349,5 +1367,5 @@ bool SpatiallyNestable::isParentPathComplete() const {
         return false;
     }
 
-    return parent->isParentPathComplete();
+    return parent->isParentPathComplete(depth + 1);
 }
diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h
index 03ed97afbd..aafcfa10eb 100644
--- a/libraries/shared/src/SpatiallyNestable.h
+++ b/libraries/shared/src/SpatiallyNestable.h
@@ -75,7 +75,7 @@ public:
     static QString nestableTypeToString(NestableType nestableType);
 
 
-    virtual bool isParentPathComplete() const;
+    virtual bool isParentPathComplete(int depth = 0) const;
 
 
     // world frame
@@ -187,8 +187,8 @@ public:
     bool isParentIDValid() const { bool success = false; getParentPointer(success); return success; }
     virtual SpatialParentTree* getParentTree() const { return nullptr; }
 
-    bool hasAncestorOfType(NestableType nestableType) const;
-    const QUuid findAncestorOfType(NestableType nestableType) const;
+    bool hasAncestorOfType(NestableType nestableType, int depth = 0) const;
+    const QUuid findAncestorOfType(NestableType nestableType, int depth = 0) const;
     SpatiallyNestablePointer getParentPointer(bool& success) const;
     static SpatiallyNestablePointer findByID(QUuid id, bool& success);
 
@@ -246,6 +246,8 @@ private:
     mutable bool _parentKnowsMe { false };
     bool _isDead { false };
     bool _queryAACubeIsPuffed { false };
+
+    void breakParentingLoop() const;
 };