Merge pull request #211 from limbonaut/fix-bt-for-each

Fix `BTForEach` crash if elements are removed from the array during iteration
This commit is contained in:
Serhii Snitsaruk 2024-09-14 17:00:10 +02:00 committed by GitHub
commit fd0eb34b4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 23 additions and 4 deletions

View File

@ -43,12 +43,15 @@ void BTForEach::_enter() {
} }
BT::Status BTForEach::_tick(double p_delta) { BT::Status BTForEach::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(get_child_count() == 0, FAILURE, "ForEach decorator has no child."); ERR_FAIL_COND_V_MSG(get_child_count() == 0, FAILURE, "BTForEach: Decorator has no child.");
ERR_FAIL_COND_V_MSG(save_var == StringName(), FAILURE, "ForEach save variable is not set."); ERR_FAIL_COND_V_MSG(save_var == StringName(), FAILURE, "BTForEach: Save variable is not set.");
ERR_FAIL_COND_V_MSG(array_var == StringName(), FAILURE, "ForEach array variable is not set."); ERR_FAIL_COND_V_MSG(array_var == StringName(), FAILURE, "BTForEach: Array variable is not set.");
Array arr = get_blackboard()->get_var(array_var, Variant()); Array arr = get_blackboard()->get_var(array_var, Variant());
if (arr.size() == 0) { if (current_idx >= arr.size()) {
if (current_idx != 0) {
WARN_PRINT("BTForEach: Array size changed during iteration.");
}
return SUCCESS; return SUCCESS;
} }
Variant elem = arr[current_idx]; Variant elem = arr[current_idx];

View File

@ -96,6 +96,22 @@ TEST_CASE("[Modules][LimboAI] BTForEach") {
CHECK_ENTRIES_TICKS_EXITS(task, 3, 6, 3); CHECK_ENTRIES_TICKS_EXITS(task, 3, 6, 3);
CHECK(blackboard->get_var("element", "wetgoop") == "mushroom"); CHECK(blackboard->get_var("element", "wetgoop") == "mushroom");
} }
SUBCASE("Shouldn't crash if elements are removed during iteration") {
CHECK(fe->execute(0.01666) == BTTask::RUNNING);
CHECK(task->get_status() == BTTask::SUCCESS);
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1);
CHECK(blackboard->get_var("element", "wetgoop") == "apple");
arr.clear();
ERR_PRINT_OFF;
CHECK(fe->execute(0.01666) == BTTask::SUCCESS); // Returns SUCCESS and prints a warning without executing child task.
ERR_PRINT_ON;
CHECK(task->get_status() == BTTask::SUCCESS); // Same status.
CHECK_ENTRIES_TICKS_EXITS(task, 1, 1, 1); // Task is not re-executed as there is not enough elements to continue iteration.
CHECK(blackboard->get_var("element", "wetgoop") == "apple"); // Not changed.
}
} }
} //namespace TestForEach } //namespace TestForEach