-
-
Notifications
You must be signed in to change notification settings - Fork 691
Description
Godot version
4.0
System information
Arch Linux
Issue description
Say I have a GDExtension class Foo:
class Foo : public godot::Node {
GDCLASS(Foo, godot::Node);
protected:
static void _bind_methods();
public:
void _enter_tree() override;
void hello();
};
void Foo::_bind_methods() {
ClassDB::bind_method(D_METHOD("hello"), &Foo::hello);
}
void Foo::_enter_tree() {
UtilityFunctions::print("Foo::_enter_tree");
}
void Foo::hello() {
UtilityFunctions::print("Foo::hello");
}If I instantiate Foo in C++ using new, the resulting object seems to work at first; I can add it to the scene tree, and add a Sprite2D under it which appears normally:
var f : Foo = … # Foo created in C++ with new
add_child(f)
var s = Sprite2D.new()
s.position = Vector2(100,100)
s.texture = …
f.add_child(s)But it misbehaves in surprising ways: Foo::_enter_tree() doesn't get called for add_child(f), and if I try to call f.hello(), the game crashes.
Foo objects instantiated with memnew don't have this problem.
If I'm reading the code correctly, every extension object (Foo in this example) gets assigned a corresponding Godot engine object (Node in this example). Each contains an opaque pointer to the other:
Fooinherits fromWrapped. TheWrappedconstructor setsWrapped::_ownerto point to theNode.Nodeinherits fromObject.ClassDB::set_object_extension_instancesetsObject::_extension_instanceto point to theFoo.
It seems like it shouldn't matter how I instantiate Foo - new Foo, memnew(Foo), and std::make_unique<Foo>() should all call the Wrapped constructor.
Although, the existence of the _extension_instance pointer implies some limitations on how I can use Foo. For example, a std::vector<Foo> presumably wouldn't work, since vector reallocation would
invalidate the _extension_instance of each corresponding Node.
It's not clear to me exactly how I'm allowed to use GDExtension objects, and so I can't tell whether this is a Godot bug or a documentation bug. Maybe new Foo isn't supposed to work in the first place?
Specifically, which of the following is allowed?
- instantiating a
Foowithnew(as above) - freeing a
Foowithdelete(throws exception if theFoowas created withmemnew!) - putting
Fooobjects in a reallocating container likestd::vector<Foo> - handling
Foowith a managed pointer likestd::unique_ptr<Foo> - including
Fooas a field of another GDExtension class, like:
class Bar : public godot::Node {
GDCLASS(Bar, godot::Node);
Foo f;
};- including
Fooas a field of a regular class, like:
class Baz {
Foo f;
};If any of these uses are forbidden, they should be called out in the documentation.
Steps to reproduce
The attached project has the above code, and some other test functions.
- unzip
make-node-test.zip - check out godot-cpp into
make-node-test/extension/godot-cpp/ cd make-node-test/extension/out/cmake ..and build, e.g.CC=gcc CXX=g++ cmake -G Ninja .. && ninjacp libfoo.so ../../project/<Godot executable> --path ../../project/