diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 8b9ce95a1..a80f01444 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -10,6 +10,7 @@ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -934,8 +935,18 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(const std::string& tree_ID, } else // special case: SubTreeNode { - auto new_bb = Blackboard::create(blackboard); const std::string subtree_ID = element->Attribute("ID"); + + // check for recursion in behavior tree + if(prefix.find(subtree_ID) != std::string::npos) + { + auto msg = StrCat("Recursive behavior trees are not supported. A cycle was found " + "in ", + " with prefix: ", prefix); + throw RuntimeError(msg); + } + + auto new_bb = Blackboard::create(blackboard); std::unordered_map subtree_remapping; bool do_autoremap = false; diff --git a/tests/gtest_subtree.cpp b/tests/gtest_subtree.cpp index 6402bafba..26312b10d 100644 --- a/tests/gtest_subtree.cpp +++ b/tests/gtest_subtree.cpp @@ -726,3 +726,63 @@ TEST(SubTree, SubtreeNameNotRegistered) ASSERT_ANY_THROW(auto tree = factory.createTreeFromText(xml_text)); ASSERT_ANY_THROW(factory.registerBehaviorTreeFromText(xml_text)); } + +TEST(SubTree, RecursiveSubtree) +{ + // clang-format off + + static const char* xml_text = R"( + + + + + + + + + )"; + + // clang-format on + BehaviorTreeFactory factory; + std::vector console; + factory.registerNodeType("PrintToConsole", &console); + + ASSERT_ANY_THROW(auto tree = factory.createTreeFromText(xml_text)); +} + +TEST(SubTree, RecursiveCycle) +{ + // clang-format off + + static const char* xml_text = R"( + + + + + + + + + + + + + + + + + + + + + + + )"; + + // clang-format on + BehaviorTreeFactory factory; + std::vector console; + factory.registerNodeType("PrintToConsole", &console); + + ASSERT_ANY_THROW(auto tree = factory.createTreeFromText(xml_text)); +}