Skip to content

4.11.0: Finally node, _onlyIf/_onStart preconditions, XML validation and many fixes

Latest

Choose a tag to compare

@facontidavide facontidavide released this 08 Nov 10:01
· 4 commits to master since this release

Changelog: 4.11.0 (since 4.10.0)

77 merged PRs: two new XML features for writing trees (the Finally node and the _onlyIf / _onStart preconditions), XML schema validation, input port subscriptions, and a large batch of robustness fixes, many of them found by fuzzing.

ABI: 4.11.0 is binary compatible with 4.10.0. We checked the class layouts against 4.10.0, and restored the timer nodes' layout that an earlier fix had changed (#1283). A few fixes change behavior, though; see Behavior changes below.

Highlights

  • New Finally control node: guaranteed cleanup. It ticks its first child ("main") and then its second child ("cleanup"), like try/finally. Cleanup runs whether main succeeds, fails, throws, or is halted. The node returns main's status, or FAILURE if cleanup fails. By default, a halt gives cleanup a single tick. Set halt_timeout_msec to let an asynchronous cleanup finish. This blocks the halting parent for up to that long, so keep it short. Issue #1228, PR #1229.

    <Finally halt_timeout_msec="500">
      <MoveArm/>
      <EnableCollisions/>
    </Finally>
  • New preconditions _onlyIf and _onStart. _onlyIf="<script>" skips the node unless the condition is true (the readable opposite of _skipIf). _onStart="<script>" runs a script right before the node's first tick, which is handy to initialize or reset blackboard entries. Issues #820, #895, PR #1261.

    <MoveBase _onlyIf="battery > 20" _onStart="attempts := 0" goal="{target}"/>
  • XML validation with XSD and Schematron. writeTreeXSD(factory, generic) can emit a lax schema that accepts unknown custom nodes. The new writeTreeSchematron() checks what XSD can't: every custom node used in a tree has a TreeNodesModel entry, every <SubTree ID> resolves, and main_tree_to_execute exists. Ready-made schemas for the built-in nodes are in tools/generated/, and tools/validate_xml.py validates a file against both. Thanks @vivoeiva. PR #1146.

  • Input port update subscriptions. TreeNode::subscribeToInputPort("port", callback) calls back when the blackboard entry behind an input port is written, so a node can react before its next tick instead of polling. It follows SubTree remapping, and it is notified by set(), setOutput(), scripts, JSON import and snapshot restore. PR #1247.

  • BTCPP_version in generated XML. WriteTreeToXML() and writeTreeNodesModelXML() write the library version in <root>, so tools such as Groot2 know which built-in nodes and attributes the file targets. PR #1264.

  • Groot2Publisher bind address. The new constructor Groot2Publisher(tree, port, bind_address) lets you listen on "127.0.0.1" only. The Groot2 protocol has no authentication, and a reachable port allows inserting hooks that replace node results. The existing constructor keeps binding to all interfaces. PR #1251.

  • Use the system nlohmann_json. With -DUSE_VENDORED_JSON=OFF, the library builds and exports against find_package(nlohmann_json), so it no longer clashes with another copy of the header in your project. The default stays ON. Issue #959, PR #1235.

  • Smaller API additions:

    • custom "__type" names in the JsonExporter lambda converters (#1255);
    • Blackboard::getKeyNames(), which returns copies; getKeys() is deprecated because its string views could dangle (#1203);
    • ports sorted in the TreeNodesModel XML, for stable diffs (#1260).

Robustness

Many of these were found by fuzzing and code review.

  • haltTree() from another thread now stops tickWhileRunning(). Before, the loop restarted the tree that had just been halted. Issue #686, PR #1201.
  • Timer nodes (Sleep, Delay, Timeout):
    • use-after-free on destruction (#1180);
    • stale timer handlers acting on a restarted node (#1267, #1283);
    • Timeout halting its child concurrently with the timer thread (#1241);
    • a TimerQueue shutdown race (#1200).
  • Stack overflows → errors:
    • deeply nested or very long script expressions (#1174, #1259);
    • nested JSON (#1274);
    • wildcard matching (#1270);
    • cyclic <include> (#1250).
  • Data races and use-after-free:
    • getAnyLocked entries (#1202);
    • SetBlackboard (#1236);
    • SqliteLogger (#1237);
    • script assignment (#1265);
    • ImportBlackboardFromJSON (#1284).
  • Numeric conversions: overflow and undefined behavior in safe casts, float narrowing, uint16 parsing and JSON integers (#1238, #1239, #1240, #1242, #1249), plus locale-independent number parsing (#1218).
  • Halting:
    • a child whose halt() throws is reset, so ~Tree() no longer terminates the process (#1273);
    • decorators keep the result of an asynchronous child (#1206).

Behavior changes

  • _onlyIf, _onStart and Finally are now reserved names. A node registered as Finally gets a duplicate-registration error.
  • SubTree literals are stored as strings again, as before 4.9.1. In script arithmetic, a numeric string is converted when mixed with a number. Two literal operands stay strings, so "1" + "2" is "12" (issue #1246, PR #1254).
  • With _autoremap="true", SubTree model defaults are now applied when the parent has no such entry at tree creation (#1272).
  • Writes through a SubTree remapping follow the same type rules as local writes (#1232).
  • Now rejected at load time instead of being accepted:
    • explicit-notation IDs that break the model-name rules (#1231);
    • a ReactiveSequence with several Async children when one of them is an AsyncFallback (#1183);
    • script expressions with more than ~255 chained operators (#1259).
  • Now throw instead of silently wrapping or truncating:
    • fractional, NaN or out-of-range JSON integers (#1240);
    • doubles out of float range (#1249);
    • trees with more than 65535 nodes (#1244).
  • A BehaviorTree ID that replaces one from another file now prints a warning (#1133).

The full list is in CHANGELOG.rst.

Contributors

Thanks to Arthur Chan, Aysha Afrah Ziya, Azer Ben Ali, David Vadovszki, Golitsin Vyacheslav, Igor Stadnyk, Jonas Otto, KImanalieva, Lim Jooyoung, Nikola Kachmaryk, Varun Kumar, Victor Voronin, ktyang and simobenziane.