Skip to content

Commit

Permalink
Add AnimationTree Advance Expressions
Browse files Browse the repository at this point in the history
* Allows specifying an expression as a condition for state machine transitions.
* For this to work safely (user not call queue_free or something in the expression), a const call mode was added to Object and Variant (and optionally Script).
* This mode ensures only const functions can be called, making it safe to use from the editor.
* Bonus: Fixed an animation import bug in Collada.

This gives much greater flexibility for creating complex state machines. By directly interfacing with the script code, it is possible to create complex animation advance condition for switching between states.

Ensure assigning AnimationTreeStateMachineTransition base expression node in editor is relative to current AnimationTree node.

Fix advance expression edge stripping.

Allow setting an expression base node on the AnimationTree itself.
  • Loading branch information
fire committed Jun 27, 2022
1 parent c41e4b1 commit 2d78b4a
Show file tree
Hide file tree
Showing 29 changed files with 531 additions and 221 deletions.
1 change: 1 addition & 0 deletions core/core_constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXPRESSION);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY);
Expand Down
2 changes: 1 addition & 1 deletion core/extension/gdnative_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ typedef enum {
GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */
GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */
GDNATIVE_CALL_ERROR_INSTANCE_IS_NULL,

GDNATIVE_CALL_ERROR_METHOD_NOT_CONST, /* used for const call */
} GDNativeCallErrorType;

typedef struct {
Expand Down
38 changes: 21 additions & 17 deletions core/math/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1240,7 +1240,7 @@ bool Expression::_compile_expression() {
return false;
}

bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) {
bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, bool p_const_calls_only, String &r_error_str) {
switch (p_node->type) {
case Expression::ENode::TYPE_INPUT: {
const Expression::InputNode *in = static_cast<const Expression::InputNode *>(p_node);
Expand All @@ -1266,15 +1266,15 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
const Expression::OperatorNode *op = static_cast<const Expression::OperatorNode *>(p_node);

Variant a;
bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, r_error_str);
bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, p_const_calls_only, r_error_str);
if (ret) {
return true;
}

Variant b;

if (op->nodes[1]) {
ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str);
ret = _execute(p_inputs, p_instance, op->nodes[1], b, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
Expand All @@ -1292,14 +1292,14 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
const Expression::IndexNode *index = static_cast<const Expression::IndexNode *>(p_node);

Variant base;
bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str);
bool ret = _execute(p_inputs, p_instance, index->base, base, p_const_calls_only, r_error_str);
if (ret) {
return true;
}

Variant idx;

ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str);
ret = _execute(p_inputs, p_instance, index->index, idx, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
Expand All @@ -1316,7 +1316,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
const Expression::NamedIndexNode *index = static_cast<const Expression::NamedIndexNode *>(p_node);

Variant base;
bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str);
bool ret = _execute(p_inputs, p_instance, index->base, base, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
Expand All @@ -1336,7 +1336,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
arr.resize(array->array.size());
for (int i = 0; i < array->array.size(); i++) {
Variant value;
bool ret = _execute(p_inputs, p_instance, array->array[i], value, r_error_str);
bool ret = _execute(p_inputs, p_instance, array->array[i], value, p_const_calls_only, r_error_str);

if (ret) {
return true;
Expand All @@ -1353,14 +1353,14 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
Dictionary d;
for (int i = 0; i < dictionary->dict.size(); i += 2) {
Variant key;
bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, r_error_str);
bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, p_const_calls_only, r_error_str);

if (ret) {
return true;
}

Variant value;
ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str);
ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
Expand All @@ -1380,7 +1380,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:

for (int i = 0; i < constructor->arguments.size(); i++) {
Variant value;
bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, r_error_str);
bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, p_const_calls_only, r_error_str);

if (ret) {
return true;
Expand Down Expand Up @@ -1408,7 +1408,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:

for (int i = 0; i < bifunc->arguments.size(); i++) {
Variant value;
bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, r_error_str);
bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
Expand All @@ -1429,7 +1429,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
const Expression::CallNode *call = static_cast<const Expression::CallNode *>(p_node);

Variant base;
bool ret = _execute(p_inputs, p_instance, call->base, base, r_error_str);
bool ret = _execute(p_inputs, p_instance, call->base, base, p_const_calls_only, r_error_str);

if (ret) {
return true;
Expand All @@ -1442,7 +1442,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:

for (int i = 0; i < call->arguments.size(); i++) {
Variant value;
ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str);
ret = _execute(p_inputs, p_instance, call->arguments[i], value, p_const_calls_only, r_error_str);

if (ret) {
return true;
Expand All @@ -1452,7 +1452,11 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
}

Callable::CallError ce;
base.callp(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
if (p_const_calls_only) {
base.call_const(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
} else {
base.callp(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
}

if (ce.error != Callable::CallError::CALL_OK) {
r_error_str = vformat(RTR("On call to '%s':"), String(call->method));
Expand Down Expand Up @@ -1491,13 +1495,13 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu
return OK;
}

Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) {
Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error, bool p_const_calls_only) {
ERR_FAIL_COND_V_MSG(error_set, Variant(), "There was previously a parse error: " + error_str + ".");

execution_error = false;
Variant output;
String error_txt;
bool err = _execute(p_inputs, p_base, root, output, error_txt);
bool err = _execute(p_inputs, p_base, root, output, p_const_calls_only, error_txt);
if (err) {
execution_error = true;
error_str = error_txt;
Expand All @@ -1517,7 +1521,7 @@ String Expression::get_error_text() const {

void Expression::_bind_methods() {
ClassDB::bind_method(D_METHOD("parse", "expression", "input_names"), &Expression::parse, DEFVAL(Vector<String>()));
ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true));
ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error", "const_calls_only"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed);
ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text);
}
Expand Down
4 changes: 2 additions & 2 deletions core/math/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,14 @@ class Expression : public RefCounted {
Vector<String> input_names;

bool execution_error = false;
bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str);
bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, bool p_const_calls_only, String &r_error_str);

protected:
static void _bind_methods();

public:
Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>());
Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true);
Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true, bool p_const_calls_only = false);
bool has_execute_failed() const;
String get_error_text() const;

Expand Down
49 changes: 49 additions & 0 deletions core/object/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
return ret;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
}
Expand All @@ -688,6 +689,54 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
return ret;
}

Variant Object::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK;

if (p_method == CoreStringNames::get_singleton()->_free) {
// Free is not const, so fail.
r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
return Variant();
}

Variant ret;
OBJ_DEBUG_LOCK

if (script_instance) {
ret = script_instance->call_const(p_method, p_args, p_argcount, r_error);
//force jumptable
switch (r_error.error) {
case Callable::CallError::CALL_OK:
return ret;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
break;
case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
break;
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
return ret;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
}
}
}

//extension does not need this, because all methods are registered in MethodBind

MethodBind *method = ClassDB::get_method(get_class_name(), p_method);

if (method) {
if (!method->is_const()) {
r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
return ret;
}
ret = method->call(this, p_args, p_argcount, r_error);
} else {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
}

return ret;
}

void Object::notification(int p_notification, bool p_reversed) {
_notificationv(p_notification, p_reversed);

Expand Down
Loading

0 comments on commit 2d78b4a

Please sign in to comment.